| | |
| | import importlib |
| | from typing import Any, Optional, Union |
| |
|
| | from mmengine.utils import is_seq_of |
| |
|
| |
|
| | class LazyObject: |
| | """LazyObject is used to lazily initialize the imported module during |
| | parsing the configuration file. |
| | |
| | During parsing process, the syntax like: |
| | |
| | Examples: |
| | >>> import torch.nn as nn |
| | >>> from mmdet.models import RetinaNet |
| | >>> import mmcls.models |
| | >>> import mmcls.datasets |
| | >>> import mmcls |
| | |
| | Will be parsed as: |
| | |
| | Examples: |
| | >>> # import torch.nn as nn |
| | >>> nn = lazyObject('torch.nn') |
| | >>> # from mmdet.models import RetinaNet |
| | >>> RetinaNet = lazyObject('mmdet.models', 'RetinaNet') |
| | >>> # import mmcls.models; import mmcls.datasets; import mmcls |
| | >>> mmcls = lazyObject(['mmcls', 'mmcls.datasets', 'mmcls.models']) |
| | |
| | ``LazyObject`` records all module information and will be further |
| | referenced by the configuration file. |
| | |
| | Args: |
| | module (str or list or tuple): The module name to be imported. |
| | imported (str, optional): The imported module name. Defaults to None. |
| | location (str, optional): The filename and line number of the imported |
| | module statement happened. |
| | """ |
| |
|
| | def __init__(self, |
| | module: Union[str, list, tuple], |
| | imported: Optional[str] = None, |
| | location: Optional[str] = None): |
| | if not isinstance(module, str) and not is_seq_of(module, str): |
| | raise TypeError('module should be `str`, `list`, or `tuple`' |
| | f'but got {type(module)}, this might be ' |
| | 'a bug of MMEngine, please report it to ' |
| | 'https://github.com/open-mmlab/mmengine/issues') |
| | self._module: Union[str, list, tuple] = module |
| |
|
| | if not isinstance(imported, str) and imported is not None: |
| | raise TypeError('imported should be `str` or None, but got ' |
| | f'{type(imported)}, this might be ' |
| | 'a bug of MMEngine, please report it to ' |
| | 'https://github.com/open-mmlab/mmengine/issues') |
| | self._imported = imported |
| | self.location = location |
| |
|
| | def build(self) -> Any: |
| | """Return imported object. |
| | |
| | Returns: |
| | Any: Imported object |
| | """ |
| | if isinstance(self._module, str): |
| | try: |
| | module = importlib.import_module(self._module) |
| | except Exception as e: |
| | raise type(e)(f'Failed to import {self._module} ' |
| | f'in {self.location} for {e}') |
| |
|
| | if self._imported is not None: |
| | if hasattr(module, self._imported): |
| | module = getattr(module, self._imported) |
| | else: |
| | raise ImportError( |
| | f'Failed to import {self._imported} ' |
| | f'from {self._module} in {self.location}') |
| |
|
| | return module |
| | else: |
| | |
| | |
| | |
| | |
| | try: |
| | for module in self._module: |
| | importlib.import_module(module) |
| | module_name = self._module[0].split('.')[0] |
| | return importlib.import_module(module_name) |
| | except Exception as e: |
| | raise type(e)(f'Failed to import {self.module} ' |
| | f'in {self.location} for {e}') |
| |
|
| | @property |
| | def module(self): |
| | if isinstance(self._module, str): |
| | return self._module |
| | return self._module[0].split('.')[0] |
| |
|
| | def __call__(self, *args, **kwargs): |
| | raise RuntimeError() |
| |
|
| | def __deepcopy__(self, memo): |
| | return LazyObject(self._module, self._imported, self.location) |
| |
|
| | def __getattr__(self, name): |
| | |
| | |
| | if self.location is not None: |
| | location = self.location.split(', line')[0] |
| | else: |
| | location = self.location |
| | return LazyAttr(name, self, location) |
| |
|
| | def __str__(self) -> str: |
| | if self._imported is not None: |
| | return self._imported |
| | return self.module |
| |
|
| | __repr__ = __str__ |
| |
|
| | |
| | |
| | |
| | |
| | def __getstate__(self): |
| | return self.__dict__ |
| |
|
| | def __setstate__(self, state): |
| | self.__dict__ = state |
| |
|
| |
|
| | class LazyAttr: |
| | """The attribute of the LazyObject. |
| | |
| | When parsing the configuration file, the imported syntax will be |
| | parsed as the assignment ``LazyObject``. During the subsequent parsing |
| | process, users may reference the attributes of the LazyObject. |
| | To ensure that these attributes also contain information needed to |
| | reconstruct the attribute itself, LazyAttr was introduced. |
| | |
| | Examples: |
| | >>> models = LazyObject(['mmdet.models']) |
| | >>> model = dict(type=models.RetinaNet) |
| | >>> print(type(model['type'])) # <class 'mmengine.config.lazy.LazyAttr'> |
| | >>> print(model['type'].build()) # <class 'mmdet.models.detectors.retinanet.RetinaNet'> |
| | """ |
| |
|
| | def __init__(self, |
| | name: str, |
| | source: Union['LazyObject', 'LazyAttr'], |
| | location=None): |
| | self.name = name |
| | self.source: Union[LazyAttr, LazyObject] = source |
| |
|
| | if isinstance(self.source, LazyObject): |
| | if isinstance(self.source._module, str): |
| | if self.source._imported is None: |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | self._module = self.source._module |
| | else: |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | self._module = f'{self.source._module}.{self.source}' |
| | else: |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | self._module = str(self.source) |
| | elif isinstance(self.source, LazyAttr): |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | self._module = f'{self.source._module}.{self.source.name}' |
| | self.location = location |
| |
|
| | @property |
| | def module(self): |
| | return self._module |
| |
|
| | def __call__(self, *args, **kwargs: Any) -> Any: |
| | raise RuntimeError() |
| |
|
| | def __getattr__(self, name: str) -> 'LazyAttr': |
| | return LazyAttr(name, self) |
| |
|
| | def __deepcopy__(self, memo): |
| | return LazyAttr(self.name, self.source) |
| |
|
| | def build(self) -> Any: |
| | """Return the attribute of the imported object. |
| | |
| | Returns: |
| | Any: attribute of the imported object. |
| | """ |
| | obj = self.source.build() |
| | try: |
| | return getattr(obj, self.name) |
| | except AttributeError: |
| | raise ImportError(f'Failed to import {self.module}.{self.name} in ' |
| | f'{self.location}') |
| | except ImportError as e: |
| | raise e |
| |
|
| | def __str__(self) -> str: |
| | return self.name |
| |
|
| | __repr__ = __str__ |
| |
|
| | |
| | |
| | |
| | |
| | def __getstate__(self): |
| | return self.__dict__ |
| |
|
| | def __setstate__(self, state): |
| | self.__dict__ = state |
| |
|