Help¶
Help Inference¶
Cappa tries to infer help text from a variety of sources, preferring them in descending order of priority:
An explicit
help=argumentA PEP-727
DocannotationThe class “attribute docstring”
The class docstring argument description
If none of the above sources produce help text, no description will be rendered.
Explicit help=¶
All of Command, Subcommand, and Arg accept a help argument, which will
take priority over any other existent form of help text inference.
from typing import Annotated
import cappa
@cappa.command(help='Command help')
class Command:
arg: Annotated[str, Arg(help='Arg help')]
arg: Annotated[Sub, cappa.Subcommand(help='Subcommand help')]
PEP-727 Doc annotation¶
PEP-727 proposes adding a typing.Doc
object, in an attempt to standardize the location tooling must handle in order
to find documentation. If accepted, this is currently targeted to land in
python 3.13.
As of typing_extensions version 4.8.0, there exists a typing_extensions.Doc
object, which preemptively will fall back to the typing definition if defined.
If PEP-727 is ultimately rejected, this variant may or may not be abruptly
removed, it would entirely depend upon typing_extensions’s reaction to the
rejection.
When found, a Doc annotation will be used to infer help text, unless
explicitly overridden by a help= argument.
from typing import Annotated
from typing_extensions import Doc
import cappa
@cappa.command
class Command:
arg: Annotated[str, Doc('Arg help')]
Class “attribute docstring” Parsing¶
An “attribute docstring” is a common convention whereby a declarative class attribute is “annotated” similarly to a class docstring. For example:
from dataclasses import dataclass
@dataclass
class Command:
arg: int
"""This arg is an int."""
Note
Attribute docstrings are not a first-class concept in python today, although there is a rejected PEP associated with the idea.
As such, ast traversal is required to obtain it, which implies a requirement
that ast.getsource be able to function on the given source class. For most
typical user-written situations this should not be an issue, but it’s worth
noting the relative complexity involved with this option.
Class Docstring Parsing¶
Note
Docstring parsing is provided by the docstring-parser dependency. You can
include this dependency through cappa with the docstring extra (cappa[docstring]).
In the event other sources of help text are not found, the command class’ docstring will be parsed, supporting either Google or Numpy styles of docstring formatting.
The short and long docstring descriptions are inferred as extended command-level help text
Argument descriptions are inferred from the arguments list within the docstring
from typing import Annotated
import cappa
@cappa.command
class Command:
arg: Annotated[str, Doc('Arg help')]
class Example:
"""Example CLI.
With some long description.
Arguments:
foo: The number of foos
"""
foo: int
would produce something like the following help text:
Usage: example.py [-h]
Example CLI. With some long description.
Positional Arguments:
foo The number of foos
Argument Help Formatting¶
By default, help text is composed from a few sources:
The actual help text (as described above)
The default argument value (if exists)
The set of available “choices” (if exists) (for
Enum,Literal, andchoices=[...])
This can be controlled through the use of the help_formatter argument to the root
cappa.parse, cappa.invoke, etc functions. Additionally it can be set on a
per-command/subcommand level by making use of the @cappa.command(help_formatter=...)
kwarg.
Customize “default” help representation¶
By default, the default value will be rendered into the help text as (Default: {default}).
You can customize this, by altering the default help_formatter:
from cappa import parse, HelpFormatter
class Command:
...
parse(Command, help_formatter=HelpFormatter(default_format="(Default `{default}`)"))
Customizing help formatter “sources”¶
The default arg_format is:
arg_format: ArgFormat = (
Markdown("{help}"),
Markdown("{choices}"),
Markdown("{default}", style="dim italic"),
)
This means each individual argument’s help text will be comprised of 3 Markdown interpreted
sections concatenated together.
Each section may be either Text or Markdown and accepts any styling customization
allowed by that primitive.
For example, some argument: foo: Annotated[Literal["one", "two"], Arg(help="Foo.")] = "two".
The rendered help text will be will (by default) look like: Foo. Valid options: one, two. (Default: two)
arg_format may be any of:
A
Text/MarkdownA string: This will be coerced to a rich
Textof the provided stringA callable: Of shape
Callable[[Arg], str | None]or stricter, returning the formatted help textAn example of this might look like:
from cappa import parse, HelpFormatter, Arg class Command: foo: Annotated[str, Arg(help="Help text.", deprecated=True)] = "foo" def deprecated(arg: Arg) -> str | None: if arg.deprecated: return "Deprecrated" return None parse(Command, help_formatter=HelpFormatter(arg_format=("{default}", "{help}", deprecated))
Resulting in something like
(Default: foo) Help text. Deprecrated.A sequence: Of any of the above. Sequences will be joined together with an empty space.
Any arg_format segment who’s ultimate formatting results in an empty string or None will be omitted.
The purpose of allowing sequences of individual segments is to ensure consistent
formatting when individual format options are not used. For example "{help} {default}"
would otherwise yield Foo. or (Default: foo) (i.e. trailing or leading spaces).
As such, where formatting may be variable (like with default), they should be split
into different segments.
The following format string identifiers are included in the format context for each segment:
{help}: TheArg.helpvalue{default}: TheArg.defaultwill first be rendered withdefault_format.{choices}:TheArg.choices` value{arg}: TheArgitself.