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. For each field of customization, there is a default inferred value, based
on the particulars of the dataclass and/or other configured fields.
Below we document when configuring a given field might imply some other field by default
(e.g. count=True implies num_args=0), however in all cases, an explicitly
provided value will take precedence over the default inference.
Note
See the annotation docs for more details on how annotations can be used and are interpreted to handle different kinds of CLI input.
Arg.value_name¶
This field controls the name of the field in helptext/errors. By default it matches the dataclass’ field name.
@dataclass
class Command:
foo: Annotated[int, cappa.Arg(value_name="bar")]
happy: Annotated[int, cappa.Arg(value_name="sad", long=True)] = 1
Would yield help text like:
Options
[--happy SAD]
Arguments
BAR
Arg.short¶
By default, unannotated dataclass fields are assumed to be positional arguments.
An Arg.short argument is a non-positional argument which can be selected by a
single - followed by the short name.
The value can be supplied in a few different ways:
foo: Annotated[int, Arg(short=True)]: Rendered as-f. If there are more than one field starting with “f” withshort=True, at least one will need to be disambiguated.foo: Annotated[int, Arg(short='-b')]: Rendered as-isfoo: Annotated[int, Arg(short='b')]: Rendered as-b.foo: Annotated[int, Arg(short=['-b', '-f'])]: Rendered as both/either-band-f.foo: Annotated[int, Arg(short='-b/-f')]: Rendered as both/either-band-f.
Arg.long¶
By default, unannotated dataclass fields are assumed to be positional arguments.
An Arg.long argument is a non-positional argument which can be selected by a
single -- followed by the long name.
Note
snake_case field names will be converted to dash-case by default.
The value can be supplied in a few different ways:
foo: Annotated[int, Arg(long=True)]: Rendered as--foo. field starting with “f” withlong=True, at least one will need to be disambiguated.foo: Annotated[int, Arg(long='--bar')]: Rendered as-isfoo: Annotated[int, Arg(long='bar')]: Rendered as--bar.foo: Annotated[int, Arg(long=['--bar', '-foo'])]: Rendered as both/either--barand--foo.foo: Annotated[int, Arg(long='--bar/--foo')]: Rendered as both/either--barand--foo.
Arg.count¶
Counts instances of the argument in question. This implies num_args=0 and action=ArgAction.count by default.
The canonical example of this is counting verbosity flags.
@dataclass
class Example:
verbose: Annotated[int, cappa.Arg(short="-v", count=True),
Arg.help¶
Controls the per-argument help text.
See Help for more details, but in short help text is inferred by default from a few different sources, including docstrings and “attribute docstrings”.
Arg.action¶
Obliquely referenced through other Arg options like count, every Arg has a
corresponding “action”. The action is automatically inferred, most of the time,
based on other options (i.e. count), the annotated type (i.e. bool ->
ArgAction.store_true/ArgAction.store_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, the behavior 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 in question).
As such, you may need to specify options like num_args, where you wouldn’t have otherwise
needed to.
Similarly to the invoke 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. If you think some specific bit of parser state is missing and could be useful to you, please raise an issue!
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]] |
Arg.num_args¶
num_args controls the number of arguments that the parser will consume in
order to fulfill 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.
Arg.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, foo: int = field(default=0), or
foo: list[str] = field(default_factory=list) for dataclasses).
However it can be convenient to use cappa’s default because it does not affect whether the underlying class definition makes that field required in the class’ constructor.
Note
The default value is not typically parsed by the given parse function.
That is to say, if no value is selected at the CLI and the default value is used
instead; it will not be coerced into the annotated type automatically.
(Env, Prompt, and Confirm are exceptions to this rule, explained in their
sections below).
The reason for this is twofold:
Typechecking should already emit an error when the given default is of the incorrect type.
This interacts poorly with types who’s constructor does not accept an instance of the given type as an input argument. For example,
foo: Foo = Foo('')would inferparse=Fooand attempt to passFoo(Foo(''))during parsing.
Additionally there are a number of natively integrated objects that can be used as default to create more complex behaviors given a missing CLI argument. The below objects will not be evaluated unless the user did not supply a value for the argument it’s attached to.
Default¶
All other types of default are ultimately shorthand forms of Default. The
Default construct allows for specifying an ordered chain of default fallback
options, with an optional static fallback item at the end.
For example:
foo: int = 4is the same asdefault=Default(default=4). This unconditionally defaults to 4.foo: Annotated[int, Arg(default=Env("FOO"))] = 4is the same asArg(default=Default(Env("FOO"), default=4). This attempts to read the environment variableFOO, and falls back to 4 if the env var is unset.foo: Annotated[int, Arg(default=Env("FOO") | Prompt("Gimme"))]is the same asArg(default=Default(Env("FOO"), Prompt("Gimme")). This attempts to read the environment variableFOO, followed by a user prompt if the env var is unset.
As shown above, any combination of defaultable values can be used as fallbacks of one another by using
the | operator to chain them together.
As noted above, a value produced by Default.default does not invoke the Arg.parse parser. This is
for similar reasons as to native dataclass defaults. The programmer is supplying the default value
which should not need to be parsed.
Note
Default’s fallback behavior will fallback to lower priority handlers in the “default sequence” when
that item’s default returns cappa.Empty. All the native (Env, Prompt, Confirm)
handlers will do this natively, when they fail to produce a value.
A ValueFrom (described below) accepts a user-provided function, so it may need to be aware of
this and return cappa.Empty, depending on the desired behavior.
Env¶
cappa.Env performs environment variable lookups in an attempt to provide a value to the class field.
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.
As noted above, a value produced by Env does invoke the Arg.parse parser. This is
because Env values will always be returned as a string, very similarly to a normal pre-parse
CLI value.
Prompt/Confirm¶
cappa.Prompt and cappa.Confirm can be used to ask for user input to fulfill the value.
Note
rich.prompt.Prompt and rich.prompt.Confirm can also be used transparently for the same purpose.
import cappa
class Example:
value: Annotated[int, cappa.Arg(default=cappa.Prompt("A number value"))]
is_ok: Annotated[bool, cappa.Arg(default=cappa.Confirm("You sure?"))]
As noted above, a value produced by Prompt/Confirm does invoke the Arg.parse parser. This is
because these values will always be returned as a string, very similarly to a normal pre-parse
CLI value.
ValueFrom¶
cappa.ValueFrom is a means for calling an arbitrary function at mapping time, to allow for dynamic default values.
Note
A dataclass field(default_factory=list) is internally the same thing as default=ValueFrom(list)!
from pathlib import Path
import cappa
def load_default(key):
config = json.loads(Path("config.json").read_text())
return config[key]
class Example:
value: Annotated[int, cappa.Arg(default=cappa.ValueFrom(load_default, key='value'))]
This construct is able to be automatically supplied with cappa.State, in the event shared parse state is required to evaluate a field’s default.
As noted above, a value produced by ValueFrom does not invoke the Arg.parse parser. This is
because the called function is programmer-supplied and can/should just return the correct end
value.
Note
If there are scenarios where ValueFrom should fail to provide a value and fall back to the
class-level, or Default-level default value, it should return cappa.Empty in order to indicate
to cappa that it shouldn’t accept the returned value as the actual value to used when constructing
the resulting class instance.
Arg.show_default¶
Defaults to True (e.g. DefaultFormatter(format='{default}', show=True)). This field controls both:
Whether to render the argument’s default for helptext
The formatting for the default value itself.
show_default can be provided as any of:
bool: Implies theDefaultFormatter.showvalue (e.g.DefaultFormatter(show=<the bool>)).str: Implies theDefaultFormatter.formatvalue (e.g.DefaultFormatter(format=<the str>, show=True)).DefaultFormatter: Is taken as-is.
DefaultFormatter.show enables/disables the display of the given Arg.default. While
the literal default value is formatted through DefaultFormatter.format.
Note
The str.format context is provided as named default format arg. That is to say, the literal
default value can be templated into the string through the named {default} syntax.
Normal format specifiers can be used to control formatting of the value, for example {default:.2f}
to limit the precision of a default float value.
Note
The format value can simply be a static string, to avoid taking the literal default value. An
reasonable example of this might be source: Annotated[BinaryIO, Arg(show_default='<stdin>')] = '-'.
Arg.group: Groups (and Mutual Exclusion)¶
Arg(group=...) can be used to customize the way arguments/options are grouped
together and interpreted.
The group argument can be any of:
str: A string that indicates a custom group. This only affects help text output, by placing anyArgs with that group string under a common heading.class Example: arg1: Annotated[str, Arg(group='Special')] arg2: Annotated[str, Arg(group='Special')]
tuple[int, str]: This additionally provides anintwhich is considered when determining theorderof group output.class Example: arg1: Annotated[str, Arg(group=(4, 'Special'))] arg2: Annotated[str, Arg(group=(4, 'Special'))]
Group: Instances of
Groupare the normalized form of the above two shorthand options (which are ultimately coerced intoGroups).Group additionally has an
exclusive: booloption, which can be used to indicate that options within a group are mutually exclusive to one other.Note
Both
orderandexclusiveoptions onGroupare expected (and validated) to be identical across all options sharing that group string.class Example: arg1: Annotated[str, Arg(group=Group('Special', exclusive=True))] arg2: Annotated[str, Arg(group=Group('Special', exclusive=True))]
In this case,
example --arg1 foo --arg2would generate an error like:Argument 'arg1' is not allowed with argument 'arg2'
Dedicated Mutual Exclusion Syntax¶
A potentially common use of mutually exclusive arguments would be two distinct CLI-level arguments, which ultimately are different ways of configuring a specific code-level field.
In such cases, two (or more) cappa.Args can be annotated on a single class field.
This implies a mutually exclusive group automatically, defaulting to the name
of the field (“Verbose” in this case) as the group name. For example:
@dataclass
class Example:
verbose: Annotated[
int,
cappa.Arg(short="-v", action=cappa.ArgAction.count),
cappa.Arg(long="--verbosity"),
] = 0
example -vvvwould yield:verbose=3example --verbosity 4would yield:verbose=4example -vvv --verbosity 4would yield the error:Argument '--verbosity' is not allowed with argument '-v'
Note
An explicit group= can still be used in concert with the above syntax to control
the order and name of the resultant group.
Arg.parse¶
Arg.parse can be used to provide specific instruction to cappa as to how to
handle the raw value given from the CLI parser backend.
In general, this argument shouldn’t need to be specified because the annotated type will generally imply how that particular value ought to be parsed, especially for built in python types.
However, there will inevitably be cases where the type itself is not enough to infer the specific parsing required. Take for example:
from datetime import date
@dataclass
class Example:
iso_date: date
american_date: Annotated[date, cappa.Arg(parse=lambda date_str: date.strptime('%d/%m/%y'))]
Cappa’s default date parsing assumes an input isoformat string. However you might instead
want a specific alternate parsing behavior; and parse= is how that is achieved.
Further, this is likely more useful for parsing any custom classes which dont have simple, single-string-input constructor arguments.
Note
Note cappa itself contains a number of component parse_* functions inside the parse
module, which can be used in combination with your own custom parse functions.
default_parse/`Arg.parse_inference¶
A function cappa.default_parse is exposed and can be directly referenced
in parse function sequences. However by default the default_parse function is applied automatically
at the end of the user-provided parse function/sequence.
With that said, if the default behavior is undesirable, one can set Arg(parse_inference=False) to
only execute the user provided parser function(s).
Parsing JSON¶
Another example of a potentially useful parsing concept could be to parse json string input. For example:
import json
from dataclasses import dataclass
from typing import Annotated
import cappa
@dataclass
class Example:
todo: Annotated[dict[str, int], cappa.Arg(parse=json.loads)]
todo = cappa.parse(Todo)
print(todo)
Natively (at present), cappa doesn’t have any specific dict type inference because it’s
ambiguous what CLI input shape that ought to map to. However, by combining that with
a dedicated parse=json.loads annotation, example.py '{"foo": "bar"}' now yields
Example({'foo': 'bar'}).
Composing Multiple Parsers¶
Arg.parse accepts either a single parser function, like above, or a sequence of parsers
which will be called…in sequence. The return value of earlier parsers in the chain will be
routed into the input of later parsers.
import json
from dataclasses import dataclass
from typing import Annotated
import cappa
def get_key(value: dict[str, str]) -> str:
return value.get("todo", "")
@dataclass
class Example:
todo: Annotated[str, cappa.Arg(parse=[json.loads, get_key])]
todo = cappa.parse(Todo)
print(todo)
unpack_arguments: Invoking constructors with * and ** operators¶
In some cases, you might want to construct objects that have required keyword arguments (pydantic models!) or otherwise who’s constructors do not accept the single argument that would typically be provided to single CLI fields’ parser. This is the use case for cappa.unpack_arguments.
Cappa will unpack the arguments based on the incoming type of data (e.g. sequence vs mapping).
Note
(Arg.destructure)[#argument-destructuring] is a similar feature, in that it allows one to compose
complex types into the CLI structure; but it in essentially opposite uses. unpack_arguments produces
a complex type from a single CLI argument, whereas a “destructured” argument composes together multiple
CLI arguments into one object without requiring a separate command.
*args Unpacking¶
Sequence unpacking (*) can be used to provide values to constructors that accept multiple arguments
positionally. For example:
from dataclasses import dataclass
from typing import Annotated
from cappa import unpack_arguments, Arg, parse
@dataclass
class Point:
x: int
y: int
@dataclass
class CLI:
point: Annotated[Point, Arg(num_args=2, parse=unpack_arguments)]
print(parse(CLI))
$ cli.py 1, 2
CLI(Point(1, 2))
Without unpack_arguments, the Point(...) constructor call would have
been handed [1, 2]. Essentially the above is roughly equivalent to
class Point:
...
@classmethod
def from_list(cls, value: list[int]):
return cls(*value)
...
class CLI:
point: Annotated[Point, Arg(num_args=2, parse=Point.from_list)]
**kwargs Unpacking¶
Mapping unpacking (**) can be used for constructors which accept multiple arguments
by keyword. For example, Pydantic classes’ constructors require keyword-only
arguments. This poses a challenge to a single CLI argument representing that constructor
value. One way around this could be accepting a JSON string, parsing the string as JSON,
and then unpacking the result into the constructor, effectively combining Parsing JSON
and Composing Multiple Parsers.
from pydantic import BaseModel
from typing import Annotated
from cappa import unpack_arguments, Arg, parse
class Point(BaseModel:
x: int
y: int
@dataclass
class CLI:
point: Annotated[Point, Arg(parse=[json.loads, unpack_arguments])]
print(parse(CLI))
$ cli.py '{"x": 1, "y": 2}'
CLI(Point(1, 2))
Similarly, you could imagine swapping json.loads for some alternate parser which accepted/parsed
x=1,y=2 or other input formats.
Parse Dependencies¶
Parse functions can are provided the same dependency injection system given by invoke and
actions. Thus, by accepting an additional argument to the parser annotated with
a supported type annotation, they’ll be provided that runtime value automatically.
This enables user-defined parsers that have the same amount of information that built-in parsers have when making decisions, namely the type information.
Currently supported injectable values include:
from cappa.type_view import TypeView
def parse(value, type_view: TypeView):
value = json.loads(value)
if type_view.is_mapping:
return value
raise ValueError("Requires a JSON mapping, string input!")
class CLI:
arg: Annotated[int, Arg(parse=parse)]
Note
More injectable dependencies could be supported. In particular the Arg instance comes to mind,
it’s just not immediately obvious what that would be. File an issue if you have a usecase!
Arg.choices¶
This can be used to limit the set of valid inputs to one of a few literal values,
for example, Arg(choices=['a', 'b', 'c'].
Both Literals (and literal unions) and Enums automatically infer choices.
Note
It may be preferable to use typing inference to imply choices as opposed to providing explicit choices depending on what you’re doing.
In scenarios with simple types/mappings, it wont make any difference (other than
yielding more or less precise type information to a type checker). For example foo: Literal[1, 2]
or foo: list[Literal[1, 2]] can be instead provided as choices= and it will yield
exactly the same behavior.
However, the type inference is sufficiently comprehensive that it is possible to
yield more complex types like list[tuple[str, Literal["1m", "1s"]]], where a simple
choices= option cannot precisely target the required values. In such cases providing
choices= will likely yield a runtime error, whereas using the inferred type validation
will correctly parse the inputs.
Arg.completion¶
This is an optional function which, if provided will be called during CLI (typically TAB) completion events in the shell. You can supply a function which accepts the partial input and returns an optional list of completions for that input.
Note
Arg.choices has a default completion implementation.
def complete_file(raw: str) -> list[cappa.Completion]:
return [filename for filename in os.listdir() if filename.startswith(raw)]
@dataclass
class Foo:
file: Annotated[str, cappa.Arg(completion=complete_file)
Note
Filename completion is actually natively supported by the shells, so this example is somewhat unnecessary to actually implement yourself.
Arg.has_value¶
Arg(has_value=True/False) can be used to explicitly control whether the argument in question
corresponds to a destination-type attribute as a value.
For example the Arg that produces the --help option has a dedicated action which produces
help text, and as such has a has_value=False.
While there may not be much point in manually setting this attribute to True (because it will default
to True in most cases), you could conceivably manually combine has_value=False and a
custom action, to avoid cappa trying to map your Arg back to a specific attribute.
Arg.destructured¶
Generally a single class/type corresponds to a command, and that type’s attributes correspond to the arguments/options for that command.
The exclusive group syntax is one counter example, where a single class attribute maps to more than one CLI argument.
“Destructured” arguments are essentially the inverse, in that they allow multiple attributes (and thus CLI arguments) to be mapped back to a single command’s class attribute.
from __future__ import annotations
from dataclasses import dataclass
from typing import Annotated
from cappa import Arg, Destructured
@dataclass
class Args:
sub_object: Destructured[SubObject]
@dataclass
class SubObject:
arg1: Annotated[str, Arg(long=True)]
arg2: Annotated[int, Arg(long=True)]
This essentially fans out the --arg1=foo --arg2=2 CLI arguments up into the parent Args command,
while ultimately mapping the resultant values back into the expected output structure of:
Args(sub_object=SubObject(arg1='foo', arg2=2)).
This concept has a couple of practical uses:
Code/argument reuse: In the above example
SubObjectcan now be shared between multiple subcommands to provide the same set of arguments in different places without requiring subclassing.Logical grouping/organization: This allows grouping of logically related fields in the python code without affecting how those arguments are represented in the CLI shape.
Note
Currently Arg.destructure() only works with singular concrete type annotations. That is,
in the above example Annotated[SubObject, Arg.destructured()];
whereas it will raise a ValueError if given SubObject | None or other more exotic annotations.
Principally, Annotated[SubObject | None, Arg.destructured()] could make sense to imply that all
child options are therefore optional, or that if any child attributes are missing, then that implies
sub_object=None at the top level. However both of these are mechanically much more complex than the
feature, as it exists today.
Additionally, this feature only works with the native backend. This probably has a workable solution for argparse, so file an issue if this affects you!
Note
(unpack_arguments)[#unpack-arguments] is a similar feature, in that it allows one to compose
complex types into the CLI structure; but it in essentially opposite uses. unpack_arguments produces
a complex type from a single CLI argument, whereas a “destructured” argument composes together multiple
CLI arguments into one object without requiring a separate command.
Arg.propagate¶
Argument propagation is a way of making higher-level arguments available during the parsing of child
subcommands. When an argument is marked as Arg(propagate=True), access to that argument will
be made available in all child subcommands, while still recording the value itself to the
object on which it was defined.
from __future__ import annotations
from dataclasses import dataclass
from typing import Annotated
import cappa
@dataclass
class Main:
file: Annotated[str, cappa.Arg(long=True, propagate=True)]
subcommand: cappa.Subcommands[One | Two | None] = None
@dataclass
class One:
...
@dataclass
class Two:
subcommand: cappa.Subcommands[Nested | None] = None
@dataclass
class Nested:
...
print(cappa.parse(Main))
Given the above example, all of the following would be valid, and produce Main(file='config.txt', ...):
main.py --file config.txtmain.py one --file config.txtmain.py two --file config.txtmain.py two nested --file config.txt
If defined on a top-level command object (like above), that argument will effectively be available globally within the CLI, again while actually propagating the value back to the place at which it was defined.
However if the propagated argument is not defined at the top-level, it will not propagate “upwards” to parent commands; only downward to child subcommands.
Note
Arg.propagate is not currently enabled/allowed for positional arguments (file an issue if this
is a problem for you!) largely because it’s not clear that the feature makes any sense except
on (particularly optional) options.
Arg.propagate is not implemented in the argparse backend.
Propagated Arg Help¶
By default propagated arguments are added to child subcommand’s help as though the argument was defined like any other argument.
If you want propagated arguments categorically separated from normal arguments, you can assign them a distinct group, which will cause them to be displayed separately.
For example:
group = cappa.Group(name="Global", section=1)
@dataclass
class Command:
other1: Annotated[int, cappa.Arg(long=True)] = 1
other2: Annotated[int, cappa.Arg(long=True)] = 1
foo: Annotated[int, cappa.Arg(long=True, propagate=True, group=group)] = 1
bar: Annotated[str, cappa.Arg(long=True, propagate=True, group=group)] = 1
Would yield:
Options
[--other1 OTHER1] (Default: 1)
[--other2 OTHER2] (Default: 1)
Global
[--foo FOO] (Default: 1)
[--bar BAR] (Default: 1)
Note, this is no different from use of Arg.group in any other context, except in that
the argument only exists at the declaration point, so any grouping configuration will
also propagate down into the way child commands render those arguments as well.
Arg.required¶
Controls whether an argument is required.
Arg.deprecated¶
When truthy, will emit a deprecation message upon use of an argument. There is a default message generated, but supplying a string will yield that as the deprecation message.