Skip to content

cmd2.decorators

cmd2.decorators

Decorators for cmd2 commands.

F module-attribute

F = TypeVar('F', bound=Callable[..., Any])

RawCommandFunc module-attribute

RawCommandFunc = UnboundCommandFunc[
    CmdOrSetT, [Statement | str]
]

ArgListCommandFunc module-attribute

ArgListCommandFunc = UnboundCommandFunc[
    CmdOrSetT, [list[str]]
]

ArgparseCommandFunc module-attribute

ArgparseCommandFunc = (
    UnboundCommandFunc[CmdOrSetT, [Namespace]]
    | UnboundCommandFunc[CmdOrSetT, [Namespace, list[str]]]
)

with_category

with_category(category)

Decorate a do_* command function to apply a category.

Permissive type hints allow this decorator to be stacked in any order, even when other decorators in the chain transform the signature or return type of the command function.

PARAMETER DESCRIPTION
category

the name of the category in which this command should be grouped when displaying the list of commands.

TYPE: str

RETURNS DESCRIPTION
Callable[[F], F]

a decorator that assigns the specified category to the command function

Example:

class MyApp(cmd2.Cmd):
    @cmd2.with_category("Text Functions")
    def do_echo(self, args: cmd2.Statement) -> None:
        self.poutput(args)

For an alternative approach to categorizing commands using a function, see cmd2.utils.categorize

Source code in cmd2/decorators.py
def with_category(
    category: str,
) -> Callable[[F], F]:
    """Decorate a ``do_*`` command function to apply a category.

    Permissive type hints allow this decorator to be stacked in any order, even
    when other decorators in the chain transform the signature or return type of
    the command function.

    :param category: the name of the category in which this command should
                     be grouped when displaying the list of commands.
    :return: a decorator that assigns the specified category to the command function

    Example:
    ```py
    class MyApp(cmd2.Cmd):
        @cmd2.with_category("Text Functions")
        def do_echo(self, args: cmd2.Statement) -> None:
            self.poutput(args)
    ```

    For an alternative approach to categorizing commands using a function, see
    [cmd2.utils.categorize][]

    """

    def cat_decorator(func: F) -> F:
        from .utils import categorize

        categorize(func, category)
        return func

    return cat_decorator

with_argument_list

with_argument_list(
    cmd_func: ArgListCommandFunc[CmdOrSetT],
    *,
    preserve_quotes: bool = False,
) -> RawCommandFunc[CmdOrSetT]
with_argument_list(
    cmd_func: None = None, *, preserve_quotes: bool = False
) -> Callable[
    [ArgListCommandFunc[CmdOrSetT]],
    RawCommandFunc[CmdOrSetT],
]
with_argument_list(cmd_func=None, *, preserve_quotes=False)

Decorate a do_* command function to receive a list of parsed arguments.

This decorator can be used either directly (@with_argument_list) or as a factory with arguments (@with_argument_list(preserve_quotes=True)).

PARAMETER DESCRIPTION
cmd_func

The command function being decorated.

TYPE: ArgListCommandFunc[CmdOrSetT] | None DEFAULT: None

preserve_quotes

If True, argument quotes will not be stripped from the input.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
RawCommandFunc[CmdOrSetT] | Callable[[ArgListCommandFunc[CmdOrSetT]], RawCommandFunc[CmdOrSetT]]

A command function that accepts a list of strings instead of a raw string.

Example:

class MyApp(cmd2.Cmd):
    # Basic usage: receives a list of words with quotes stripped
    @cmd2.with_argument_list
    def do_echo(self, arglist: list[str]) -> None:
        self.poutput(" ".join(arglist))

    # Factory usage: preserves quotes in the argument list
    @cmd2.with_argument_list(preserve_quotes=True)
    def do_print_raw(self, arglist: list[str]) -> None:
        self.poutput(" ".join(arglist))

