Command

The @command decorator is a lightweight wrapper around register(), which acts as a convenience for simple use cases.

The advantage of this decorator is to remove the boilerplate of register()ing a command when its registration is a one-to-one mapping to the command signature. For example, consider the following multi-config registration:

main.py
from dataclasses import dataclass

import coma

@dataclass
class Config1:
    ...

@dataclass
class Config2:
    ...

def greet(cfg_1: Config1, cfg_2: Config2, cfg_3: dict):
    print("Hello World!")
    print(cfg_1)
    print(cfg_2)
    print(cfg_3)

if __name__ == "__main__":
    coma.register("greet", greet, cfg_1=Config1, cfg_2=Config2, cfg_3={})
    coma.wake()

Notice that the function signature of greet is a one-to-one mapping to the registration. Specifically, each parameter of the function signature is a config, and the parameter name and type annotation of each config in the function signature matches a config identifier and type pair in the registration. As such, we can use the @command decorator to remove the boilerplate registration:

main.py
from dataclasses import dataclass

from coma import command
import coma

@dataclass
class Config1:
    ...

@dataclass
class Config2:
    ...

@command("greet")
def greet(cfg_1: Config1, cfg_2: Config2, cfg_3: dict):
    print("Hello World!")
    print(cfg_1)
    print(cfg_2)
    print(cfg_3)

if __name__ == "__main__":
    # Removed call to coma.register()
    coma.wake()

This decorator 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 does not work with forget() (a more common, if still slightly advanced use case). For such advanced uses cases, an explicit call to register() must be made instead.

Note

When the command is defined as a class instead of a function, it is the signature of the class’s __init__() method that must match the registration format.

Just like register(), the @command decorator accepts local hooks and parser keywords arguments. These are passed directly to register() without modification or processing:

main.py
from dataclasses import dataclass

from coma import command
import coma

@command(
    "greet",
    parser_hook=...,
    pre_config_hook=...,
    config_hook=...,
    post_config_hook=...,
    pre_init_hook=...,
    init_hook=...,
    post_init_hook=...,
    pre_run_hook=...,
    run_hook=...,
    post_run_hook=...,
    parser_kwargs=...,
)
def greet():
    print("Hello World!")

if __name__ == "__main__":
    coma.wake()

Warning

Be aware that, under the hood, the @command decorator delays the call to register() until after the call to initiate(). This should not cause issues, but may lead to unintended side effects if mucking around with Python’s inspect module on the Coma singleton object (which you should not do unless you really know what you are doing).