Initiate
The first step to using coma
is always to initiate()
a new coma.
initiate()
is always called exactly once, before any
calls to register()
. Calling
initiate()
explicitly is required only to initiate a
coma with non-default parameters (coma
implicitly calls
initiate()
with default parameters otherwise).
argparse
Overrides
By default, coma
creates an ArgumentParser
with default parameters. However, initiate()
can
optionally accept a custom ArgumentParser
:
import argparse
import coma
if __name__ == "__main__":
coma.initiate(parser=argparse.ArgumentParser(description="My Program description."))
coma.register("greet", lambda: print("Hello World!"))
coma.wake()
Now, let’s run this program with the -h
flag to see the result:
$ python main.py -h
usage: main.py [-h] {greet} ...
My Program description.
positional arguments:
{greet}
optional arguments:
-h, --help show this help message and exit
You can also provide keyword arguments to override the default parameter values
to the internal ArgumentParser.add_subparsers()
call through the subparsers_kwargs
parameter to
initiate()
:
coma.initiate(subparsers_kwargs=dict(help="sub-command help"))
Global Configs
Configs can be initiate()
d globally to all
commands or register()
ed locally to a specific command.
Let’s revisit the second of the Multiple Configurations examples from the introductory tutorial to see the difference:
from dataclasses import dataclass
import coma
@dataclass
class Greeting:
message: str = "Hello"
@dataclass
class Receiver:
entity: str = "World!"
if __name__ == "__main__":
coma.register("greet", lambda g, r: print(g.message, r.entity), Greeting, Receiver)
coma.register("leave", lambda r: print("Goodbye", r.entity), Receiver)
coma.wake()
Notice how, in the original example, the Receiver
config is
register()
ed (locally) to both commands. Instead, we
can initiate()
a coma with this config so that it is
(globally) supplied to all commands:
from dataclasses import dataclass
import coma
@dataclass
class Greeting:
message: str = "Hello"
@dataclass
class Receiver:
entity: str = "World!"
if __name__ == "__main__":
coma.initiate(Receiver)
coma.register("greet", lambda r, g: print(g.message, r.entity), Greeting)
coma.register("leave", lambda r: print("Goodbye", r.entity))
coma.wake()
This produces the same overall effect, while being more DRY.
Note
Configs need to be uniquely identified per-command, but not across commands.
Note
Each command parameter will be bound (in the given order) to the supplied
config objects if the command is invoked. In this example, because
Receiver
is now supplied first instead of second to greet
, the
order of parameters to greet
had to be swapped: g, r
becomes
r, g
. See below for a nexample of how to prevent this.
Global Hooks
coma
’s behavior can be easily tweaked, replaced, or extended using hooks.
These are covered in great detail in their own tutorial.
Here, the emphasis is on the difference between global and local hooks: As with
configs, hooks can be initiate()
d globally to affect
coma
’s behavior towards all commands or register()
ed
locally to only affect coma
’s behavior towards a specific command.
Let’s revisit the previous example. Recall that the order
of parameters to greet
had to be swapped: g, r
became r, g
.
Suppose we want to prevent this change. To do so, we can force coma
to bind
configs to parameters differently by writing a custom init_hook
:
from dataclasses import dataclass
import coma
@dataclass
class Greeting:
message: str = "Hello"
@dataclass
class Receiver:
entity: str = "World!"
@coma.hooks.hook
def custom_init_hook(command, configs):
return command(*reversed(list(configs.values())))
if __name__ == "__main__":
coma.initiate(Receiver, init_hook=custom_init_hook)
coma.register("greet", lambda g, r: print(g.message, r.entity), Greeting)
coma.register("leave", lambda r: print("Goodbye", r.entity))
coma.wake()
The details of how the hook is defined aren’t important for the moment. The
point is that coma
’s default behavior regarding config binding has been
replaced from positional matching to anti-positional matching, which is
sufficient in this simple example.