Source code in cmd2/decorators.py
def with_argument_list(
    cmd_func: ArgListCommandFunc[CmdOrSetT] | None = None,
    *,
    preserve_quotes: bool = False,
) -> RawCommandFunc[CmdOrSetT] | Callable[[ArgListCommandFunc[CmdOrSetT]], RawCommandFunc[CmdOrSetT]]:
    """Decorate a ``do_*`` command function to receive a list of parsed arguments.

    This decorator can be used either directly (``@with_argument_list``) or as a
    factory with arguments (``@with_argument_list(preserve_quotes=True)``).

    :param cmd_func: The command function being decorated.
    :param preserve_quotes: If ``True``, argument quotes will not be stripped from the input.
    :return: A command function that accepts a list of strings instead of a raw string.

    Example:
    ```py
    class MyApp(cmd2.Cmd):
        # Basic usage: receives a list of words with quotes stripped
        @cmd2.with_argument_list
        def do_echo(self, arglist: list[str]) -> None:
            self.poutput(" ".join(arglist))

        # Factory usage: preserves quotes in the argument list
        @cmd2.with_argument_list(preserve_quotes=True)
        def do_print_raw(self, arglist: list[str]) -> None:
            self.poutput(" ".join(arglist))
    ```

    """

    def arg_decorator(func: ArgListCommandFunc[CmdOrSetT]) -> RawCommandFunc[CmdOrSetT]:
        """Decorate function that ingests an Argument List function and returns a raw command function.

        The returned function will process the raw input into an argument list to be passed to the wrapped function.

        :param func: The defined argument list command function
        :return: Function that takes raw input and converts to an argument list to pass to the wrapped function.
        """

        @functools.wraps(func)
        def cmd_wrapper(*args: Any, **kwargs: Any) -> bool | None:
            """Command function wrapper which translates command line into an argument list and calls actual command function.

            :param args: All positional arguments to this function.  We're expecting there to be:
                            cmd_app, statement: Union[Statement, str]
                            contiguously somewhere in the list
            :param kwargs: any keyword arguments being passed to command function
            :return: return value of command function
            """
            cmd_app, statement = _parse_positionals(args)
            _, command_arg_list = cmd_app.statement_parser.get_command_arg_list(command_name, statement, preserve_quotes)
            func_arg_list = _arg_swap(args, statement, command_arg_list)
            return func(*func_arg_list, **kwargs)

        command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX) :]
        cmd_wrapper.__doc__ = func.__doc__
        return cmd_wrapper

    if callable(cmd_func):
        return arg_decorator(cmd_func)
    return arg_decorator

with_argparser

with_argparser(
    parser_source: Cmd2ArgumentParser,
    *,
    ns_provider: Callable[..., Namespace] | None = None,
    preserve_quotes: bool = False,
    with_unknown_args: bool = False,
) -> Callable[
    [ArgparseCommandFunc[CmdOrSetT]],
    RawCommandFunc[CmdOrSetT],
]
with_argparser(
    parser_source: NoParamParserFactory,
    *,
    ns_provider: Callable[..., Namespace] | None = None,
    preserve_quotes: bool = False,
    with_unknown_args: bool = False,
) -> Callable[
    [ArgparseCommandFunc[CmdOrSetT]],
    RawCommandFunc[CmdOrSetT],
]
with_argparser(
    parser_source: ClassParamParserFactory[CmdOrSetT],
    *,
    ns_provider: Callable[..., Namespace] | None = None,
    preserve_quotes: bool = False,
    with_unknown_args: bool = False,
) -> Callable[
    [ArgparseCommandFunc[CmdOrSetT]],
    RawCommandFunc[CmdOrSetT],
]
with_argparser(
    parser_source,
    *,
    ns_provider=None,
    preserve_quotes=False,
    with_unknown_args=False,
)

Decorate a do_* command function to populate its args argument with a Cmd2ArgumentParser.

PARAMETER DESCRIPTION
parser_source

an existing Cmd2ArgumentParser instance or a factory (callable, staticmethod, or classmethod) that returns one.

TYPE: ParserSource[CmdOrSetT]

ns_provider

An optional function that accepts a cmd2.Cmd or cmd2.CommandSet object as an argument and returns an argparse.Namespace. This is useful if the Namespace needs to be prepopulated with state data that affects parsing.

TYPE: Callable[..., Namespace] | None DEFAULT: None

preserve_quotes

