Arguments¶
Similar to commands, use of cappa-specific argument annotations are “only” required when the default behavior is not what you want. A completely un-annotated dataclass like
@dataclass
class Example:
name: str
is perfectly valid. However, a CLI of any real complexity will need to use
the Arg
annotation.
By default:
Each class member is inferred to be a positional argument
Its name is the dash-case version of the class member name
Its value is coerced to the annotated type
Its help text is inferred from various natural sources (See Help Text Inference for details)
However, by using Arg
, with syntax like
name: Annotated[str, cappa.Arg(...)]
, you can customize the behavior of the
field.
short=True
(equivalent toshort='-n'
for this example) turns the option from a positional argument into a flag.long
works the same way as short (and can be used together), but for--
flags, like--name
.count=True
counts the number of flag instance. Frequently used for-v
,-vv
,-vvv
verbosity handling.help='...'
allows customizing the help text outside the docstring.parse=<callable>
allows parsing of more complex input values the cannot be handled through the type annotation system directly.
Note
See the annotation docs for more details on how annotations can be used and are interpreted to handle different kinds of CLI input.
- class cappa.Arg
Describe a CLI argument.
- Parameters:
value_name – Placeholder for the argument’s value in the help message / usage. Defaults to the name of the corresponding class field.
short – If True, uses first letter of the name to infer a (ex. -s) short flag. If a string is supplied, that will be used instead. If a string is supplied, it is split on ‘/’ (forward slash), to support multiple options. Additionally accepts a list of strings.
long – If True, uses first letter of the name to infer a (ex. –long) long flag. If a string is supplied, that will be used instead. If a string is supplied, it is split on ‘/’ (forward slash), to support multiple options. Additionally accepts a list of strings.
count – If True the resultant argmuent will count instances and accept zero arguments.
default – An explicit default CLI value. When left unspecified, the default is inferred from the class’ default or the adapter default/default_factory.
help – By default, the help text will be inferred from the containing class’ arguments’ section, if it exists. Alternatively, you can directly supply the help text here.
parse – An optional function which accepts the raw string argument as input and returns a parsed value type’s instance. This should only be required for complex types that the type system’s built-in parsing cannot handle.
group – Optional group names for the argument. This affects how they’re displayed in the backended help text.
hidden – Whether the argument should be hidden in help text. Defaults to False.
action – Generally automatically inferred from the data type. This allows to override the default.
num_args – Generally automatically inferred from the data type. This allows to override the default.
choices – Generally automatically inferred from the data type. This allows to override the default.
completion – Used to provide custom completions. If specified, should be a function which accepts a partial string value and returns a list of [cappa.Completion](cappa.Completion) objects.
required – Defaults to automatically inferring requiredness, based on whether the class’s value has a default. By setting this, you can force a particular value.
field_name – The name of the class field to populate with this arg. In most usecases, this field should be left unspecified and automatically inferred.
deprecated – If supplied, the argument will be marked as deprecated. If given True, a default message will be generated, otherwise a supplied string will be used as the deprecation message.
- action
- annotations
- choices
- classmethod collect(field, type_hint, fallback_help=None, default_short=False, default_long=False)
- completion
- count = False
- default
- deprecated = False
- field_name
- group
- help
- hidden = False
- long = False
- normalize(annotation=NoneType, fallback_help=None, action=None, default=missing, field_name=None, default_short=False, default_long=False)
- num_args
- parse
- required
- short = False
- value_name
Action¶
Obliquely referenced through other Arg
options like count
, every Arg
has a
corrresponding “action”. The action is automatically inferred, most of the time,
based on other options (i.e. count
), the annotated type (i.e. bool
->
ArgAction.set_true/ArgAction.set_false
, list
-> ArgAction.append
), etc.
However the inferred action can always be directly set, in order to override the default inferred behavior.
Custom Actions¶
Note
This feature is currently experimental, in particular because the parser state available to either backend’s callable is radically different. However, for an action callable which accepts no arguments, behaviors is unlikely to change.
In addition to one of the literal ArgAction
variants, the provided action can
be given as an arbitrary callable.
The callable will be called as the parser “action” in response to parsing that argument.
Note
Custom actions may interfere with general inference features, depending on what you’re doing (given that you’re taking over the parser’s duty of determining how the code ought to handle the argument).
As such, you may need to specify options like num_args
, where you wouldn’t have otherwise
needed to.
Similarly to the [invoke][./invoke.md] system, you can use the type system to automatically inject objects of supported types from the parse context into the function in question. The return value of the function will be used as the result of parsing that particular argument.
The set of available objects to inject include:
Command: The command currently being parsed (a relevant piece of context when using subcommands)
Arg: The argument being parsed.
Value: The raw input value parsed in the context of the argument. Depending on other settings, this may be a list (when num_args > 1), or a raw value otherwise.
RawOption: In the event the value in question corresponds to an option value, the representation of that option from the original input.
The above set of objects is of potentially limited value. More parser state will likely be exposed through this interface in the future.
For example:
def example():
return 'foo!'
def example(arg: Arg):
return 'foo!'
def example2(value: Value):
return value.value[1:]
@dataclass
class Example:
ex1: Annotated[str, cappa.Arg(action=example)
ex2: Annotated[str, cappa.Arg(action=example2)
ex3: Annotated[str, cappa.Arg(action=example3)
Warning
ArgAction.append
and num_args=-1
can be potentially confused, in that they can both
produce list/sequence types as outputs. They can be used in conjunction or separately,
but they are not the same!
Given some example option --foo
:
ArgAction
generally, affects the way the parser maps input CLI values to a given field
overall. As such ArgAction.append
causes the field-level value to be a sequence, and
for multiple instances of --foo
to accumulate into a list. e.x. --foo 1 --foo 2
-> [1, 2]
.
num_args
instead, affects the number of values that a single instance of --foo
will
consume before stopping. e.x. --foo 1 2
-> [1, 2]
.
From an input perspective, the above examples show how they present differently at the CLI interface, requiring different inputs to successfully parse. From an output perspective, the table below shows how their parsed output will end up looking when mapped to real values.
action |
num_args |
result |
---|---|---|
set |
1 |
1 |
set |
-1 |
[1] |
append |
1 |
[1] |
append |
-1 |
[[1]] |
Num Args¶
num_args
controls the number of arguments that the parser will consume in
order to fullfill a specific field. num_args=1
is the default behavior,
meaning only one argument/value will be consumed for that field. This yields a
scalar-type value.
num_args=3
would therefore mean that exactly 3 values would be required,
resulting in a sequence-type output, rather than a scalar one.
num_args=-1
can be used to indicate that 0 or more (i.e. unbounded number of)
arguments will be consumed, similarly resulting in a sequence-type output (even
in the zero case).
Note
Generally num_args
will be automatically inferred by your type annotation. For example,
tuple[int, int, int]
implies num_args=3
.
However, an explicitly provided value is always preferred to the inferred value. See annotations for more details.
Default¶
Controls the default argument value at the CLI level. Generally, you can avoid
direct use of cappa’s default by simply using the source class’ native default
mechanism. (i.e. foo: int = 0
or foo: int = field(default=0)
for
dataclasses).
However it can be convenient to use cappa’s default because it does not affect the optionality of the field in question in the resultant class constructor.
Environment Variable Fallback¶
You can also use the default field to supply supported kinds of default-value-getting behaviors.
Env
is one such example, where with
Arg(..., default=Env("FOO", default='default value'))
, cappa will attempt to
look up the environment variable FOO
for the default value, if there was no
supplied value at the CLI level.
- class cappa.Env(env_var, *env_vars, default=None)
Lazily evaluates environment variables in the order given.
Argument value interpolation will handle and Arg’s default being an Env instance, by evaluating the Env, in the event the parser-level value fell back to the default.
Examples
>>> from cappa import Arg, Env >>> arg = Arg(default=Env("HOST", "HOSTNAME", default="localhost"))
- default
- env_vars
- evaluate()
- Return type:
str | None