Commands

A command can be as simple as a dataclass-like object, with no additional annotations. Supported object-types include:

dataclasses
from cappa import parse

from dataclasses import dataclass

@dataclass
class Dataclass:
    name: str

value = parse(Dataclass)
assert isinstance(value, Dataclass)
Pydantic models
from cappa import parse
from pydantic import BaseModel

class PydanticModel(BaseModel):
    name: str

value = parse(PydanticModel)
assert isinstance(value, PydanticModel)
Pydantic dataclasses
from cappa import parse
from pydantic.dataclasses import dataclass as pydantic_dataclass

@pydantic_dataclass
class PydanticDataclass:
    name: str

value = parse(PydanticDataclass)
assert isinstance(value, PydanticDataclass)
Attrs classes
from cappa import parse
from attr import define

@define
class AttrsClass:
    name: str

value = parse(AttrsClass)
assert isinstance(value, AttrsClass)
cattrs classes
from cappa import parse
from attr import define

@define
class CAttrsClass:
    name: str

value = parse(CAttrsClass)
assert isinstance(value, CAttrsClass)

An undecorated dataclass-class will receive all the default cappa.command behavior. In order to customize or change the default behavior, you would begin by wrapping the class in the @cappa.command decorator like so:

@cappa.command()
@dataclass
class Dataclass:
    name: str

Note

The wrapped class is directly returned from the decorator. So unlike click, the resultant object can be directly used in the same way that you’d have been able to do sans decorator.

Note

All arguments to cappa.command directly translate to cappa.Command attributes. When annotating an object with @command(), you are essentially pre-configuring the generated cappa.Command for that particular command.

Subcommands

See Subcommands for details.

In short, any supported dataclass-like class that can be a top-level command is also compatible with being a subcommand. The only difference is that subcommands are attached to parent commands in a tree by way of having the parent define which subcommands are available options.

from cappa import Subcommands

@dataclass
class Command:
    subcommand: Subcommands[SubcmdOne | SubcmdTwo]

@command(name=...) / Command.name

By default, the name of the command is inferred from the name of the containing class, and converted to dash-case. For example Example would become example, and SomeLongName would become some-long-name.

The name field can be used to override this behavior.

@command(help=...) / Command.help

See Help Text Inference for details.

Essentially this replaces any inferred (from docstring) short help text for the command.

@command(description=...) / Command.description

See Help Text Inference for details.

Essentially this replaces any inferred (from docstring) extended help text for the command.

@command(invoke=...) / Command.invoke

See Invoke documentation for more details.

Essentially this invokes a function upon a command/subcommand being selected during parsing, corresponds to use of the cappa.invoke API rather than cappa.parse.

This API is roughly comparable to how click maps subcommands to functions.

@command(hidden=...) / Command.hidden

Excludes the command in question from parent helptext generation. As such this option is only relevant to subcommands.

@command(default_short=...) / Command.default_short

By default unannotated arguments are considered to be ordered positional aruguments.

This option controls whether un-annotated arguments default to implying Arg(short=True).

For example:

@command(default_short=True)
@dataclass
class Foo:
    bar: int

# which is then called like:
# `foo -b 4`

@command(default_long=...) / Command.default_long

By default unannotated arguments are considered to be ordered positional aruguments.

This option controls whether un-annotated arguments default to implying Arg(long=True).

For example:

@command(default_long=True)
@dataclass
class Foo:
    bar: int

# which is then called like:
# `foo --bar 4`

@command(deprecated=...) / Command.deprecated

This generates a warning if the command in question is used at runtime.

If True is supplied, a default deprecation message is generated. Alternatively it accepts the string message that should be emitted.

API