if True, then arguments passed to argparse maintain their quotes

TYPE: bool DEFAULT: False

with_unknown_args

if true, then capture unknown args

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
Callable[[ArgparseCommandFunc[CmdOrSetT]], RawCommandFunc[CmdOrSetT]]

function that gets passed argparse-parsed args in a Namespace A cmd2_statement attribute is included in the Namespace to provide access to the cmd2.Statement object that was created when parsing the command line. This can be useful if the command function needs to know the command line.

Example:

parser = cmd2.Cmd2ArgumentParser()
parser.add_argument("-p", "--piglatin", action="store_true", help="atinLay")
parser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE")
parser.add_argument("-r", "--repeat", type=int, help="output [n] times")
parser.add_argument("words", nargs="+", help="words to print")


class MyApp(cmd2.Cmd):
    @cmd2.with_argparser(parser, preserve_quotes=True)
    def do_argprint(self, args: argparse.Namespace) -> None:
        "Print the options and argument list this options command was called with."
        self.poutput(f"args: {args!r}")

Example with unknown args:

parser = cmd2.Cmd2ArgumentParser()
parser.add_argument("-p", "--piglatin", action="store_true", help="atinLay")
parser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE")
parser.add_argument("-r", "--repeat", type=int, help="output [n] times")


class MyApp(cmd2.Cmd):
    @cmd2.with_argparser(parser, with_unknown_args=True)
    def do_argprint(self, args: argparse.Namespace, unknown_args: list[str]):
        "Print the options and argument list this options command was called with."
        self.poutput(f"args: {args!r}")
        self.poutput(f"unknown_args: {unknown_args}")
