This library simplifies the startup process of a more complicated python project with multiple subcommands, a complicated configuration, aswell as certain targets that need to be run or initialized before starting commands in a specific order.
The point of this library is to easily provide a way to setup a commandline interface using
logging
and argparse
and contextvars
that looks like this:
my-programm -vvv -c <config_file.py> my-custom-command --my-custom-options
While providing a default way of starting the programm you might implemented way. Just have a look
at ackermann.units
. The main purpose of this library is just to provide an extentable way to
implement commands and startup targets.
The most simple setup is just to call ackermann.run
in your __main__
file, like so:
from ackermann import run
from ackermann.units import run_command
run(targets=[run_command])
Now if you execute your module with python -m <module>
you can specify a config with -c
change the verbosity level with -v
and specify a subcommand.
If you want to write your own command you can do so:
from ackermann import Command, Config
from argparse import ArgumentParser
from time import sleep
class MyCommand(Command):
name = "my-command"
@classmethod
def get_arguments(cls, parser: ArgumentParser):
parser.add_argument("-s", "--skip", action="store_true")
def run(self, config: Config):
if config["ARGS"].skip:
print("You wanted to skip")
else:
print("We are doing it as you are not skipping")
sleep(1000)
If you run your program as suggested you are able to run this command with:
python -m <project> my-command -s
The same works for async
commands aswell:
from ackermann import Command, Config
from argparse import ArgumentParser
from asyncio import sleep
class MyAsyncCommand(Command):
name = "my-async-command"
is_async = True
@classmethod
def get_arguments(cls, parser: ArgumentParser):
parser.add_argument("-s", "--skip", action="store_true")
async def async_run(self, config: Config):
if config["ARGS"].skip:
print("You wanted to skip")
else:
print("We are doing it as you are not skipping")
await sleep(1000)
running this command would look like this:
python -m <project> my-async-command -s
More complicated commands might want to reuse certain parts of code to initialize their project in
the correct way. For this ackermann
has ConfigUnit
s. These work pretty similiar to context
managers around your command.
A unit might be defined like this:
from ackermann import Config, config_unit
@config_unit
async def do_sth(config: Config):
print("I want to do sth before my command starts")
yield
print("Now I need to cleanup as the command finished")
This unit will not run by default, but it must be explicitly stated in the command. Using the
targets
variable. Config units might depend on each other, might conflict with one another or must
be run in a certain order.
They might also force the programm to be run in an async context or on multiple processors.
If your programm needs to signal certain parts of code about it's state you might also add signals.
To your config by default before executing a command ackermann will signal ready
and after
exiting a command it will signal stopping
. This might be used to signal systemd in case of using
a notify service.
All commands, units and signals are called with the current instance of Config
which itself
stores configuration variables that might be stored in a python module supplied with the -c
flag.
If you want to explicitly tell the programm about these variables you can use the small wrapper
ackermann.ConfigVar
around contextvars.ContextVar
which allows you to specify the format,
type, and default value for a config variable.
These variables might then be imported in your programm without worrying where the get the correct value like so:
from ackermann import ConfigVar
config_my_config_var = ConfigVar("MY_CONFIG_VAR", description="Does something", type=int, default=0)
# At some point in the code
def do_sth():
my_config_var_value = config_my_config_var.get()
One benefit of using this interface is that you can easily check the variables set when starting the
programm with the -V
flag.