declare-lab/tango2
92
1# coding=utf-82# Copyright 2023 The HuggingFace Inc. team.3# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.4#5# Licensed under the Apache License, Version 2.0 (the "License");6# you may not use this file except in compliance with the License.7# You may obtain a copy of the License at8#9# http://www.apache.org/licenses/LICENSE-2.010#11# Unless required by applicable law or agreed to in writing, software12# distributed under the License is distributed on an "AS IS" BASIS,13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.14# See the License for the specific language governing permissions and15# limitations under the License.16""" ConfigMixin base class and utilities."""17import dataclasses18import functools19import importlib20import inspect21import json22import os23import re24from collections import OrderedDict25from pathlib import PosixPath26from typing import Any, Dict, Tuple, Union27 28import numpy as np29from huggingface_hub import hf_hub_download30from huggingface_hub.utils import EntryNotFoundError, RepositoryNotFoundError, RevisionNotFoundError31from requests import HTTPError32 33from . import __version__34from .utils import (35 DIFFUSERS_CACHE,36 HUGGINGFACE_CO_RESOLVE_ENDPOINT,37 DummyObject,38 deprecate,39 extract_commit_hash,40 http_user_agent,41 logging,42)43 44 45logger = logging.get_logger(__name__)46 47_re_configuration_file = re.compile(r"config\.(.*)\.json")48 49 50class FrozenDict(OrderedDict):51 def __init__(self, *args, **kwargs):52 super().__init__(*args, **kwargs)53 54 for key, value in self.items():55 setattr(self, key, value)56 57 self.__frozen = True58 59 def __delitem__(self, *args, **kwargs):60 raise Exception(f"You cannot use ``__delitem__`` on a {self.__class__.__name__} instance.")61 62 def setdefault(self, *args, **kwargs):63 raise Exception(f"You cannot use ``setdefault`` on a {self.__class__.__name__} instance.")64 65 def pop(self, *args, **kwargs):66 raise Exception(f"You cannot use ``pop`` on a {self.__class__.__name__} instance.")67 68 def update(self, *args, **kwargs):69 raise Exception(f"You cannot use ``update`` on a {self.__class__.__name__} instance.")70 71 def __setattr__(self, name, value):72 if hasattr(self, "__frozen") and self.__frozen:73 raise Exception(f"You cannot use ``__setattr__`` on a {self.__class__.__name__} instance.")74 super().__setattr__(name, value)75 76 def __setitem__(self, name, value):77 if hasattr(self, "__frozen") and self.__frozen:78 raise Exception(f"You cannot use ``__setattr__`` on a {self.__class__.__name__} instance.")79 super().__setitem__(name, value)80 81 82class ConfigMixin:83 r"""84 Base class for all configuration classes. Stores all configuration parameters under `self.config` Also handles all85 methods for loading/downloading/saving classes inheriting from [`ConfigMixin`] with86 - [`~ConfigMixin.from_config`]87 - [`~ConfigMixin.save_config`]88 89 Class attributes:90 - **config_name** (`str`) -- A filename under which the config should stored when calling91 [`~ConfigMixin.save_config`] (should be overridden by parent class).92 - **ignore_for_config** (`List[str]`) -- A list of attributes that should not be saved in the config (should be93 overridden by subclass).94 - **has_compatibles** (`bool`) -- Whether the class has compatible classes (should be overridden by subclass).95 - **_deprecated_kwargs** (`List[str]`) -- Keyword arguments that are deprecated. Note that the init function96 should only have a `kwargs` argument if at least one argument is deprecated (should be overridden by97 subclass).98 """99 config_name = None100 ignore_for_config = []101 has_compatibles = False102 103 _deprecated_kwargs = []104 105 def register_to_config(self, **kwargs):106 if self.config_name is None:107 raise NotImplementedError(f"Make sure that {self.__class__} has defined a class name `config_name`")108 # Special case for `kwargs` used in deprecation warning added to schedulers109 # TODO: remove this when we remove the deprecation warning, and the `kwargs` argument,110 # or solve in a more general way.111 kwargs.pop("kwargs", None)112 for key, value in kwargs.items():113 try:114 setattr(self, key, value)115 except AttributeError as err:116 logger.error(f"Can't set {key} with value {value} for {self}")117 raise err118 119 if not hasattr(self, "_internal_dict"):120 internal_dict = kwargs121 else:122 previous_dict = dict(self._internal_dict)123 internal_dict = {**self._internal_dict, **kwargs}124 logger.debug(f"Updating config from {previous_dict} to {internal_dict}")125 126 self._internal_dict = FrozenDict(internal_dict)127 128 def save_config(self, save_directory: Union[str, os.PathLike], push_to_hub: bool = False, **kwargs):129 """130 Save a configuration object to the directory `save_directory`, so that it can be re-loaded using the131 [`~ConfigMixin.from_config`] class method.132 133 Args:134 save_directory (`str` or `os.PathLike`):135 Directory where the configuration JSON file will be saved (will be created if it does not exist).136 """137 if os.path.isfile(save_directory):138 raise AssertionError(f"Provided path ({save_directory}) should be a directory, not a file")139 140 os.makedirs(save_directory, exist_ok=True)141 142 # If we save using the predefined names, we can load using `from_config`143 output_config_file = os.path.join(save_directory, self.config_name)144 145 self.to_json_file(output_config_file)146 logger.info(f"Configuration saved in {output_config_file}")147 148 @classmethod149 def from_config(cls, config: Union[FrozenDict, Dict[str, Any]] = None, return_unused_kwargs=False, **kwargs):150 r"""151 Instantiate a Python class from a config dictionary152 153 Parameters:154 config (`Dict[str, Any]`):155 A config dictionary from which the Python class will be instantiated. Make sure to only load156 configuration files of compatible classes.157 return_unused_kwargs (`bool`, *optional*, defaults to `False`):158 Whether kwargs that are not consumed by the Python class should be returned or not.159 160 kwargs (remaining dictionary of keyword arguments, *optional*):161 Can be used to update the configuration object (after it being loaded) and initiate the Python class.162 `**kwargs` will be directly passed to the underlying scheduler/model's `__init__` method and eventually163 overwrite same named arguments of `config`.164 165 Examples:166 167 ```python168 >>> from diffusers import DDPMScheduler, DDIMScheduler, PNDMScheduler169 170 >>> # Download scheduler from huggingface.co and cache.171 >>> scheduler = DDPMScheduler.from_pretrained("google/ddpm-cifar10-32")172 173 >>> # Instantiate DDIM scheduler class with same config as DDPM174 >>> scheduler = DDIMScheduler.from_config(scheduler.config)175 176 >>> # Instantiate PNDM scheduler class with same config as DDPM177 >>> scheduler = PNDMScheduler.from_config(scheduler.config)178 ```179 """180 # <===== TO BE REMOVED WITH DEPRECATION181 # TODO(Patrick) - make sure to remove the following lines when config=="model_path" is deprecated182 if "pretrained_model_name_or_path" in kwargs:183 config = kwargs.pop("pretrained_model_name_or_path")184 185 if config is None:186 raise ValueError("Please make sure to provide a config as the first positional argument.")187 # ======>188 189 if not isinstance(config, dict):190 deprecation_message = "It is deprecated to pass a pretrained model name or path to `from_config`."191 if "Scheduler" in cls.__name__:192 deprecation_message += (193 f"If you were trying to load a scheduler, please use {cls}.from_pretrained(...) instead."194 " Otherwise, please make sure to pass a configuration dictionary instead. This functionality will"195 " be removed in v1.0.0."196 )197 elif "Model" in cls.__name__:198 deprecation_message += (199 f"If you were trying to load a model, please use {cls}.load_config(...) followed by"200 f" {cls}.from_config(...) instead. Otherwise, please make sure to pass a configuration dictionary"201 " instead. This functionality will be removed in v1.0.0."202 )203 deprecate("config-passed-as-path", "1.0.0", deprecation_message, standard_warn=False)204 config, kwargs = cls.load_config(pretrained_model_name_or_path=config, return_unused_kwargs=True, **kwargs)205 206 init_dict, unused_kwargs, hidden_dict = cls.extract_init_dict(config, **kwargs)207 208 # Allow dtype to be specified on initialization209 if "dtype" in unused_kwargs:210 init_dict["dtype"] = unused_kwargs.pop("dtype")211 212 # add possible deprecated kwargs213 for deprecated_kwarg in cls._deprecated_kwargs:214 if deprecated_kwarg in unused_kwargs:215 init_dict[deprecated_kwarg] = unused_kwargs.pop(deprecated_kwarg)216 217 # Return model and optionally state and/or unused_kwargs218 model = cls(**init_dict)219 220 # make sure to also save config parameters that might be used for compatible classes221 model.register_to_config(**hidden_dict)222 223 # add hidden kwargs of compatible classes to unused_kwargs224 unused_kwargs = {**unused_kwargs, **hidden_dict}225 226 if return_unused_kwargs:227 return (model, unused_kwargs)228 else:229 return model230 231 @classmethod232 def get_config_dict(cls, *args, **kwargs):233 deprecation_message = (234 f" The function get_config_dict is deprecated. Please use {cls}.load_config instead. This function will be"235 " removed in version v1.0.0"236 )237 deprecate("get_config_dict", "1.0.0", deprecation_message, standard_warn=False)238 return cls.load_config(*args, **kwargs)239 240 @classmethod241 def load_config(242 cls,243 pretrained_model_name_or_path: Union[str, os.PathLike],244 return_unused_kwargs=False,245 return_commit_hash=False,246 **kwargs,247 ) -> Tuple[Dict[str, Any], Dict[str, Any]]:248 r"""249 Instantiate a Python class from a config dictionary250 251 Parameters:252 pretrained_model_name_or_path (`str` or `os.PathLike`, *optional*):253 Can be either:254 255 - A string, the *model id* of a model repo on huggingface.co. Valid model ids should have an256 organization name, like `google/ddpm-celebahq-256`.257 - A path to a *directory* containing model weights saved using [`~ConfigMixin.save_config`], e.g.,258 `./my_model_directory/`.259 260 cache_dir (`Union[str, os.PathLike]`, *optional*):261 Path to a directory in which a downloaded pretrained model configuration should be cached if the262 standard cache should not be used.263 force_download (`bool`, *optional*, defaults to `False`):264 Whether or not to force the (re-)download of the model weights and configuration files, overriding the265 cached versions if they exist.266 resume_download (`bool`, *optional*, defaults to `False`):267 Whether or not to delete incompletely received files. Will attempt to resume the download if such a268 file exists.269 proxies (`Dict[str, str]`, *optional*):270 A dictionary of proxy servers to use by protocol or endpoint, e.g., `{'http': 'foo.bar:3128',271 'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request.272 output_loading_info(`bool`, *optional*, defaults to `False`):273 Whether or not to also return a dictionary containing missing keys, unexpected keys and error messages.274 local_files_only(`bool`, *optional*, defaults to `False`):275 Whether or not to only look at local files (i.e., do not try to download the model).276 use_auth_token (`str` or *bool*, *optional*):277 The token to use as HTTP bearer authorization for remote files. If `True`, will use the token generated278 when running `transformers-cli login` (stored in `~/.huggingface`).279 revision (`str`, *optional*, defaults to `"main"`):280 The specific model version to use. It can be a branch name, a tag name, or a commit id, since we use a281 git-based system for storing models and other artifacts on huggingface.co, so `revision` can be any282 identifier allowed by git.283 subfolder (`str`, *optional*, defaults to `""`):284 In case the relevant files are located inside a subfolder of the model repo (either remote in285 huggingface.co or downloaded locally), you can specify the folder name here.286 return_unused_kwargs (`bool`, *optional*, defaults to `False):287 Whether unused keyword arguments of the config shall be returned.288 return_commit_hash (`bool`, *optional*, defaults to `False):289 Whether the commit_hash of the loaded configuration shall be returned.290 291 <Tip>292 293 It is required to be logged in (`huggingface-cli login`) when you want to use private or [gated294 models](https://huggingface.co/docs/hub/models-gated#gated-models).295 296 </Tip>297 298 <Tip>299 300 Activate the special ["offline-mode"](https://huggingface.co/transformers/installation.html#offline-mode) to301 use this method in a firewalled environment.302 303 </Tip>304 """305 cache_dir = kwargs.pop("cache_dir", DIFFUSERS_CACHE)306 force_download = kwargs.pop("force_download", False)307 resume_download = kwargs.pop("resume_download", False)308 proxies = kwargs.pop("proxies", None)309 use_auth_token = kwargs.pop("use_auth_token", None)310 local_files_only = kwargs.pop("local_files_only", False)311 revision = kwargs.pop("revision", None)312 _ = kwargs.pop("mirror", None)313 subfolder = kwargs.pop("subfolder", None)314 user_agent = kwargs.pop("user_agent", {})315 316 user_agent = {**user_agent, "file_type": "config"}317 user_agent = http_user_agent(user_agent)318 319 pretrained_model_name_or_path = str(pretrained_model_name_or_path)320 321 if cls.config_name is None:322 raise ValueError(323 "`self.config_name` is not defined. Note that one should not load a config from "324 "`ConfigMixin`. Please make sure to define `config_name` in a class inheriting from `ConfigMixin`"325 )326 327 if os.path.isfile(pretrained_model_name_or_path):328 config_file = pretrained_model_name_or_path329 elif os.path.isdir(pretrained_model_name_or_path):330 if os.path.isfile(os.path.join(pretrained_model_name_or_path, cls.config_name)):331 # Load from a PyTorch checkpoint332 config_file = os.path.join(pretrained_model_name_or_path, cls.config_name)333 elif subfolder is not None and os.path.isfile(334 os.path.join(pretrained_model_name_or_path, subfolder, cls.config_name)335 ):336 config_file = os.path.join(pretrained_model_name_or_path, subfolder, cls.config_name)337 else:338 raise EnvironmentError(339 f"Error no file named {cls.config_name} found in directory {pretrained_model_name_or_path}."340 )341 else:342 try:343 # Load from URL or cache if already cached344 config_file = hf_hub_download(345 pretrained_model_name_or_path,346 filename=cls.config_name,347 cache_dir=cache_dir,348 force_download=force_download,349 proxies=proxies,350 resume_download=resume_download,351 local_files_only=local_files_only,352 use_auth_token=use_auth_token,353 user_agent=user_agent,354 subfolder=subfolder,355 revision=revision,356 )357 except RepositoryNotFoundError:358 raise EnvironmentError(359 f"{pretrained_model_name_or_path} is not a local folder and is not a valid model identifier"360 " listed on 'https://huggingface.co/models'\nIf this is a private repository, make sure to pass a"361 " token having permission to this repo with `use_auth_token` or log in with `huggingface-cli"362 " login`."363 )364 except RevisionNotFoundError:365 raise EnvironmentError(366 f"{revision} is not a valid git identifier (branch name, tag name or commit id) that exists for"367 " this model name. Check the model page at"368 f" 'https://huggingface.co/{pretrained_model_name_or_path}' for available revisions."369 )370 except EntryNotFoundError:371 raise EnvironmentError(372 f"{pretrained_model_name_or_path} does not appear to have a file named {cls.config_name}."373 )374 except HTTPError as err:375 raise EnvironmentError(376 "There was a specific connection error when trying to load"377 f" {pretrained_model_name_or_path}:\n{err}"378 )379 except ValueError:380 raise EnvironmentError(381 f"We couldn't connect to '{HUGGINGFACE_CO_RESOLVE_ENDPOINT}' to load this model, couldn't find it"382 f" in the cached files and it looks like {pretrained_model_name_or_path} is not the path to a"383 f" directory containing a {cls.config_name} file.\nCheckout your internet connection or see how to"384 " run the library in offline mode at"385 " 'https://huggingface.co/docs/diffusers/installation#offline-mode'."386 )387 except EnvironmentError:388 raise EnvironmentError(389 f"Can't load config for '{pretrained_model_name_or_path}'. If you were trying to load it from "390 "'https://huggingface.co/models', make sure you don't have a local directory with the same name. "391 f"Otherwise, make sure '{pretrained_model_name_or_path}' is the correct path to a directory "392 f"containing a {cls.config_name} file"393 )394 395 try:396 # Load config dict397 config_dict = cls._dict_from_json_file(config_file)398 399 commit_hash = extract_commit_hash(config_file)400 except (json.JSONDecodeError, UnicodeDecodeError):401 raise EnvironmentError(f"It looks like the config file at '{config_file}' is not a valid JSON file.")402 403 if not (return_unused_kwargs or return_commit_hash):404 return config_dict405 406 outputs = (config_dict,)407 408 if return_unused_kwargs:409 outputs += (kwargs,)410 411 if return_commit_hash:412 outputs += (commit_hash,)413 414 return outputs415 416 @staticmethod417 def _get_init_keys(cls):418 return set(dict(inspect.signature(cls.__init__).parameters).keys())419 420 @classmethod421 def extract_init_dict(cls, config_dict, **kwargs):422 # 0. Copy origin config dict423 original_dict = dict(config_dict.items())424 425 # 1. Retrieve expected config attributes from __init__ signature426 expected_keys = cls._get_init_keys(cls)427 expected_keys.remove("self")428 # remove general kwargs if present in dict429 if "kwargs" in expected_keys:430 expected_keys.remove("kwargs")431 # remove flax internal keys432 if hasattr(cls, "_flax_internal_args"):433 for arg in cls._flax_internal_args:434 expected_keys.remove(arg)435 436 # 2. Remove attributes that cannot be expected from expected config attributes437 # remove keys to be ignored438 if len(cls.ignore_for_config) > 0:439 expected_keys = expected_keys - set(cls.ignore_for_config)440 441 # load diffusers library to import compatible and original scheduler442 diffusers_library = importlib.import_module(__name__.split(".")[0])443 444 if cls.has_compatibles:445 compatible_classes = [c for c in cls._get_compatibles() if not isinstance(c, DummyObject)]446 else:447 compatible_classes = []448 449 expected_keys_comp_cls = set()450 for c in compatible_classes:451 expected_keys_c = cls._get_init_keys(c)452 expected_keys_comp_cls = expected_keys_comp_cls.union(expected_keys_c)453 expected_keys_comp_cls = expected_keys_comp_cls - cls._get_init_keys(cls)454 config_dict = {k: v for k, v in config_dict.items() if k not in expected_keys_comp_cls}455 456 # remove attributes from orig class that cannot be expected457 orig_cls_name = config_dict.pop("_class_name", cls.__name__)458 if orig_cls_name != cls.__name__ and hasattr(diffusers_library, orig_cls_name):459 orig_cls = getattr(diffusers_library, orig_cls_name)460 unexpected_keys_from_orig = cls._get_init_keys(orig_cls) - expected_keys461 config_dict = {k: v for k, v in config_dict.items() if k not in unexpected_keys_from_orig}462 463 # remove private attributes464 config_dict = {k: v for k, v in config_dict.items() if not k.startswith("_")}465 466 # 3. Create keyword arguments that will be passed to __init__ from expected keyword arguments467 init_dict = {}468 for key in expected_keys:469 # if config param is passed to kwarg and is present in config dict470 # it should overwrite existing config dict key471 if key in kwargs and key in config_dict:472 config_dict[key] = kwargs.pop(key)473 474 if key in kwargs:475 # overwrite key476 init_dict[key] = kwargs.pop(key)477 elif key in config_dict:478 # use value from config dict479 init_dict[key] = config_dict.pop(key)480 481 # 4. Give nice warning if unexpected values have been passed482 if len(config_dict) > 0:483 logger.warning(484 f"The config attributes {config_dict} were passed to {cls.__name__}, "485 "but are not expected and will be ignored. Please verify your "486 f"{cls.config_name} configuration file."487 )488 489 # 5. Give nice info if config attributes are initiliazed to default because they have not been passed490 passed_keys = set(init_dict.keys())491 if len(expected_keys - passed_keys) > 0:492 logger.info(493 f"{expected_keys - passed_keys} was not found in config. Values will be initialized to default values."494 )495 496 # 6. Define unused keyword arguments497 unused_kwargs = {**config_dict, **kwargs}498 499 # 7. Define "hidden" config parameters that were saved for compatible classes500 hidden_config_dict = {k: v for k, v in original_dict.items() if k not in init_dict}501 502 return init_dict, unused_kwargs, hidden_config_dict503 504 @classmethod505 def _dict_from_json_file(cls, json_file: Union[str, os.PathLike]):506 with open(json_file, "r", encoding="utf-8") as reader:507 text = reader.read()508 return json.loads(text)509 510 def __repr__(self):511 return f"{self.__class__.__name__} {self.to_json_string()}"512 513 @property514 def config(self) -> Dict[str, Any]:515 """516 Returns the config of the class as a frozen dictionary517 518 Returns:519 `Dict[str, Any]`: Config of the class.520 """521 return self._internal_dict522 523 def to_json_string(self) -> str:524 """525 Serializes this instance to a JSON string.526 527 Returns:528 `str`: String containing all the attributes that make up this configuration instance in JSON format.529 """530 config_dict = self._internal_dict if hasattr(self, "_internal_dict") else {}531 config_dict["_class_name"] = self.__class__.__name__532 config_dict["_diffusers_version"] = __version__533 534 def to_json_saveable(value):535 if isinstance(value, np.ndarray):536 value = value.tolist()537 elif isinstance(value, PosixPath):538 value = str(value)539 return value540 541 config_dict = {k: to_json_saveable(v) for k, v in config_dict.items()}542 return json.dumps(config_dict, indent=2, sort_keys=True) + "\n"543 544 def to_json_file(self, json_file_path: Union[str, os.PathLike]):545 """546 Save this instance to a JSON file.547 548 Args:549 json_file_path (`str` or `os.PathLike`):550 Path to the JSON file in which this configuration instance's parameters will be saved.551 """552 with open(json_file_path, "w", encoding="utf-8") as writer:553 writer.write(self.to_json_string())554 555 556def register_to_config(init):557 r"""558 Decorator to apply on the init of classes inheriting from [`ConfigMixin`] so that all the arguments are559 automatically sent to `self.register_for_config`. To ignore a specific argument accepted by the init but that560 shouldn't be registered in the config, use the `ignore_for_config` class variable561 562 Warning: Once decorated, all private arguments (beginning with an underscore) are trashed and not sent to the init!563 """564 565 @functools.wraps(init)566 def inner_init(self, *args, **kwargs):567 # Ignore private kwargs in the init.568 init_kwargs = {k: v for k, v in kwargs.items() if not k.startswith("_")}569 config_init_kwargs = {k: v for k, v in kwargs.items() if k.startswith("_")}570 if not isinstance(self, ConfigMixin):571 raise RuntimeError(572 f"`@register_for_config` was applied to {self.__class__.__name__} init method, but this class does "573 "not inherit from `ConfigMixin`."574 )575 576 ignore = getattr(self, "ignore_for_config", [])577 # Get positional arguments aligned with kwargs578 new_kwargs = {}579 signature = inspect.signature(init)580 parameters = {581 name: p.default for i, (name, p) in enumerate(signature.parameters.items()) if i > 0 and name not in ignore582 }583 for arg, name in zip(args, parameters.keys()):584 new_kwargs[name] = arg585 586 # Then add all kwargs587 new_kwargs.update(588 {589 k: init_kwargs.get(k, default)590 for k, default in parameters.items()591 if k not in ignore and k not in new_kwargs592 }593 )594 new_kwargs = {**config_init_kwargs, **new_kwargs}595 getattr(self, "register_to_config")(**new_kwargs)596 init(self, *args, **init_kwargs)597 598 return inner_init599 600 601def flax_register_to_config(cls):602 original_init = cls.__init__603 604 @functools.wraps(original_init)605 def init(self, *args, **kwargs):606 if not isinstance(self, ConfigMixin):607 raise RuntimeError(608 f"`@register_for_config` was applied to {self.__class__.__name__} init method, but this class does "609 "not inherit from `ConfigMixin`."610 )611 612 # Ignore private kwargs in the init. Retrieve all passed attributes613 init_kwargs = dict(kwargs.items())614 615 # Retrieve default values616 fields = dataclasses.fields(self)617 default_kwargs = {}618 for field in fields:619 # ignore flax specific attributes620 if field.name in self._flax_internal_args:621 continue622 if type(field.default) == dataclasses._MISSING_TYPE:623 default_kwargs[field.name] = None624 else:625 default_kwargs[field.name] = getattr(self, field.name)626 627 # Make sure init_kwargs override default kwargs628 new_kwargs = {**default_kwargs, **init_kwargs}629 # dtype should be part of `init_kwargs`, but not `new_kwargs`630 if "dtype" in new_kwargs:631 new_kwargs.pop("dtype")632 633 # Get positional arguments aligned with kwargs634 for i, arg in enumerate(args):635 name = fields[i].name636 new_kwargs[name] = arg637 638 getattr(self, "register_to_config")(**new_kwargs)639 original_init(self, *args, **kwargs)640 641 cls.__init__ = init642 return cls643 