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`

    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72

    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`

    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203

    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`

    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377

    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`

    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480

    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