Source code in cmd2/decorators.py
def with_argparser(
    parser_source: ParserSource[CmdOrSetT],
    *,
    ns_provider: Callable[..., argparse.Namespace] | None = None,
    preserve_quotes: bool = False,
    with_unknown_args: bool = False,
) -> Callable[[ArgparseCommandFunc[CmdOrSetT]], RawCommandFunc[CmdOrSetT]]:
    """Decorate a ``do_*`` command function to populate its ``args`` argument with a Cmd2ArgumentParser.

    :param parser_source: an existing Cmd2ArgumentParser instance or a factory
                          (callable, staticmethod, or classmethod) that returns one.
    :param ns_provider: An optional function that accepts a cmd2.Cmd or cmd2.CommandSet object as an argument and returns an
                        argparse.Namespace. This is useful if the Namespace needs to be prepopulated with state data that
                        affects parsing.
    :param preserve_quotes: if ``True``, then arguments passed to argparse maintain their quotes
    :param with_unknown_args: if true, then capture unknown args
    :return: function that gets passed argparse-parsed args in a ``Namespace``
             A ``cmd2_statement`` attribute is included in the ``Namespace`` to provide access to the
             [cmd2.Statement][] object that was created when parsing the command line. This can be useful
             if the command function needs to know the command line.

    Example:
    ```py
    parser = cmd2.Cmd2ArgumentParser()
    parser.add_argument("-p", "--piglatin", action="store_true", help="atinLay")
    parser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE")
    parser.add_argument("-r", "--repeat", type=int, help="output [n] times")
    parser.add_argument("words", nargs="+", help="words to print")


    class MyApp(cmd2.Cmd):
        @cmd2.with_argparser(parser, preserve_quotes=True)
        def do_argprint(self, args: argparse.Namespace) -> None:
            "Print the options and argument list this options command was called with."
            self.poutput(f"args: {args!r}")
    ```

    Example with unknown args:

    ```py
    parser = cmd2.Cmd2ArgumentParser()
    parser.add_argument("-p", "--piglatin", action="store_true", help="atinLay")
    parser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE")
    parser.add_argument("-r", "--repeat", type=int, help="output [n] times")


    class MyApp(cmd2.Cmd):
        @cmd2.with_argparser(parser, with_unknown_args=True)
        def do_argprint(self, args: argparse.Namespace, unknown_args: list[str]):
            "Print the options and argument list this options command was called with."
            self.poutput(f"args: {args!r}")
            self.poutput(f"unknown_args: {unknown_args}")
    ```

    """

    def arg_decorator(func: ArgparseCommandFunc[CmdOrSetT]) -> RawCommandFunc[CmdOrSetT]:
        """Decorate function that ingests an Argparse Command Function and returns a raw command function.

        The returned function will process the raw input into an argparse Namespace to be passed to the wrapped function.

        :param func: The defined argparse command function
        :return: Function that takes raw input and converts to an argparse Namespace to passed to the wrapped function.
        """

        @functools.wraps(func)
        def cmd_wrapper(*args: Any, **kwargs: Any) -> bool | None:
            """Command function wrapper which translates command line into argparse Namespace and call actual command function.

            :param args: All positional arguments to this function.  We're expecting there to be:
                            cmd_app, statement: Union[Statement, str]
                            contiguously somewhere in the list
            :param kwargs: any keyword arguments being passed to command function
            :return: return value of command function
            :raises Cmd2ArgparseError: if argparse has error parsing command line
            """
            cmd_app, statement_arg = _parse_positionals(args)
            statement, command_arg_list = cmd_app.statement_parser.get_command_arg_list(
                command_name, statement_arg, preserve_quotes
            )

            # Pass cmd_wrapper instead of func, since it contains the parser info.
            arg_parser = cmd_app.command_parsers.get(cmd_wrapper)
            if arg_parser is None:
                # This shouldn't be possible to reach
                raise ValueError(f"No argument parser found for {command_name}")  # pragma: no cover

            if ns_provider is None:
                initial_namespace = None
            else:
                # The namespace provider may or may not be defined in the same class as the command. Since provider
                # functions are registered with the command argparser before anything is instantiated, we
                # need to find an instance at runtime that matches the types during declaration
                provider_self = cmd_app._resolve_func_self(ns_provider, args[0])
                initial_namespace = ns_provider(provider_self if provider_self is not None else cmd_app)

            try:
                parsing_results: tuple[argparse.Namespace] | tuple[argparse.Namespace, list[str]]
                with arg_parser.output_to(cmd_app.stdout):
                    if with_unknown_args:
                        parsing_results = arg_parser.parse_known_args(command_arg_list, initial_namespace)
                    else:
                        parsing_results = (arg_parser.parse_args(command_arg_list, initial_namespace),)
            except SystemExit as exc:
                raise Cmd2ArgparseError from exc

            # Add cmd2-specific attributes to the Namespace
            parsed_namespace = parsing_results[0]

            # Include the Statement object created from the command line
            setattr(parsed_namespace, constants.NS_ATTR_STATEMENT, statement)

            # Ensure subcommand function attribute is always present.
            if not hasattr(parsed_namespace, constants.NS_ATTR_SUBCOMMAND_FUNC):
                setattr(parsed_namespace, constants.NS_ATTR_SUBCOMMAND_FUNC, None)

            func_arg_list = _arg_swap(args, statement_arg, *parsing_results)
            return func(*func_arg_list, **kwargs)

        command_name = func.__name__[len(constants.COMMAND_FUNC_PREFIX) :]

        spec = ArgparseCommandSpec(
            parser_source=parser_source,
            preserve_quotes=preserve_quotes,
        )
        setattr(cmd_wrapper, constants.ARGPARSE_COMMAND_ATTR_SPEC, spec)

        return cmd_wrapper

    return arg_decorator

as_subcommand_to

as_subcommand_to(
    command: str,
    subcommand: str,
    parser_source: Cmd2ArgumentParser,
    *,
    help: str | None = None,
    aliases: Sequence[str] = (),
    deprecated: bool = False,
) -> Callable[[F], F]
as_subcommand_to(
    command: str,
    subcommand: str,
    parser_source: NoParamParserFactory,
    *,
    help: str | None = None,
    aliases: Sequence[str] = (),
    deprecated: bool = False,
) -> Callable[[F], F]
as_subcommand_to(
    command: str,
    subcommand: str,
    parser_source: ClassParamParserFactory[CmdOrSetT],
    *,
    help: str | None = None,
    aliases: Sequence[str] = (),
    deprecated: bool = False,
) -> Callable[[F], F]
as_subcommand_to(
    command,
    subcommand,
    parser_source,
    *,
    help=None,
    aliases=(),
    deprecated=False,
)

