Skip to content

Registry

ConfigRegistry

Bases: Registry

ConfigRegistry for components that can be initialised with a config.

ConfigRegistry is particularly useful when you want to construct a component from a configuration, such as a Hugginface Transformers model.

See Also

Registry: General purpose Registry.

Examples:

Python Console Session
>>> from dataclasses import dataclass, field
>>> @dataclass
... class Config:
...     a: int
...     b: int
...     mode: str = "proj"
>>> registry = ConfigRegistry(key="mode")
>>> @registry.register("proj")
... class Proj:
...     def __init__(self, config):
...         self.a = config.a
...         self.b = config.b
>>> @registry.register("inv")
... class Inv:
...     def __init__(self, config):
...         self.a = config.b
...         self.b = config.a
>>> registry
ConfigRegistry(
  ('proj'): <class 'chanfig.registry.Proj'>
  ('inv'): <class 'chanfig.registry.Inv'>
)
>>> config = Config(a=0, b=1)
>>> module = registry.build(config)
>>> module.a, module.b
(0, 1)
>>> config = Config(a=0, b=1, mode="inv")
>>> module = registry.build(config)
>>> module.a, module.b
(1, 0)
>>> @dataclass
... class ModuleConfig:
...     a: int = 0
...     b: int = 1
...     mode: str = "proj"
>>> @dataclass
... class NestedConfig:
...     module: ModuleConfig = field(default_factory=ModuleConfig)
>>> nested_registry = ConfigRegistry(key="module.mode")
>>> @nested_registry.register("proj")
... class Proj:
...     def __init__(self, config):
...         self.a = config.module.a
...         self.b = config.module.b
>>> @nested_registry.register("inv")
... class Inv:
...     def __init__(self, config):
...         self.a = config.module.b
...         self.b = config.module.a
>>> nested_config = NestedConfig()
>>> module = nested_registry.build(nested_config)
>>> module.a, module.b
(0, 1)
Source code in chanfig/registry.py
Python
class ConfigRegistry(Registry):
    """
    `ConfigRegistry` for components that can be initialised with a `config`.

    `ConfigRegistry` is particularly useful when you want to construct a component from a configuration, such as a
    Hugginface Transformers model.

    See Also:
        [`Registry`][chanfig.Registry]: General purpose Registry.

    Examples:
        >>> from dataclasses import dataclass, field
        >>> @dataclass
        ... class Config:
        ...     a: int
        ...     b: int
        ...     mode: str = "proj"
        >>> registry = ConfigRegistry(key="mode")
        >>> @registry.register("proj")
        ... class Proj:
        ...     def __init__(self, config):
        ...         self.a = config.a
        ...         self.b = config.b
        >>> @registry.register("inv")
        ... class Inv:
        ...     def __init__(self, config):
        ...         self.a = config.b
        ...         self.b = config.a
        >>> registry
        ConfigRegistry(
          ('proj'): <class 'chanfig.registry.Proj'>
          ('inv'): <class 'chanfig.registry.Inv'>
        )
        >>> config = Config(a=0, b=1)
        >>> module = registry.build(config)
        >>> module.a, module.b
        (0, 1)
        >>> config = Config(a=0, b=1, mode="inv")
        >>> module = registry.build(config)
        >>> module.a, module.b
        (1, 0)
        >>> @dataclass
        ... class ModuleConfig:
        ...     a: int = 0
        ...     b: int = 1
        ...     mode: str = "proj"
        >>> @dataclass
        ... class NestedConfig:
        ...     module: ModuleConfig = field(default_factory=ModuleConfig)
        >>> nested_registry = ConfigRegistry(key="module.mode")
        >>> @nested_registry.register("proj")
        ... class Proj:
        ...     def __init__(self, config):
        ...         self.a = config.module.a
        ...         self.b = config.module.b
        >>> @nested_registry.register("inv")
        ... class Inv:
        ...     def __init__(self, config):
        ...         self.a = config.module.b
        ...         self.b = config.module.a
        >>> nested_config = NestedConfig()
        >>> module = nested_registry.build(nested_config)
        >>> module.a, module.b
        (0, 1)
    """

    @staticmethod
    def init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
        r"""
        Constructor of component.

        Args:
            cls: The component to construct.
            *args: The arguments to pass to the component.
            **kwargs: The keyword arguments to pass to the component.

        Returns:
            (Any):

        Examples:
            >>> class Module:
            ...     def __init__(self, config, a=None, b=None):
            ...         self.config = config
            ...         self.a = config.a if a is None else a
            ...         self.b = config.b if b is None else b
            >>> config = NestedDict({"a": 0, "b": 1})
            >>> module = ConfigRegistry.init(Module, config, b=2)
            >>> module.a, module.b
            (0, 2)
        """

        return cls(config, *args, **kwargs)

    def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]
        r"""
        Build a component.

        Args:
            config

        Returns:
            (Any):

        Raises:
            KeyError: If the component is not registered.

        Examples:
            >>> from dataclasses import dataclass, field
            >>> registry = ConfigRegistry(key="module.mode")
            >>> @registry.register("proj")
            ... class Proj:
            ...     def __init__(self, config):
            ...         self.a = config.module.a
            ...         self.b = config.module.b
            >>> @registry.register("inv")
            ... class Inv:
            ...     def __init__(self, config):
            ...         self.a = config.module.b
            ...         self.b = config.module.a
            >>> @dataclass
            ... class ModuleConfig:
            ...     a: int = 0
            ...     b: int = 1
            ...     mode: str = "proj"
            >>> @dataclass
            ... class Config:
            ...     module: ModuleConfig = field(default_factory=ModuleConfig)
            >>> config = Config()
            >>> module = registry.build(config)
            >>> type(module)
            <class 'chanfig.registry.Proj'>
            >>> module.a, module.b
            (0, 1)
            >>> type(module)
            <class 'chanfig.registry.Proj'>
        """

        key = self.getattr("key")
        config_ = deepcopy(config)

        while "." in key:
            key, rest = key.split(".", 1)
            config_, key = getattr(config_, key), rest
        name = getattr(config_, key)

        return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]

