Source code for coma.core.command

"""Decorator for declaring a coma command without explicit calls to coma.register()."""

from typing import Callable, Optional, get_origin  # NOTE: requires Python >= 3.8
from inspect import signature

from .internal import store_registration
from .register import register


[docs] def command( name: str, parser_hook: Optional[Callable] = None, pre_config_hook: Optional[Callable] = None, config_hook: Optional[Callable] = None, post_config_hook: Optional[Callable] = None, pre_init_hook: Optional[Callable] = None, init_hook: Optional[Callable] = None, post_init_hook: Optional[Callable] = None, pre_run_hook: Optional[Callable] = None, run_hook: Optional[Callable] = None, post_run_hook: Optional[Callable] = None, parser_kwargs: Optional[dict] = None, ): """ Decorator declaring the decorated object as a coma command. Acts as a lightweight wrapper around :func:`~coma.core.register.register`. Specifically, inspects the decorated object's signature (either the function signature if the object is a function, or the signature of the __init__() method if the object is a class), and calls :obj:`coma.register()` with its :obj:`**id_configs` keyword arguments field populated using the signature parameters. The name of each parameter is used as the config ID and the type hint annotation is used as the class type. Example: The following declaration: .. code-block:: python @dataclass class SomeConfig: ... @command("command_name") def some_command(structured_config: SomeConfig, dict_config: dict): ... is equivalent to: .. code-block:: python @dataclass class SomeConfig: ... def some_command(structured_cfg: SomeConfig, dict_cfg: dict): ... coma.register( "command_name", some_command, structured_cfg=SomeConfig, dict_cfg={} ) The advantage of this decorator is to remove the boilerplate of registering a command when its registration is a one-to-one mapping to the command signature. As such, this decorator acts as a convenience wrapper for :obj:`coma.register()`. It works in simple use cases. It does not work if the decorated object's signature contains non-config parameters (which is a rare and advanced use case). It also doesn't work with :func:`~coma.core.forget.forget` (a more common, if still slightly advanced use case). For such advanced uses cases, an explicit call to :obj:`coma.register()` must be made instead. Args: name (str): Passed directly to :func:`~coma.core.register.register`. parser_hook (typing.Callable): See :func:`~coma.core.register.register`. pre_config_hook (typing.Callable): See :func:`~coma.core.register.register`. config_hook (typing.Callable): See :func:`~coma.core.register.register`. post_config_hook (typing.Callable): See :func:`~coma.core.register.register`. pre_init_hook (typing.Callable): See :func:`~coma.core.register.register`. init_hook (typing.Callable): See :func:`~coma.core.register.register`. post_init_hook (typing.Callable): See :func:`~coma.core.register.register`. pre_run_hook (typing.Callable): See :func:`~coma.core.register.register`. run_hook (typing.Callable): See :func:`~coma.core.register.register`. post_run_hook (typing.Callable): See :func:`~coma.core.register.register`. parser_kwargs (typing.Dict[str, typing.Any]): See :func:`~coma.core.register.register`. See also: * :func:`~coma.core.forget.forget` * :func:`~coma.core.register.register` """ def decorator(command_: Callable): id_configs = {} fn = command_.__init__ if isinstance(command_, type) else command_ for i, p in enumerate(signature(fn).parameters.values()): if i == 0 and isinstance(command_, type): # Skip 'self' argument if command is a class. continue # If the annotation is list, List, dict, or Dict, convert it to an object # of the same type. Otherwise, pass the type directly to OmegaConf.create(). if p.annotation is list or get_origin(p.annotation) is list: id_configs[p.name] = [] elif p.annotation is dict or get_origin(p.annotation) is dict: id_configs[p.name] = {} else: id_configs[p.name] = p.annotation store_registration( lambda: register( name, command_, parser_hook=parser_hook, pre_config_hook=pre_config_hook, config_hook=config_hook, post_config_hook=post_config_hook, pre_init_hook=pre_init_hook, init_hook=init_hook, post_init_hook=post_init_hook, pre_run_hook=pre_run_hook, run_hook=run_hook, post_run_hook=post_run_hook, parser_kwargs=parser_kwargs, **id_configs, ) ) return command_ return decorator