Tag a function as a subcommand to an existing argparse decorated command.

Permissive type hints allow this decorator to be stacked in any order, even when other decorators in the chain transform the signature or return type of the subcommand function.

While this decorator has permissive type hints, the subcommand function's signature must match the root command's signature. For example, if the root command uses with_unknown_args=True, then the subcommand function must also accept the unknown arguments list.

PARAMETER DESCRIPTION
command

Command Name. Space-delimited subcommands may optionally be specified

TYPE: str

subcommand

Subcommand name

TYPE: str

parser_source

an existing Cmd2ArgumentParser instance or a factory (callable, staticmethod, or classmethod) that returns one.

TYPE: ParserSource[CmdOrSetT]

help

optional help message for this subcommand which displays in the list of subcommands of the command we are adding to.

TYPE: str | None DEFAULT: None

aliases

optional alternative names for this subcommand.

TYPE: Sequence[str] DEFAULT: ()

deprecated

whether this subcommand is deprecated (requires Python 3.13+).

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
Callable[[F], F]

a decorator which configures the target function to be a subcommand handler

Example:

base_parser = cmd2.Cmd2ArgumentParser()
base_parser.add_subparsers(title="subcommands", metavar="SUBCOMMAND", required=True)
sub_parser = cmd2.Cmd2ArgumentParser()


class MyApp(cmd2.Cmd):
    @cmd2.with_argparser(base_parser)
    def do_base(self, args: argparse.Namespace) -> None:
        args.cmd2_subcommand_func(args)

    @cmd2.as_subcommand_to("base", "sub", sub_parser, help="the subcommand")
    def sub_func(self, args: argparse.Namespace) -> None:
        self.poutput("Subcommand executed")

Source code in cmd2/decorators.py
def as_subcommand_to(
    command: str,
    subcommand: str,
    parser_source: ParserSource[CmdOrSetT],
    *,
    help: str | None = None,  # noqa: A002
    aliases: Sequence[str] = (),
    deprecated: bool = False,
) -> Callable[[F], F]:
    """Tag a function as a subcommand to an existing argparse decorated command.

    Permissive type hints allow this decorator to be stacked in any order, even
    when other decorators in the chain transform the signature or return type of
    the subcommand function.

    While this decorator has permissive type hints, the subcommand function's signature
    must match the root command's signature. For example, if the root command uses
    `with_unknown_args=True`, then the subcommand function must also accept the
    unknown arguments list.

    :param command: Command Name. Space-delimited subcommands may optionally be specified
    :param subcommand: Subcommand name
    :param parser_source: an existing Cmd2ArgumentParser instance or a factory
                          (callable, staticmethod, or classmethod) that returns one.
    :param help: optional help message for this subcommand which displays in the list of subcommands
                 of the command we are adding to.
    :param aliases: optional alternative names for this subcommand.
    :param deprecated: whether this subcommand is deprecated (requires Python 3.13+).
    :return: a decorator which configures the target function to be a subcommand handler

    Example:
    ```py
    base_parser = cmd2.Cmd2ArgumentParser()
    base_parser.add_subparsers(title="subcommands", metavar="SUBCOMMAND", required=True)
    sub_parser = cmd2.Cmd2ArgumentParser()


    class MyApp(cmd2.Cmd):
        @cmd2.with_argparser(base_parser)
        def do_base(self, args: argparse.Namespace) -> None:
            args.cmd2_subcommand_func(args)

        @cmd2.as_subcommand_to("base", "sub", sub_parser, help="the subcommand")
        def sub_func(self, args: argparse.Namespace) -> None:
            self.poutput("Subcommand executed")
    ```

    """

    def arg_decorator(func: F) -> F:
        spec = SubcommandSpec(
            name=subcommand,
            command=command,
            help=help,
            aliases=tuple(aliases),
            deprecated=deprecated,
            parser_source=parser_source,
        )
        setattr(func, constants.SUBCOMMAND_ATTR_SPEC, spec)
        return func

    return arg_decorator