diff --git a/mmengine/config/lazy.py b/mmengine/config/lazy.py index ab5ce35d..9018b49a 100644 --- a/mmengine/config/lazy.py +++ b/mmengine/config/lazy.py @@ -147,19 +147,24 @@ class LazyAttr: if isinstance(self.source, LazyObject): if isinstance(self.source._module, str): - # In this case, the source code of LazyObject could be one of - # the following: - # 1. import xxx.yyy as zzz - # 2. from xxx.yyy import zzz - - # The equivalent code of LazyObject is: - # 1. zzz = LazyObject('xxx.yyy') - # 2. zzz = LazyObject('xxx.yyy', 'zzz') - - # The source code of LazyAttr will be: - # eee = zzz.eee - # Then, eee._module = xxx.yyy - self._module = self.source._module + if self.source._imported is None: + # source code: + # from xxx.yyy import zzz + # equivalent code: + # zzz = LazyObject('xxx.yyy', 'zzz') + # The source code of get attribute: + # eee = zzz.eee + # Then, `eee._module` should be "xxx.yyy.zzz" + self._module = self.source._module + else: + # source code: + # import xxx.yyy as zzz + # equivalent code: + # zzz = LazyObject('xxx.yyy') + # The source code of get attribute: + # eee = zzz.eee + # Then, `eee._module` should be "xxx.yyy" + self._module = f'{self.source._module}.{self.source}' else: # The source code of LazyObject should be # 1. import xxx.yyy diff --git a/mmengine/registry/registry.py b/mmengine/registry/registry.py index f2c21b06..31fd44d8 100644 --- a/mmengine/registry/registry.py +++ b/mmengine/registry/registry.py @@ -442,19 +442,6 @@ class Registry: 'The key argument of `Registry.get` must be a str, ' f'got {type(key)}') - # Actually, it's strange to implement this `try ... except` to get the - # object by its name in `Registry.get`. However, If we want to build - # the model using a configuration like - # `dict(type='mmengine.model.BaseModel')`, which can - # be dumped by lazy import config, we need this code snippet - # for `Registry.get` to work. - try: - obj_cls = get_object_from_string(key) - except Exception: - raise RuntimeError(f'Failed to get {key}') - if obj_cls is not None: - return obj_cls - scope, real_key = self.split_scope_key(key) obj_cls = None registry_name = self.name @@ -508,6 +495,18 @@ class Registry: else: obj_cls = root.get(key) + if obj_cls is None: + # Actually, it's strange to implement this `try ... except` to + # get the object by its name in `Registry.get`. However, If we + # want to build the model using a configuration like + # `dict(type='mmengine.model.BaseModel')`, which can + # be dumped by lazy import config, we need this code snippet + # for `Registry.get` to work. + try: + obj_cls = get_object_from_string(key) + except Exception: + raise RuntimeError(f'Failed to get {key}') + if obj_cls is not None: # For some rare cases (e.g. obj_cls is a partial function), obj_cls # doesn't have `__name__`. Use default value to prevent error @@ -517,6 +516,7 @@ class Registry: f' registry in "{scope_name}"', logger='current', level=logging.DEBUG) + return obj_cls def _search_child(self, scope: str) -> Optional['Registry']: diff --git a/tests/data/config/lazy_module_config/test_ast_transform.py b/tests/data/config/lazy_module_config/test_ast_transform.py index 8e8b02f4..a8803dde 100644 --- a/tests/data/config/lazy_module_config/test_ast_transform.py +++ b/tests/data/config/lazy_module_config/test_ast_transform.py @@ -11,3 +11,5 @@ from mmengine.fileio import LocalBackend as local from mmengine.fileio import PetrelBackend from ._base_.default_runtime import default_scope as scope from ._base_.scheduler import val_cfg +from rich.progress import Progress +start = Progress.start diff --git a/tests/test_config/test_lazy.py b/tests/test_config/test_lazy.py index 265f22de..d6982281 100644 --- a/tests/test_config/test_lazy.py +++ b/tests/test_config/test_lazy.py @@ -10,6 +10,7 @@ from unittest import TestCase import numpy import numpy.compat import numpy.linalg as linalg +from rich.progress import Progress import mmengine from mmengine.config import Config @@ -62,12 +63,18 @@ class TestImportTransformer(TestCase): self.assertIs(imported_numpy.linalg, linalg) self.assertIs(imported_numpy.compat, numpy.compat) - # 1.4 Build module from LazyAttr + # 1.4.1 Build module from LazyAttr imported_linalg = lazy_numpy.linalg.build() imported_compat = lazy_numpy.compat.build() self.assertIs(imported_compat, numpy.compat) self.assertIs(imported_linalg, linalg) + # 1.4.2 build class method from LazyAttr + start = global_dict['start'] + self.assertEqual(start.module, 'rich.progress.Progress') + self.assertEqual(str(start), 'start') + self.assertIs(start.build(), Progress.start) + # 1.5 import ... as, and build module from LazyObject lazy_linalg = global_dict['linalg'] self.assertIsInstance(lazy_linalg, LazyObject)