Command-line apps
pip install austial # `austial.commander` ships in austial-core, which `austial` installsaustial.commander turns providers into CLI commands that share your application’s dependency injection, configuration and
lifecycle hooks (Nest Commander’s counterpart). Use it for one-off scripts, maintenance tasks and seeding.
from austial import Injectable, Module
from austial.commander import Command, CommandFactory, CommandRunner, Option
@Command(name="greet", description="Say hello", arguments="<name> [title]", aliases=["hi"])
@Injectable()
class GreetCommand(CommandRunner):
def __init__(self, greeter: GreeterService):
self.greeter = greeter
async def run(self, passed_params: list[str], options: dict) -> None:
print(self.greeter.greet(*passed_params))
@Option(flags="-n, --number <count>", description="Repeat", default=1)
def parse_number(self, value: str) -> int:
return int(value)
@Module(providers=[GreeterService, GreetCommand])
class AppModule: ...
if __name__ == "__main__":
CommandFactory.main(AppModule, name="mytool") # exits with the command's exit codepython cli.py greet Ada Dr --number 2
python cli.py --helpCommands, arguments and options#
| Piece | Meaning |
|---|---|
@Command(name, description=, arguments=, aliases=, subcommands=, default=) | Marks a provider class as a command. default=True runs when no command is named. |
arguments="<file> [mode]" | Positionals: <x> required, [x] optional, a trailing ... (<files...>) takes the rest. They arrive as passed_params. |
@Option(flags=, description=, required=, default=, choices=, multiple=) | On a method; the method parses the value and its return value lands in options[name]. Flags use commander.js syntax: -s, --shout (boolean), --name <value> (required value), --name [value] (optional value). multiple=True makes the flag repeatable and the option a list. |
@SubCommand(name, ...) | A command listed in a parent’s subcommands=[...], giving tool db migrate. |
Commands are ordinary providers, so list them in a module’s providers. Module on_module_init / on_module_destroy hooks run around
the command.
Interactive prompts#
from austial.commander import Prompts
async def run(self, passed_params, options):
prompts = Prompts()
name = prompts.ask("Project name", default="app", validate=str.isidentifier)
tls = prompts.confirm("Enable TLS?", default=False)
database = prompts.choose("Database", ["postgres", "sqlite"], default="sqlite") # by number or name
token = prompts.password("API token") # hidden inputWhen stdin is not a terminal (CI, pipes) prompts return their defaults instead of blocking, and raise CommanderError when
there is no default (always for password). For tests, pass scripted answers:
Prompts(input_fn=lambda _prompt: next(answers), password_fn=lambda _prompt: "s3cret").
Exit codes#
CommandFactory.run(AppModule, argv, name=...) returns the code and main() passes it to sys.exit: 0 success, 2 usage error
(unknown command or option, missing argument), the exit_code of a CommanderError("message", exit_code=3) you raise, and 1 for any
other exception. Only warn and error log levels are on by default; pass logger=False to silence the framework entirely.