Command
face.Command is the central type in face. It wraps a
Parser with handler dispatch, middleware, help generation,
and dependency injection. Build a CLI by creating a Command, adding flags
and subcommands, then calling run().
Construction
The minimal Command takes a handler function:
from face import Command
def greet():
print('hello')
cmd = Command(greet)
cmd.run()
name defaults to the function name (greet above). doc defaults
to the first line of the function’s docstring. Override either explicitly:
def greet():
"""Say hello to the world.
This longer description is ignored by face.
"""
print('hello')
cmd = Command(greet, name='hi', doc='A greeting command')
Pass flags, positional arg specs, and middlewares at construction time:
from face import Command, Flag
def serve(host, port):
print(f'Serving on {host}:{port}')
cmd = Command(
serve,
name='serve',
flags=[
Flag('--host', missing='localhost'),
Flag('--port', parse_as=int, missing=8080),
],
)
See Positional arguments below and Middleware for those constructor parameters.
Adding flags
The most common way to add flags is through add()
with a flag string as the first argument:
cmd = Command(handler)
cmd.add('--verbose', parse_as=True, doc='enable verbose output')
cmd.add('--output', char='-o', missing='out.txt', doc='output file path')
cmd.add('--count', parse_as=int, missing=1, doc='repetition count')
parse_as controls how the flag’s argument is parsed:
parse_as=str(default): flag takes one string argument.parse_as=int,parse_as=float, or any callable: flag takes one argument, converted by the callable.parse_as=True(or any non-callable): flag takes no argument. When present, the flag yields that value.
missing controls the value when the flag is absent:
missing=None(default): flag is optional,Nonewhen absent.missing=ERROR: flag is required. Parse fails if absent.missing=<value>: flag is optional, uses this default when absent.
multi controls behavior when a flag appears more than once:
multi='error'(default): raisesDuplicateFlag.multi='extend'ormulti=True: collects all values into a list.multi='override': last value wins.
char sets a short alias (e.g., char='-v').
Required flags
Use face.ERROR as the missing value:
from face import Command, ERROR
def deploy(target):
print(f'deploying to {target}')
cmd = Command(deploy)
cmd.add('--target', missing=ERROR, doc='deploy target (required)')
ListParam and ChoicesParam
face.ListParam parses comma-separated (or other delimiter) values
into a list. face.ChoicesParam restricts input to a fixed set.
from face import Command, ListParam, ChoicesParam
def tag(tags, env):
print(f'tagging {env} with {tags}')
cmd = Command(tag)
cmd.add('--tags', parse_as=ListParam(str), doc='comma-separated tags')
cmd.add('--env', parse_as=ChoicesParam(['dev', 'staging', 'prod']),
missing='dev', doc='target environment')
Adding subcommands
Pass a callable to add() to create a subcommand:
from face import Command, echo
def add(posargs_):
echo(str(sum(int(x) for x in posargs_)))
def mul(posargs_):
result = 1
for x in posargs_:
result *= int(x)
echo(str(result))
def main():
cmd = Command(None, name='calc')
cmd.add(add, posargs=True)
cmd.add(mul, posargs=True)
cmd.run()
name defaults to the function name. Override with name='sub-name'.
You can also add a pre-built Command instance:
sub = Command(handler, name='sub')
sub.add('--flag', parse_as=True)
root = Command(None, name='app')
root.add(sub)
Subcommands nest arbitrarily. A subcommand is itself a Command, so it can have its own subcommands, flags, and middleware.
Subcommand groups
For commands with many subcommands, organize them under named headings
in help output using CommandGroup:
from face import Command, CommandGroup
cmd = Command(None, name='myapp')
# Ungrouped subcommands appear first in help
cmd.add(version)
# Grouped subcommands appear under headings
users = CommandGroup('Users')
users.add(create_user)
users.add(delete_user)
cmd.add(users)
admin = CommandGroup('Admin')
admin.add(list_logs)
cmd.add(admin)
This produces help output like:
Subcommands:
version show the version
Users:
create-user create a new user
delete-user remove a user
Admin:
list-logs display application logs
A group keyword argument can also be passed directly to
add() for convenience:
cmd.add(create_user, group='Users')
Positional arguments
Control positional argument handling with the posargs parameter:
posargs=True: accept any number of string positional args.posargs=Falseor omitted: no positional args allowed.posargs=int: accept exactly that many positional args.posargs=str: sets the display name and provides name.posargs=callable: accept any number, each parsed with the callable.posargs=dict: keyword arguments forwarded toPosArgSpec.posargs=PosArgSpec(...): full control over parsing and display.
Post-positional arguments (after --) are controlled by
post_posargs, which accepts the same forms.
Adding middleware
Add middleware decorated with face.face_middleware():
from face import Command, face_middleware
@face_middleware(provides=['db'])
def provide_db(next_):
db = connect_db()
try:
next_(db=db)
finally:
db.close()
cmd = Command(handler)
cmd.add(provide_db)
Both cmd.add(mw) and cmd.add_middleware(mw) work.
add_middleware also accepts undecorated callables (it wraps them
automatically).
Middleware flags are automatically added to any Command that uses the middleware. See Middleware for full details.
Running
Call run() to parse sys.argv and dispatch:
cmd.run()
Pass explicit arguments for testing or embedding:
cmd.run(argv=['myapp', '--verbose', 'subcommand', 'arg1'])
run() parses arguments, resolves the subcommand path, builds the
middleware chain, and calls the handler with injected dependencies.
For pre-validation of all subcommand paths without executing, call
prepare():
cmd.prepare() # raises if any subcommand has unmet dependencies
run() only validates the specific subcommand invoked. Call
prepare() after all flags, subcommands, and middlewares are added to
catch configuration errors early.
Dependency injection
Handler functions and middleware receive arguments by parameter name. Face inspects the function signature and injects matching values automatically. There are two categories of injectables: flags and builtins.
Flag values are injected by the flag’s name attribute (derived from
the flag string, e.g., --output-file becomes output_file).
Builtin injectables are always available:
args_The
CommandParseResultinstance containing all parsed data.cmd_String of the command name (
argv[0]).subcmds_Tuple of subcommand name strings for the matched path.
flags_OrderedDictmapping flag name to parsed value.posargs_Tuple of positional argument strings.
post_posargs_Tuple of post-positional argument strings (after
--).command_The root
Commandinstance.subcommand_The specific subparser (
Parser) for the matched subcommand path. Same ascommand_when no subcommand is used.
A handler can request any combination of these:
from face import Command, echo
def status(verbose, flags_, subcmds_):
echo(f'subcommands: {subcmds_}')
echo(f'verbose: {verbose}')
echo(f'all flags: {dict(flags_)}')
cmd = Command(status)
cmd.add('--verbose', parse_as=True)
Parameters not matching any flag or builtin name cause a
NameError at prepare() or run() time.
Error handling
face.CommandLineError is raised on parse failures (bad flags,
missing required flags, invalid subcommands). It is a subclass of both
FaceException and SystemExit, so uncaught it exits
the process with a nonzero status code.
face.UsageError is for handler-level validation errors. Raise it
from your handler or middleware to signal incorrect usage with a message:
from face import Command, UsageError
def deploy(target):
if '/' in target:
raise UsageError('--target must not contain slashes')
print(f'deploying to {target}')
cmd = Command(deploy)
cmd.add('--target', missing='prod')
UsageError is also a SystemExit subclass, so it exits cleanly
when uncaught.
API reference
- class face.Command(func: ~typing.Callable | None, name: str | None = None, doc: str | None = None, *, flags: ~typing.List[~face.parser.Flag] | None = None, posargs: bool | ~face.parser.PosArgSpec | None = None, post_posargs: bool | None = None, flagfile: bool = True, help: bool | ~face.helpers.HelpHandler = <face.helpers.HelpHandler object>, middlewares: ~typing.List[~typing.Callable] | None = None, group: str | None = None)[source]
The central type in the face framework. Instantiate a Command, populate it with flags and subcommands, and then call command.run() to execute your CLI.
- Parameters:
func – The function called when this command is run with an argv that contains no subcommands.
name – The name of this command, used when this command is included as a subcommand. (Defaults to name of function)
doc – A description or message that appears in various help outputs.
flags – A list of Flag instances to initialize the Command with. Flags can always be added later with the .add() method.
posargs – Pass True if the command takes positional arguments. Defaults to False. Can also pass a PosArgSpec instance.
post_posargs – Pass True if the command takes additional positional arguments after a conventional ‘–’ specifier.
help – Pass False to disable the automatically added –help flag. Defaults to True. Also accepts a HelpHandler instance.
middlewares – A list of @face_middleware decorated callables which participate in dispatch.
group – An optional string group name for display in help output. See CommandGroup for the recommended way to group multiple subcommands.
- add(*a, **kw)[source]
Add a flag, subcommand, or middleware to this Command.
If the first argument is a callable, this method contructs a Command from it and the remaining arguments, all of which are optional. See the Command docs for for full details on names and defaults.
If the first argument is a string, this method constructs a Flag from that flag string and the rest of the method arguments, all of which are optional. See the Flag docs for more options.
If the argument is already an instance of Flag or Command, an exception is only raised on conflicting subcommands and flags. See add_command for details.
Middleware is only added if it is already decorated with @face_middleware. Use .add_middleware() for automatic wrapping of callables.
- add_command(subcmd)[source]
Add a Command, and all of its subcommands, as a subcommand of this Command.
Middleware from the current command is layered on top of the subcommand’s. An exception may be raised if there are conflicting middlewares or subcommand names.
- add_command_group(group)[source]
Add all commands from a CommandGroup as direct subcommands of this Command, tagged with the group’s name for organized help display.
No new subcommand path level is created — the group’s commands become siblings of any existing subcommands.
- add_middleware(mw)[source]
Add a single middleware to this command. Outermost middleware should be added first. Remember: first added, first called.
- get_dep_names(path=())[source]
Get a list of the names of all required arguments of a command (and any associated middleware).
By specifying path, the same can be done for any subcommand.
- get_flag_map(path=(), with_hidden=True)[source]
Command’s get_flag_map differs from Parser’s in that it filters the flag map to just the flags used by the endpoint at the associated subcommand path.
- prepare(paths=None)[source]
Compile and validate one or more subcommands to ensure all dependencies are met. Call this once all flags, subcommands, and middlewares have been added (using .add()).
This method is automatically called by .run() method, but it only does so for the specific subcommand being invoked. More conscientious users may want to call this method with no arguments to validate that all subcommands are ready for execution.
- run(argv=None, extras=None, print_error=None)[source]
Parses arguments and dispatches to the appropriate subcommand handler. If there is a parse error due to invalid user input, an error is printed and a CommandLineError is raised. If not caught, a CommandLineError will exit the process, typically with status code 1. Also handles dispatching to the appropriate HelpHandler, if configured.
Defaults to handling the arguments on the command line (
sys.argv), but can also be explicitly passed arguments via the argv parameter.- Parameters:
argv (list) – A sequence of strings representing the command-line arguments. Defaults to
sys.argv.extras (dict) – A map of additional arguments to be made available to the subcommand’s handler function.
print_error (callable) – The function that formats/prints error messages before program exit on CLI errors.
Command Exception Types
In addition to all the Parser-layer exceptions, a command or user endpoint function can raise:
- class face.CommandLineError(msg, code=1)[source]
A
FaceExceptionandSystemExitsubtype that enables safely catching runtime errors that would otherwise cause the process to exit.If instances of this exception are left uncaught, they will exit the process.
If raised from a
run()call andprint_erroris True, face will print the error before reraising. Seeface.Command.run()for more details.
- class face.UsageError(msg, code=1)[source]
Application developers should raise this
CommandLineErrorsubtype to indicate to users that the application user has used the command incorrectly.Instead of printing an ugly stack trace, Face will print a readable error message of your choosing, then exit with a nonzero exit code.