Source code for trinity.utils.registry
import traceback
from typing import Any, Type
from trinity.utils.log import get_logger
[docs]
class Registry(object):
"""A class for registry."""
[docs]
def __init__(self, name: str):
"""
Args:
name (`str`): The name of the registry.
"""
self._name = name
self._modules = {}
self.logger = get_logger()
@property
def name(self) -> str:
"""
Get name of current registry.
Returns:
`str`: The name of current registry.
"""
return self._name
@property
def modules(self) -> dict:
"""
Get all modules in current registry.
Returns:
`dict`: A dict storing modules in current registry.
"""
return self._modules
[docs]
def get(self, module_key) -> Any:
"""
Get module named module_key from in current registry. If not found,
return None.
Args:
module_key (`str`): specified module name
Returns:
`Any`: the module object
"""
module = self._modules.get(module_key, None)
if module is None:
# try to dynamic import
if isinstance(module_key, str) and "." in module_key:
module_path, class_name = module_key.rsplit(".", 1)
try:
module = self._dynamic_import(module_path, class_name)
except Exception:
self.logger.error(
f"Failed to dynamically import {class_name} from {module_path}:\n"
+ traceback.format_exc()
)
raise ImportError(f"Cannot dynamically import {class_name} from {module_path}")
self._register_module(module_name=module_key, module_cls=module)
return module
def _register_module(self, module_name=None, module_cls=None, force=False):
"""
Register module to registry.
"""
if module_name is None:
module_name = module_cls.__name__
if module_name in self._modules and not force:
self.logger.warning(
f"{module_name} is already registered in {self._name}, "
f"if you want to override it, please set force=True."
)
raise KeyError(f"{module_name} is already registered in {self._name}")
self._modules[module_name] = module_cls
module_cls._name = module_name
[docs]
def register_module(self, module_name: str, module_cls: Type = None, force=False, lazy=False):
"""
Register module class object to registry with the specified module name.
Args:
module_name (`str`): The module name.
module_cls (`Type`): module class object
force (`bool`): Whether to override an existing class with
the same name. Default: False.
lazy (`bool`): Whether to register the module class object lazily.
Default: False.
Example:
.. code-block:: python
WORKFLOWS = Registry("workflows")
# register a module using decorator
@WORKFLOWS.register_module(name="workflow_name")
class MyWorkflow(Workflow):
pass
# or register a module directly
WORKFLOWS.register_module(
name="workflow_name",
module_cls=MyWorkflow,
force=True,
)
"""
if not (module_name is None or isinstance(module_name, str)):
raise TypeError(f"module_name must be either of None, str," f"got {type(module_name)}")
if module_cls is not None:
self._register_module(module_name=module_name, module_cls=module_cls, force=force)
return module_cls
# if module_cls is None, should return a decorator function
def _register(module_cls):
"""
Register module class object to registry.
Args:
module_cls (`Type`): module class object
Returns:
`Type`: Decorated module class object.
"""
self._register_module(module_name=module_name, module_cls=module_cls, force=force)
return module_cls
return _register
def _dynamic_import(self, module_path: str, class_name: str) -> Type:
"""
Dynamically import a module class object from the specified module path.
Args:
module_path (`str`): The module path. For example, "my_package.my_module".
class_name (`str`): The class name. For example, "MyWorkflow".
Returns:
`Type`: The imported module class object.
"""
import importlib
module = importlib.import_module(module_path)
module_cls = getattr(module, class_name)
return module_cls