build(config, *args, **kwargs)

Build a component.

Returns:

Type Description
Any

Raises:

Type Description
KeyError

If the component is not registered.

Examples:

Python Console Session
>>> from dataclasses import dataclass, field
>>> registry = ConfigRegistry(key="module.mode")
>>> @registry.register("proj")
... class Proj:
...     def __init__(self, config):
...         self.a = config.module.a
...         self.b = config.module.b
>>> @registry.register("inv")
... class Inv:
...     def __init__(self, config):
...         self.a = config.module.b
...         self.b = config.module.a
>>> @dataclass
... class ModuleConfig:
...     a: int = 0
...     b: int = 1
...     mode: str = "proj"
>>> @dataclass
... class Config:
...     module: ModuleConfig = field(default_factory=ModuleConfig)
>>> config = Config()
>>> module = registry.build(config)
>>> type(module)
<class 'chanfig.registry.Proj'>
>>> module.a, module.b
(0, 1)
>>> type(module)
<class 'chanfig.registry.Proj'>
Source code in chanfig/registry.py
Python
def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]
    r"""
    Build a component.

    Args:
        config

    Returns:
        (Any):

    Raises:
        KeyError: If the component is not registered.

    Examples:
        >>> from dataclasses import dataclass, field
        >>> registry = ConfigRegistry(key="module.mode")
        >>> @registry.register("proj")
        ... class Proj:
        ...     def __init__(self, config):
        ...         self.a = config.module.a
        ...         self.b = config.module.b
        >>> @registry.register("inv")
        ... class Inv:
        ...     def __init__(self, config):
        ...         self.a = config.module.b
        ...         self.b = config.module.a
        >>> @dataclass
        ... class ModuleConfig:
        ...     a: int = 0
        ...     b: int = 1
        ...     mode: str = "proj"
        >>> @dataclass
        ... class Config:
        ...     module: ModuleConfig = field(default_factory=ModuleConfig)
        >>> config = Config()
        >>> module = registry.build(config)
        >>> type(module)
        <class 'chanfig.registry.Proj'>
        >>> module.a, module.b
        (0, 1)
        >>> type(module)
        <class 'chanfig.registry.Proj'>
    """

    key = self.getattr("key")
    config_ = deepcopy(config)

    while "." in key:
        key, rest = key.split(".", 1)
        config_, key = getattr(config_, key), rest
    name = getattr(config_, key)

    return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]

init(config, *args, **kwargs) staticmethod

Constructor of component.

Parameters:

Name Type Description Default
cls Callable

The component to construct.

required
*args Any

The arguments to pass to the component.

()
**kwargs Any

The keyword arguments to pass to the component.

{}

Returns:

Type Description
Any

Examples:

Python Console Session
>>> class Module:
...     def __init__(self, config, a=None, b=None):
...         self.config = config
...         self.a = config.a if a is None else a
...         self.b = config.b if b is None else b
>>> config = NestedDict({"a": 0, "b": 1})
>>> module = ConfigRegistry.init(Module, config, b=2)
>>> module.a, module.b
(0, 2)
Source code in chanfig/registry.py
Python
@staticmethod
def init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
    r"""
    Constructor of component.

    Args:
        cls: The component to construct.
        *args: The arguments to pass to the component.
        **kwargs: The keyword arguments to pass to the component.

    Returns:
        (Any):

    Examples:
        >>> class Module:
        ...     def __init__(self, config, a=None, b=None):
        ...         self.config = config
        ...         self.a = config.a if a is None else a
        ...         self.b = config.b if b is None else b
        >>> config = NestedDict({"a": 0, "b": 1})
        >>> module = ConfigRegistry.init(Module, config, b=2)
        >>> module.a, module.b
        (0, 2)
    """

    return cls(config, *args, **kwargs)

Registry

Bases: NestedDict

Registry for components.

Registry provides 3 core functionalities:

  • Register a new component.
  • Lookup for a component.
  • Build a component.

To facilitate the usage scenario, registry is designed to be a decorator. You could register a component by simply calling registry.register, and it will be registered with its name. You may also specify the name of the component by calling registry.register(name="ComponentName").

build makes it easy to construct a component from a configuration. build automatically determines the component to construct by the name field in the configuration. So you could either call registry.build(config) or registry.build(**config). Beyond this, build is just a syntax sugar for registry.init(registry.lookup(name), *args, **kwargs).

lookup is used to lookup for a component by its name. By default, lookup internally calls NestedDict.get, but you may override it to provide more functionalities.

init is used to construct a component. By default, init internally calls cls(*args, **kwargs), but you may override it to provide more functionalities.

Notes

Registry inherits from NestedDict.

Therefore, Registry comes in a nested structure by nature. You could create a sub-registry by simply calling registry.sub_registry = Registry, and access through registry.sub_registry.register().

See Also

ConfigRegistry: Optimised for components that can be initialised with a config.

Examples:

Python Console Session
>>> registry = Registry()
>>> @registry.register
... @registry.register("Module1", default=True)
... class Module:
...     def __init__(self, a, b):
...         self.a = a
...         self.b = b
>>> module = registry.register(Module, "Module2")
>>> registry
Registry(
  ('Module1'): <class 'chanfig.registry.Module'>
  ('Module'): <class 'chanfig.registry.Module'>
  ('Module2'): <class 'chanfig.registry.Module'>
)
>>> module = registry.register(Module, "Module")
Traceback (most recent call last):
ValueError: Component with name Module already registered.
>>> registry.lookup("Module")
<class 'chanfig.registry.Module'>
>>> config = {"module": {"name": "Module", "a": 0, "b": 1}}
>>> # registry.register(Module)
>>> module = registry.build(config["module"])
>>> type(module)
<class 'chanfig.registry.Module'>
>>> module.a, module.b
(0, 1)
>>> config = {"module": {"name": "NE", "a": 1, "b": 0}}
>>> module = registry.build(config["module"])
>>> module.a, module.b
(1, 0)
Source code in chanfig/registry.py
Python
class Registry(NestedDict):
    """
    `Registry` for components.

    `Registry` provides 3 core functionalities:

    - Register a new component.
    - Lookup for a component.
    - Build a component.

    To facilitate the usage scenario, `registry` is designed to be a decorator.
    You could register a component by simply calling `registry.register`, and it will be registered with its name.
    You may also specify the name of the component by calling `registry.register(name="ComponentName")`.

    `build` makes it easy to construct a component from a configuration.
    `build` automatically determines the component to construct by the `name` field in the configuration.
    So you could either call `registry.build(config)` or `registry.build(**config)`.
    Beyond this, `build` is just a syntax sugar for `registry.init(registry.lookup(name), *args, **kwargs)`.

    `lookup` is used to lookup for a component by its name.
    By default, `lookup` internally calls `NestedDict.get`, but you may override it to provide more functionalities.

    `init` is used to construct a component.
    By default, `init` internally calls `cls(*args, **kwargs)`, but you may override it to provide more functionalities.

    Notes:
        `Registry` inherits from `NestedDict`.

        Therefore, `Registry` comes in a nested structure by nature.
        You could create a sub-registry by simply calling `registry.sub_registry = Registry`,
        and access through `registry.sub_registry.register()`.

    See Also:
        [`ConfigRegistry`][chanfig.ConfigRegistry]: Optimised for components that can be initialised with a `config`.

    Examples:
        >>> registry = Registry()
        >>> @registry.register
        ... @registry.register("Module1", default=True)
        ... class Module:
        ...     def __init__(self, a, b):
        ...         self.a = a
        ...         self.b = b
        >>> module = registry.register(Module, "Module2")
        >>> registry
        Registry(
          ('Module1'): <class 'chanfig.registry.Module'>
          ('Module'): <class 'chanfig.registry.Module'>
          ('Module2'): <class 'chanfig.registry.Module'>
        )
        >>> module = registry.register(Module, "Module")
        Traceback (most recent call last):
        ValueError: Component with name Module already registered.
        >>> registry.lookup("Module")
        <class 'chanfig.registry.Module'>
        >>> config = {"module": {"name": "Module", "a": 0, "b": 1}}
        >>> # registry.register(Module)
        >>> module = registry.build(config["module"])
        >>> type(module)
        <class 'chanfig.registry.Module'>
        >>> module.a, module.b
        (0, 1)
        >>> config = {"module": {"name": "NE", "a": 1, "b": 0}}
        >>> module = registry.build(config["module"])
        >>> module.a, module.b
        (1, 0)
    """

    override = False
    key = "name"
    default = Null

    def __init__(
        self,
        override: bool | None = None,
        key: str | None = None,
        fallback: bool | None = None,
        default: Any = None,
        default_factory: Callable | NULL = Null,
    ):
        super().__init__(default_factory=default_factory, fallback=fallback)
        if override is not None:
            self.setattr("override", override)
        if key is not None:
            self.setattr("key", key)
        if default is not None:
            self.setattr("default", default)

    def register(
        self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False
    ) -> Callable:
        r"""
        Register a new component.

        Args:
            component: The component to register.
            name: The name of the component.

        Returns:
            component: The registered component.
                Registered component are expected to be `Callable`.

        Raises:
            ValueError: If the component with the same name already registered and `Registry.override=False`.

        Examples:
            >>> registry = Registry()
            >>> @registry.register
            ... @registry.register("Module1")
            ... class Module:
            ...     def __init__(self, a, b):
            ...         self.a = a
            ...         self.b = b
            >>> module = registry.register(Module, "Module2")
            >>> registry
            Registry(
              ('Module1'): <class 'chanfig.registry.Module'>
              ('Module'): <class 'chanfig.registry.Module'>
              ('Module2'): <class 'chanfig.registry.Module'>
            )
        """

        if name in self and not (override or self.override):
            raise ValueError(f"Component with name {name} already registered.")

        # Registry.register()
        if name is not Null:
            self.set(name, component)
            if default:
                self.setattr("default", component)
            return component
        # @Registry.register
        if component is not Null and callable(component) and name is Null:
            self.set(component.__name__, component)
            if default:
                self.setattr("default", component)
            return component

        # @Registry.register()
        def decorator(name: Any = Null):
            @wraps(self.register)
            def wrapper(component):
                if name is Null:
                    self.set(component.__name__, component)
                else:
                    self.set(name, component)
                if default:
                    self.setattr("default", component)
                return component

            return wrapper

        return decorator(component)

    def lookup(self, name: str, default: Any = Null) -> Any:
        r"""
        Lookup for a component.

        Args:
            name:

        Returns:
            (Any): The component.

        Raises:
            KeyError: If the component is not registered.

        Examples:
            >>> registry = Registry()
            >>> @registry.register
            ... class Module:
            ...     def __init__(self, a, b):
            ...         self.a = a
            ...         self.b = b
            >>> registry.lookup("Module")
            <class 'chanfig.registry.Module'>
        """

        if default is Null:
            default = self.getattr("default", Null)
        element = self.get(name, default)
        if isinstance(element, Registry):
            return element.getattr("default")
        return element

    @staticmethod
    def init(cls: Callable, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
        r"""
        Constructor of component.

        Args:
            cls: The component to construct.
            *args: The arguments to pass to the component.
            **kwargs: The keyword arguments to pass to the component.

        Returns:
            (Any):

        Examples:
            >>> class Module:
            ...     def __init__(self, a, b):
            ...         self.a = a
            ...         self.b = b
            >>> kwargs = {"a": 0, "b": 1}
            >>> module = Registry.init(Module, **kwargs)
            >>> type(module)
            <class 'chanfig.registry.Module'>
            >>> module.a, module.b
            (0, 1)
        """

        return cls(*args, **kwargs)

    def build(self, name: str | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any:
        r"""
        Build a component.

        Args:
            name (str | MutableMapping):
                If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`.
                Note that values in `kwargs` will override values in `name` if its a `MutableMapping`.
            *args: The arguments to pass to the component.
            **kwargs: The keyword arguments to pass to the component.

        Returns:
            (Any):

        Raises:
            KeyError: If the component is not registered.

        Examples:
            >>> registry = Registry(key="model")
            >>> @registry.register
            ... class Module:
            ...     def __init__(self, a, b):
            ...         self.a = a
            ...         self.b = b
            >>> config = {"module": {"model": "Module", "a": 1, "b": 2}}
            >>> # registry.register(Module)
            >>> module = registry.build(**config["module"])
            >>> type(module)
            <class 'chanfig.registry.Module'>
            >>> module.a
            1
            >>> module.b
            2
            >>> module = registry.build(config["module"], a=2)
            >>> module.a
            2
        """

        if isinstance(name, MutableMapping):
            name = deepcopy(name)
            name, kwargs = name.pop(self.getattr("key", "name")), dict(name, **kwargs)  # type: ignore[arg-type]
        if name is Null:
            name, kwargs = kwargs.pop(self.getattr("key"), None), dict(**kwargs)
        return self.init(self.lookup(name), *args, **kwargs)  # type: ignore[arg-type]

build(name=Null, *args, **kwargs)

Build a component.

Parameters:

Name Type Description Default
name str | MutableMapping

If its a MutableMapping, it must contain key as a member, the rest will be treated as **kwargs. Note that values in kwargs will override values in name if its a MutableMapping.

Null
*args Any

The arguments to pass to the component.

()
**kwargs Any

The keyword arguments to pass to the component.

{}

Returns:

Type Description
Any

Raises:

Type Description
KeyError

If the component is not registered.

Examples:

Python Console Session
>>> registry = Registry(key="model")
>>> @registry.register
... class Module:
...     def __init__(self, a, b):
...         self.a = a
...         self.b = b
>>> config = {"module": {"model": "Module", "a": 1, "b": 2}}
>>> # registry.register(Module)
>>> module = registry.build(**config["module"])
>>> type(module)
<class 'chanfig.registry.Module'>
>>> module.a
1
>>> module.b
2
>>> module = registry.build(config["module"], a=2)
>>> module.a
2
Source code in chanfig/registry.py
Python
def build(self, name: str | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any:
    r"""
    Build a component.

    Args:
        name (str | MutableMapping):
            If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`.
            Note that values in `kwargs` will override values in `name` if its a `MutableMapping`.
        *args: The arguments to pass to the component.
        **kwargs: The keyword arguments to pass to the component.

    Returns:
        (Any):

    Raises:
        KeyError: If the component is not registered.

    Examples:
        >>> registry = Registry(key="model")
        >>> @registry.register
        ... class Module:
        ...     def __init__(self, a, b):
        ...         self.a = a
        ...         self.b = b
        >>> config = {"module": {"model": "Module", "a": 1, "b": 2}}
        >>> # registry.register(Module)
        >>> module = registry.build(**config["module"])
        >>> type(module)
        <class 'chanfig.registry.Module'>
        >>> module.a
        1
        >>> module.b
        2
        >>> module = registry.build(config["module"], a=2)
        >>> module.a
        2
    """

    if isinstance(name, MutableMapping):
        name = deepcopy(name)
        name, kwargs = name.pop(self.getattr("key", "name")), dict(name, **kwargs)  # type: ignore[arg-type]
    if name is Null:
        name, kwargs = kwargs.pop(self.getattr("key"), None), dict(**kwargs)
    return self.init(self.lookup(name), *args, **kwargs)  # type: ignore[arg-type]

init(*args, **kwargs) staticmethod

Constructor of component.

Parameters:

Name Type Description Default
cls Callable

The component to construct.

required
*args Any

The arguments to pass to the component.

()
**kwargs Any

The keyword arguments to pass to the component.

{}

Returns:

Type Description
Any

Examples:

Python Console Session
>>> class Module:
...     def __init__(self, a, b):
...         self.a = a
...         self.b = b
>>> kwargs = {"a": 0, "b": 1}
>>> module = Registry.init(Module, **kwargs)
>>> type(module)
<class 'chanfig.registry.Module'>
>>> module.a, module.b
(0, 1)
Source code in chanfig/registry.py
Python
@staticmethod
def init(cls: Callable, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
    r"""
    Constructor of component.

    Args:
        cls: The component to construct.
        *args: The arguments to pass to the component.
        **kwargs: The keyword arguments to pass to the component.

    Returns:
        (Any):

    Examples:
        >>> class Module:
        ...     def __init__(self, a, b):
        ...         self.a = a
        ...         self.b = b
        >>> kwargs = {"a": 0, "b": 1}
        >>> module = Registry.init(Module, **kwargs)
        >>> type(module)
        <class 'chanfig.registry.Module'>
        >>> module.a, module.b
        (0, 1)
    """

    return cls(*args, **kwargs)

lookup(name, default=Null)

Lookup for a component.

Parameters:

Name Type Description Default
name str
required

Returns:

Type Description
Any

The component.

Raises:

Type Description
KeyError

If the component is not registered.

Examples:

Python Console Session
>>> registry = Registry()
>>> @registry.register
... class Module:
...     def __init__(self, a, b):
...         self.a = a
...         self.b = b
>>> registry.lookup("Module")
<class 'chanfig.registry.Module'>
Source code in chanfig/registry.py
Python
def lookup(self, name: str, default: Any = Null) -> Any:
    r"""
    Lookup for a component.

    Args:
        name:

    Returns:
        (Any): The component.

    Raises:
        KeyError: If the component is not registered.

    Examples:
        >>> registry = Registry()
        >>> @registry.register
        ... class Module:
        ...     def __init__(self, a, b):
        ...         self.a = a
        ...         self.b = b
        >>> registry.lookup("Module")
        <class 'chanfig.registry.Module'>
    """

    if default is Null:
        default = self.getattr("default", Null)
    element = self.get(name, default)
    if isinstance(element, Registry):
        return element.getattr("default")
    return element

register(component=Null, name=Null, override=False, default=False)

Register a new component.

Parameters:

Name Type Description Default
component Any

The component to register.

Null
name Any

The name of the component.

Null

Returns:

Name Type Description
component Callable

The registered component. Registered component are expected to be Callable.

Raises:

Type Description
ValueError

If the component with the same name already registered and Registry.override=False.

Examples:

Python Console Session
>>> registry = Registry()
>>> @registry.register
... @registry.register("Module1")
... class Module:
...     def __init__(self, a, b):
...         self.a = a
...         self.b = b
>>> module = registry.register(Module, "Module2")
>>> registry
Registry(
  ('Module1'): <class 'chanfig.registry.Module'>
  ('Module'): <class 'chanfig.registry.Module'>
  ('Module2'): <class 'chanfig.registry.Module'>
)
Source code in chanfig/registry.py
Python
def register(
    self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False
) -> Callable:
    r"""
    Register a new component.

    Args:
        component: The component to register.
        name: The name of the component.

    Returns:
        component: The registered component.
            Registered component are expected to be `Callable`.

    Raises:
        ValueError: If the component with the same name already registered and `Registry.override=False`.

    Examples:
        >>> registry = Registry()
        >>> @registry.register
        ... @registry.register("Module1")
        ... class Module:
        ...     def __init__(self, a, b):
        ...         self.a = a
        ...         self.b = b
        >>> module = registry.register(Module, "Module2")
        >>> registry
        Registry(
          ('Module1'): <class 'chanfig.registry.Module'>
          ('Module'): <class 'chanfig.registry.Module'>
          ('Module2'): <class 'chanfig.registry.Module'>
        )
    """

    if name in self and not (override or self.override):
        raise ValueError(f"Component with name {name} already registered.")

    # Registry.register()
    if name is not Null:
        self.set(name, component)
        if default:
            self.setattr("default", component)
        return component
    # @Registry.register
    if component is not Null and callable(component) and name is Null:
        self.set(component.__name__, component)
        if default:
            self.setattr("default", component)
        return component

    # @Registry.register()
    def decorator(name: Any = Null):
        @wraps(self.register)
        def wrapper(component):
            if name is Null:
                self.set(component.__name__, component)
            else:
                self.set(name, component)
            if default:
                self.setattr("default", component)
            return component

        return wrapper

    return decorator(component)