Skip to content

# cmd2.argparse_custom

## `` cmd2.argparse_custom

Module adds capabilities to argparse by patching a few of its functions.

It also defines a parser class called Cmd2ArgumentParser which improves error and help output over normal argparse. All cmd2 code uses this parser and it is recommended that developers of cmd2-based apps either use it or write their own parser that inherits from it. This will give a consistent look-and-feel between the help/error output of built-in cmd2 commands and the app-specific commands. If you wish to override the parser used by cmd2's built-in commands, see custom_parser.py example.

Since the new capabilities are added by patching at the argparse API level, they are available whether or not Cmd2ArgumentParser is used. However, the help and error output of Cmd2ArgumentParser is customized to notate nargs ranges whereas any other parser class won't be as explicit in their output.

Added capabilities

Extends argparse nargs functionality by allowing tuples which specify a range (min, max). To specify a max value with no upper bound, use a 1-item tuple (min,)

Example::

    # -f argument expects at least 3 values
    parser.add_argument('-f', nargs=(3,))

    # -f argument expects 3 to 5 values
    parser.add_argument('-f', nargs=(3, 5))

Tab Completion

cmd2 uses its ArgparseCompleter class to enable argparse-based tab completion on all commands that use the @with_argparse wrappers. Out of the box you get tab completion of commands, subcommands, and flag names, as well as instructive hints about the current argument that print when tab is pressed. In addition, you can add tab completion for each argument's values using parameters passed to add_argument().

Below are the 3 add_argument() parameters for enabling tab completion of an argument's value. Only one can be used at a time.

`choices` - pass a list of values to the choices parameter.

    Example::

        my_list = ['An Option', 'SomeOtherOption']
        parser.add_argument('-o', '--options', choices=my_list)

`choices_provider` - pass a function that returns choices. This is good in cases where the choice list is dynamically generated when the user hits tab.

    Example::

        def my_choices_provider(self):
            ...
            return my_generated_list

        parser.add_argument("arg", choices_provider=my_choices_provider)

`completer` - pass a tab completion function that does custom completion.

cmd2 provides a few completer methods for convenience (e.g., path_complete, delimiter_complete)

    Example::

        # This adds file-path completion to an argument
        parser.add_argument('-o', '--options', completer=cmd2.Cmd.path_complete)

    You can use functools.partial() to prepopulate values of the underlying
    choices and completer functions/methods.

    Example::

        # This says to call path_complete with a preset value for its path_filter argument
        dir_completer = functools.partial(path_complete,
                                          path_filter=lambda path: os.path.isdir(path))
        parser.add_argument('-o', '--options', completer=dir_completer)

For `choices_provider` and `completer`, do not set them to a bound method. This is because ArgparseCompleter passes the `self` argument explicitly to these functions. When ArgparseCompleter calls one, it will detect whether it is bound to a `Cmd` subclass or `CommandSet`. If bound to a `cmd2.Cmd subclass`, it will pass the app instance as the `self` argument. If bound to a `cmd2.CommandSet` subclass, it will pass the `CommandSet` instance as the `self` argument. Therefore instead of passing something like `self.path_complete`, pass `cmd2.Cmd.path_complete`.

`choices_provider` and `completer` functions can also be implemented as standalone functions (i.e. not a member of a class). In this case, ArgparseCompleter will pass its `cmd2.Cmd` app instance as the first positional argument.

Of the 3 tab completion parameters, `choices` is the only one where argparse validates user input against items in the choices list. This is because the other 2 parameters are meant to tab complete data sets that are viewed as dynamic. Therefore it is up to the developer to validate if the user has typed an acceptable value for these arguments.

There are times when what's being tab completed is determined by a previous argument on the command line. In these cases, ArgparseCompleter can pass a dictionary that maps the command line tokens up through the one being completed to their argparse argument name. To receive this dictionary, your choices/completer function should have an argument called arg_tokens.

    Example::

        def my_choices_provider(self, arg_tokens)
        def my_completer(self, text, line, begidx, endidx, arg_tokens)

All values of the arg_tokens dictionary are lists, even if a particular argument expects only 1 token. Since ArgparseCompleter is for tab completion, it does not convert the tokens to their actual argument types or validate their values. All tokens are stored in the dictionary as the raw strings provided on the command line. It is up to the developer to determine if the user entered the correct argument type (e.g. int) and validate their values.

CompletionItem Class - This class was added to help in cases where uninformative data is being tab completed. For instance, tab completing ID numbers isn't very helpful to a user without context. Returning a list of CompletionItems instead of a regular string for completion results will signal the ArgparseCompleter to output the completion results in a table of completion tokens with descriptive data instead of just a table of tokens::

    Instead of this:
        1     2     3

    The user sees this:
         ITEM_ID   Description
        ────────────────────────────
               1   My item
               2   Another item
               3   Yet another item

The left-most column is the actual value being tab completed and its header is that value's name. The right column header is defined using the `descriptive_headers` parameter of add_argument(), which is a list of header names that defaults to ["Description"]. The right column values come from the `CompletionItem.descriptive_data` member, which is a list with the same number of items as columns defined in descriptive_headers.

To use CompletionItems, just return them from your choices_provider or completer functions. They can also be used as argparse choices. When a CompletionItem is created, it stores the original value (e.g. ID number) and makes it accessible through a property called orig_value. cmd2 has patched argparse so that when evaluating choices, input is compared to CompletionItem.orig_value instead of the CompletionItem instance.

Example::

    Add an argument and define its descriptive_headers.

        parser.add_argument(
            add_argument(
            "item_id",
            type=int,
            choices_provider=get_items,
            descriptive_headers=["Item Name", "Checked Out", "Due Date"],
        )

    Implement the choices_provider to return CompletionItems.

        def get_items(self) -> list[CompletionItems]:
            """choices_provider which returns CompletionItems"""

            # CompletionItem's second argument is descriptive_data.
            # Its item count should match that of descriptive_headers.
            return [
                CompletionItem(1, ["My item", True, "02/02/2022"]),
                CompletionItem(2, ["Another item", False, ""]),
                CompletionItem(3, ["Yet another item", False, ""]),
            ]

    This is what the user will see during tab completion.

        ITEM_ID   Item Name          Checked Out   Due Date
        ───────────────────────────────────────────────────────
              1   My item            True          02/02/2022
              2   Another item       False
              3   Yet another item   False

`descriptive_headers` can be strings or `Rich.table.Columns` for more control over things like alignment.

- If a header is a string, it will render as a left-aligned column with its overflow behavior set to "fold". This means a long string will wrap within its cell, creating as many new lines as required to fit.

- If a header is a `Column`, it defaults to "ellipsis" overflow behavior. This means a long string which exceeds the width of its column will be truncated with an ellipsis at the end. You can override this and other settings when you create the `Column`.

`descriptive_data` items can include Rich objects, including styled Text and Tables.

To avoid printing a excessive information to the screen at once when a user presses tab, there is a maximum threshold for the number of CompletionItems that will be shown. Its value is defined in `cmd2.Cmd.max_completion_items`. It defaults to 50, but can be changed. If the number of completion suggestions exceeds this number, they will be displayed in the typical columnized format and will not include the descriptive_data of the CompletionItems.

Patched argparse functions

`argparse._ActionsContainer.add_argument` - adds arguments related to tab completion and enables nargs range parsing. See _add_argument_wrapper for more details on these arguments.

`argparse.ArgumentParser._check_value` - adds support for using `CompletionItems` as argparse choices. When evaluating choices, input is compared to `CompletionItem.orig_value` instead of the `CompletionItem` instance. See _ArgumentParser_check_value for more details.

`argparse.ArgumentParser._get_nargs_pattern` - adds support for nargs ranges. See _get_nargs_pattern_wrapper for more details.

`argparse.ArgumentParser._match_argument` - adds support for nargs ranges. See _match_argument_wrapper for more details.

`argparse._SubParsersAction.remove_parser` - new function which removes a sub-parser from a sub-parsers group. See _SubParsersAction_remove_parser for more details.

Added accessor methods

cmd2 has patched `argparse.Action` to include the following accessor methods for cases in which you need to manually access the cmd2-specific attributes.

- `argparse.Action.get_choices_callable()` - See `action_get_choices_callable` for more details.
- `argparse.Action.set_choices_provider()` - See `_action_set_choices_provider` for more details.
- `argparse.Action.set_completer()` - See `_action_set_completer` for more details.
- `argparse.Action.get_descriptive_headers()` - See `_action_get_descriptive_headers` for more details.
- `argparse.Action.set_descriptive_headers()` - See `_action_set_descriptive_headers` for more details.
- `argparse.Action.get_nargs_range()` - See `_action_get_nargs_range` for more details.
- `argparse.Action.set_nargs_range()` - See `_action_set_nargs_range` for more details.
- `argparse.Action.get_suppress_tab_hint()` - See `_action_get_suppress_tab_hint` for more details.
- `argparse.Action.set_suppress_tab_hint()` - See `_action_set_suppress_tab_hint` for more details.

cmd2 has patched `argparse.ArgumentParser` to include the following accessor methods

- `argparse.ArgumentParser.get_ap_completer_type()` - See `_ArgumentParser_get_ap_completer_type` for more details.
- `argparse.Action.set_ap_completer_type()` - See `_ArgumentParser_set_ap_completer_type` for more details.

Subcommand removal

cmd2 has patched `argparse._SubParsersAction` to include a `remove_parser()` method which can be used to remove a subcommand.

`argparse._SubParsersAction.remove_parser` - new function which removes a sub-parser from a sub-parsers group. See _SubParsersAction_remove_parser` for more details.

### `` ChoicesProviderFunc `module-attribute`

    ChoicesProviderFunc = ChoicesProviderFuncBase | ChoicesProviderFuncWithTokens

### `` CompleterFunc `module-attribute`

    CompleterFunc = CompleterFuncBase | CompleterFuncWithTokens

### `` ATTR_CHOICES_CALLABLE `module-attribute`

    ATTR_CHOICES_CALLABLE = 'choices_callable'

### `` ATTR_DESCRIPTIVE_HEADERS `module-attribute`

    ATTR_DESCRIPTIVE_HEADERS = 'descriptive_headers'

### `` ATTR_NARGS_RANGE `module-attribute`

    ATTR_NARGS_RANGE = 'nargs_range'

### `` ATTR_SUPPRESS_TAB_HINT `module-attribute`

    ATTR_SUPPRESS_TAB_HINT = 'suppress_tab_hint'

### `` CUSTOM_ACTION_ATTRIBS `module-attribute`

    CUSTOM_ACTION_ATTRIBS = set()

### `` orig_actions_container_add_argument `module-attribute`

    orig_actions_container_add_argument = add_argument

### `` orig_argument_parser_get_nargs_pattern `module-attribute`

    orig_argument_parser_get_nargs_pattern = _get_nargs_pattern

### `` orig_argument_parser_match_argument `module-attribute`

    orig_argument_parser_match_argument = _match_argument

### `` ATTR_AP_COMPLETER_TYPE `module-attribute`

    ATTR_AP_COMPLETER_TYPE = 'ap_completer_type'

### `` DEFAULT_ARGUMENT_PARSER `module-attribute`

    DEFAULT_ARGUMENT_PARSER = Cmd2ArgumentParser

### `` CompletionItem

    CompletionItem(value, descriptive_data, *args)

Bases: `str`

Completion item with descriptive text attached.

See header of this file for more information

CompletionItem Initializer.

PARAMETER | DESCRIPTION
--- | ---
`value` | the value being tab completed TYPE: `object`
`descriptive_data` | a list of descriptive data to display in the columns that follow the completion value. The number of items in this list must equal the number of descriptive headers defined for the argument. TYPE: `Sequence[Any]`
`args` | args for str init TYPE: `Any` DEFAULT: `()`

Source code in `cmd2/argparse_custom.py`

    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407

    def __init__(self, value: object, descriptive_data: Sequence[Any], *args: Any) -> None:
        """CompletionItem Initializer.

        :param value: the value being tab completed
        :param descriptive_data: a list of descriptive data to display in the columns that follow
                                 the completion value. The number of items in this list must equal
                                 the number of descriptive headers defined for the argument.
        :param args: args for str __init__
        """
        super().__init__(*args)

        # Make sure all objects are renderable by a Rich table.
        renderable_data = [obj if is_renderable(obj) else str(obj) for obj in descriptive_data]

        # Convert strings containing ANSI style sequences to Rich Text objects for correct display width.
        self.descriptive_data = ru.prepare_objects_for_rendering(*renderable_data)

        # Save the original value to support CompletionItems as argparse choices.
        # cmd2 has patched argparse so input is compared to this value instead of the CompletionItem instance.
        self._orig_value = value

#### `` descriptive_data `instance-attribute`

    descriptive_data = prepare_objects_for_rendering(*renderable_data)

#### `` orig_value `property`

    orig_value

Read-only property for _orig_value.

### `` ChoicesProviderFuncBase

Bases: `Protocol`

Function that returns a list of choices in support of tab completion.

### `` ChoicesProviderFuncWithTokens

Bases: `Protocol`

Function that returns a list of choices in support of tab completion and accepts a dictionary of prior arguments.

### `` CompleterFuncBase

Bases: `Protocol`

Function to support tab completion with the provided state of the user prompt.

### `` CompleterFuncWithTokens

Bases: `Protocol`

Function to support tab completion with the provided state of the user prompt, accepts a dictionary of prior args.

### `` ChoicesCallable

    ChoicesCallable(is_completer, to_call)

Enables using a callable as the choices provider for an argparse argument.

While argparse has the built-in choices attribute, it is limited to an iterable.

Initialize the ChoiceCallable instance.

PARAMETER | DESCRIPTION
--- | ---
`is_completer` | True if to_call is a tab completion routine which expects the args: text, line, begidx, endidx TYPE: `bool`
`to_call` | the callable object that will be called to provide choices for the argument. TYPE: `CompleterFunc | ChoicesProviderFunc`

Source code in `cmd2/argparse_custom.py`

    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502

    def __init__(
        self,
        is_completer: bool,
        to_call: CompleterFunc | ChoicesProviderFunc,
    ) -> None:
        """Initialize the ChoiceCallable instance.

        :param is_completer: True if to_call is a tab completion routine which expects
                             the args: text, line, begidx, endidx
        :param to_call: the callable object that will be called to provide choices for the argument.
        """
        self.is_completer = is_completer
        if is_completer:
            if not isinstance(to_call, (CompleterFuncBase, CompleterFuncWithTokens)):  # pragma: no cover
                # runtime checking of Protocols do not currently check the parameters of a function.
                raise ValueError(
                    'With is_completer set to true, to_call must be either CompleterFunc, CompleterFuncWithTokens'
                )
        elif not isinstance(to_call, (ChoicesProviderFuncBase, ChoicesProviderFuncWithTokens)):  # pragma: no cover
            # runtime checking of Protocols do not currently check the parameters of a function.
            raise ValueError(
                'With is_completer set to false, to_call must be either: '
                'ChoicesProviderFuncBase, ChoicesProviderFuncWithTokens'
            )
        self.to_call = to_call

#### `` is_completer `instance-attribute`

    is_completer = is_completer

#### `` to_call `instance-attribute`

    to_call = to_call

#### `` completer `property`

    completer

Retreive the internal Completer function, first type checking to ensure it is the right type.

#### `` choices_provider `property`

    choices_provider

Retreive the internal ChoicesProvider function, first type checking to ensure it is the right type.

### `` Cmd2HelpFormatter

    Cmd2HelpFormatter(prog, indent_increment=2, max_help_position=24, width=None, *, console=None, **kwargs)

Bases: `RichHelpFormatter`

Custom help formatter to configure ordering of help text.

Initialize Cmd2HelpFormatter.

Source code in `cmd2/argparse_custom.py`

    1116
    1117
    1118
    1119
    1120
    1121
    1122
    1123
    1124
    1125
    1126
    1127
    1128
    1129
    1130

    def __init__(
        self,
        prog: str,
        indent_increment: int = 2,
        max_help_position: int = 24,
        width: int | None = None,
        *,
        console: Cmd2RichArgparseConsole | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize Cmd2HelpFormatter."""
        super().__init__(prog, indent_increment, max_help_position, width, console=console, **kwargs)

        # Recast to assist type checkers
        self._console: Cmd2RichArgparseConsole | None

#### `` highlights `class-attribute`

    highlights = []

#### `` usage_markup `class-attribute`

    usage_markup = False

#### `` help_markup `class-attribute`

    help_markup = False

#### `` text_markup `class-attribute`

    text_markup = False

#### `` console `property` `writable`

    console

Return our console instance.

### `` RawDescriptionCmd2HelpFormatter

    RawDescriptionCmd2HelpFormatter(prog, indent_increment=2, max_help_position=24, width=None, *, console=None, **kwargs)

Bases: `RawDescriptionRichHelpFormatter`, `Cmd2HelpFormatter`

Cmd2 help message formatter which retains any formatting in descriptions and epilogs.

Initialize Cmd2HelpFormatter.

Source code in `cmd2/argparse_custom.py`

    1116
    1117
    1118
    1119
    1120
    1121
    1122
    1123
    1124
    1125
    1126
    1127
    1128
    1129
    1130

    def __init__(
        self,
        prog: str,
        indent_increment: int = 2,
        max_help_position: int = 24,
        width: int | None = None,
        *,
        console: Cmd2RichArgparseConsole | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize Cmd2HelpFormatter."""
        super().__init__(prog, indent_increment, max_help_position, width, console=console, **kwargs)

        # Recast to assist type checkers
        self._console: Cmd2RichArgparseConsole | None

#### `` highlights `class-attribute`

    highlights = []

#### `` usage_markup `class-attribute`

    usage_markup = False

#### `` help_markup `class-attribute`

    help_markup = False

#### `` text_markup `class-attribute`

    text_markup = False

#### `` console `property` `writable`

    console

Return our console instance.

### `` RawTextCmd2HelpFormatter

    RawTextCmd2HelpFormatter(prog, indent_increment=2, max_help_position=24, width=None, *, console=None, **kwargs)

Bases: `RawTextRichHelpFormatter`, `Cmd2HelpFormatter`

Cmd2 help message formatter which retains formatting of all help text.

Initialize Cmd2HelpFormatter.

Source code in `cmd2/argparse_custom.py`

    1116
    1117
    1118
    1119
    1120
    1121
    1122
    1123
    1124
    1125
    1126
    1127
    1128
    1129
    1130

    def __init__(
        self,
        prog: str,
        indent_increment: int = 2,
        max_help_position: int = 24,
        width: int | None = None,
        *,
        console: Cmd2RichArgparseConsole | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize Cmd2HelpFormatter."""
        super().__init__(prog, indent_increment, max_help_position, width, console=console, **kwargs)

        # Recast to assist type checkers
        self._console: Cmd2RichArgparseConsole | None

#### `` highlights `class-attribute`

    highlights = []

#### `` usage_markup `class-attribute`

    usage_markup = False

#### `` help_markup `class-attribute`

    help_markup = False

#### `` text_markup `class-attribute`

    text_markup = False

#### `` console `property` `writable`

    console

Return our console instance.

### `` ArgumentDefaultsCmd2HelpFormatter

    ArgumentDefaultsCmd2HelpFormatter(prog, indent_increment=2, max_help_position=24, width=None, *, console=None, **kwargs)

Bases: `ArgumentDefaultsRichHelpFormatter`, `Cmd2HelpFormatter`

Cmd2 help message formatter which adds default values to argument help.

Initialize Cmd2HelpFormatter.

Source code in `cmd2/argparse_custom.py`

    1116
    1117
    1118
    1119
    1120
    1121
    1122
    1123
    1124
    1125
    1126
    1127
    1128
    1129
    1130

    def __init__(
        self,
        prog: str,
        indent_increment: int = 2,
        max_help_position: int = 24,
        width: int | None = None,
        *,
        console: Cmd2RichArgparseConsole | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize Cmd2HelpFormatter."""
        super().__init__(prog, indent_increment, max_help_position, width, console=console, **kwargs)

        # Recast to assist type checkers
        self._console: Cmd2RichArgparseConsole | None

#### `` highlights `class-attribute`

    highlights = []

#### `` usage_markup `class-attribute`

    usage_markup = False

#### `` help_markup `class-attribute`

    help_markup = False

#### `` text_markup `class-attribute`

    text_markup = False

#### `` console `property` `writable`

    console

Return our console instance.

### `` MetavarTypeCmd2HelpFormatter

    MetavarTypeCmd2HelpFormatter(prog, indent_increment=2, max_help_position=24, width=None, *, console=None, **kwargs)

Bases: `MetavarTypeRichHelpFormatter`, `Cmd2HelpFormatter`

Cmd2 help message formatter which uses the argument 'type' as the default metavar value (instead of the argument 'dest').

Initialize Cmd2HelpFormatter.

Source code in `cmd2/argparse_custom.py`

    1116
    1117
    1118
    1119
    1120
    1121
    1122
    1123
    1124
    1125
    1126
    1127
    1128
    1129
    1130

    def __init__(
        self,
        prog: str,
        indent_increment: int = 2,
        max_help_position: int = 24,
        width: int | None = None,
        *,
        console: Cmd2RichArgparseConsole | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize Cmd2HelpFormatter."""
        super().__init__(prog, indent_increment, max_help_position, width, console=console, **kwargs)

        # Recast to assist type checkers
        self._console: Cmd2RichArgparseConsole | None

#### `` highlights `class-attribute`

    highlights = []

#### `` usage_markup `class-attribute`

    usage_markup = False

#### `` help_markup `class-attribute`

    help_markup = False

#### `` text_markup `class-attribute`

    text_markup = False

#### `` console `property` `writable`

    console

Return our console instance.

### `` TextGroup

    TextGroup(title, text, formatter_creator)

A block of text which is formatted like an argparse argument group, including a title.

Title: Here is the first row of text. Here is yet another row of text.

TextGroup initializer.

PARAMETER | DESCRIPTION
--- | ---
`title` | the group's title TYPE: `str`
`text` | the group's text (string or object that may be rendered by Rich) TYPE: `RenderableType`
`formatter_creator` | callable which returns a Cmd2HelpFormatter instance TYPE: `Callable[[], Cmd2HelpFormatter]`

Source code in `cmd2/argparse_custom.py`

    1244
    1245
    1246
    1247
    1248
    1249
    1250
    1251
    1252
    1253
    1254
    1255
    1256
    1257
    1258

    def __init__(
        self,
        title: str,
        text: RenderableType,
        formatter_creator: Callable[[], Cmd2HelpFormatter],
    ) -> None:
        """TextGroup initializer.

        :param title: the group's title
        :param text: the group's text (string or object that may be rendered by Rich)
        :param formatter_creator: callable which returns a Cmd2HelpFormatter instance
        """
        self.title = title
        self.text = text
        self.formatter_creator = formatter_creator

#### `` title `instance-attribute`

    title = title

#### `` text `instance-attribute`

    text = text

#### `` formatter_creator `instance-attribute`

    formatter_creator = formatter_creator

### `` Cmd2ArgumentParser

    Cmd2ArgumentParser(prog=None, usage=None, description=None, epilog=None, parents=(), formatter_class=Cmd2HelpFormatter, prefix_chars='-', fromfile_prefix_chars=None, argument_default=None, conflict_handler='error', add_help=True, allow_abbrev=True, exit_on_error=True, suggest_on_error=False, color=False, *, ap_completer_type=None)

Bases: `ArgumentParser`

Custom ArgumentParser class that improves error and help output.

Initialize the Cmd2ArgumentParser instance, a custom ArgumentParser added by cmd2.

PARAMETER | DESCRIPTION
--- | ---
`ap_completer_type` | optional parameter which specifies a subclass of ArgparseCompleter for custom tab completion behavior on this parser. If this is None or not present, then cmd2 will use argparse_completer.DEFAULT_AP_COMPLETER when tab completing this parser's arguments TYPE: `type[ArgparseCompleter] | None` DEFAULT: `None`

Source code in `cmd2/argparse_custom.py`

    1282
    1283
    1284
    1285
    1286
    1287
    1288
    1289
    1290
    1291
    1292
    1293
    1294
    1295
    1296
    1297
    1298
    1299
    1300
    1301
    1302
    1303
    1304
    1305
    1306
    1307
    1308
    1309
    1310
    1311
    1312
    1313
    1314
    1315
    1316
    1317
    1318
    1319
    1320
    1321
    1322
    1323
    1324
    1325
    1326
    1327
    1328
    1329
    1330
    1331
    1332
    1333
    1334
    1335
    1336
    1337

    def __init__(
        self,
        prog: str | None = None,
        usage: str | None = None,
        description: RenderableType | None = None,
        epilog: RenderableType | None = None,
        parents: Sequence[argparse.ArgumentParser] = (),
        formatter_class: type[Cmd2HelpFormatter] = Cmd2HelpFormatter,
        prefix_chars: str = '-',
        fromfile_prefix_chars: str | None = None,
        argument_default: str | None = None,
        conflict_handler: str = 'error',
        add_help: bool = True,
        allow_abbrev: bool = True,
        exit_on_error: bool = True,
        suggest_on_error: bool = False,
        color: bool = False,
        *,
        ap_completer_type: type['ArgparseCompleter'] | None = None,
    ) -> None:
        """Initialize the Cmd2ArgumentParser instance, a custom ArgumentParser added by cmd2.

        :param ap_completer_type: optional parameter which specifies a subclass of ArgparseCompleter for custom tab completion
                                  behavior on this parser. If this is None or not present, then cmd2 will use
                                  argparse_completer.DEFAULT_AP_COMPLETER when tab completing this parser's arguments
        """
        kwargs: dict[str, bool] = {}
        if sys.version_info >= (3, 14):
            # Python >= 3.14 so pass new arguments to parent argparse.ArgumentParser class
            kwargs = {
                "suggest_on_error": suggest_on_error,
                "color": color,
            }

        super().__init__(
            prog=prog,
            usage=usage,
            description=description,  # type: ignore[arg-type]
            epilog=epilog,  # type: ignore[arg-type]
            parents=parents or [],
            formatter_class=formatter_class,
            prefix_chars=prefix_chars,
            fromfile_prefix_chars=fromfile_prefix_chars,
            argument_default=argument_default,
            conflict_handler=conflict_handler,
            add_help=add_help,
            allow_abbrev=allow_abbrev,
            exit_on_error=exit_on_error,  # added in Python 3.9
            **kwargs,  # added in Python 3.14
        )

        # Recast to assist type checkers since these can be Rich renderables in a Cmd2HelpFormatter.
        self.description: RenderableType | None  # type: ignore[assignment]
        self.epilog: RenderableType | None  # type: ignore[assignment]

        self.set_ap_completer_type(ap_completer_type)  # type: ignore[attr-defined]

#### `` description `instance-attribute`

    description

#### `` epilog `instance-attribute`

    epilog

#### `` argument_default `instance-attribute`

    argument_default = argument_default

#### `` prefix_chars `instance-attribute`

    prefix_chars = prefix_chars

#### `` conflict_handler `instance-attribute`

    conflict_handler = conflict_handler

#### `` prog `instance-attribute`

    prog = _prog_name(prog)

#### `` usage `instance-attribute`

    usage = usage

#### `` formatter_class `instance-attribute`

    formatter_class = formatter_class

#### `` fromfile_prefix_chars `instance-attribute`

    fromfile_prefix_chars = fromfile_prefix_chars

#### `` add_help `instance-attribute`

    add_help = add_help

#### `` allow_abbrev `instance-attribute`

    allow_abbrev = allow_abbrev

#### `` exit_on_error `instance-attribute`

    exit_on_error = exit_on_error

#### `` suggest_on_error `instance-attribute`

    suggest_on_error = suggest_on_error

#### `` color `instance-attribute`

    color = color

#### `` add_subparsers

    add_subparsers(**kwargs)

Add a subcommand parser.

Set a default title if one was not given.

PARAMETER | DESCRIPTION
--- | ---
`kwargs` | additional keyword arguments TYPE: `Any` DEFAULT: `{}`

RETURNS | DESCRIPTION
--- | ---
`_SubParsersAction` | argparse Subparser Action

Source code in `cmd2/argparse_custom.py`

    1339
    1340
    1341
    1342
    1343
    1344
    1345
    1346
    1347
    1348
    1349
    1350

    def add_subparsers(self, **kwargs: Any) -> argparse._SubParsersAction:  # type: ignore[type-arg]
        """Add a subcommand parser.

        Set a default title if one was not given.

        :param kwargs: additional keyword arguments
        :return: argparse Subparser Action
        """
        if 'title' not in kwargs:
            kwargs['title'] = 'subcommands'

        return super().add_subparsers(**kwargs)

#### `` error

    error(message)

Override that applies custom formatting to the error message.

Source code in `cmd2/argparse_custom.py`

    1352
    1353
    1354
    1355
    1356
    1357
    1358
    1359
    1360
    1361
    1362
    1363
    1364
    1365
    1366
    1367
    1368
    1369
    1370

    def error(self, message: str) -> NoReturn:
        """Override that applies custom formatting to the error message."""
        lines = message.split('\n')
        formatted_message = ''
        for linum, line in enumerate(lines):
            if linum == 0:
                formatted_message = 'Error: ' + line
            else:
                formatted_message += '\n       ' + line

        self.print_usage(sys.stderr)

        # Add error style to message
        console = self._get_formatter().console
        with console.capture() as capture:
            console.print(formatted_message, style=Cmd2Style.ERROR, crop=False)
        formatted_message = f"{capture.get()}"

        self.exit(2, f'{formatted_message}\n')

#### `` format_help

    format_help()

Override to add a newline.

Source code in `cmd2/argparse_custom.py`

    1376
    1377
    1378

    def format_help(self) -> str:
        """Override to add a newline."""
        return super().format_help() + '\n'

#### `` create_text_group

    create_text_group(title, text)

Create a TextGroup using this parser's formatter creator.

Source code in `cmd2/argparse_custom.py`

    1380
    1381
    1382

    def create_text_group(self, title: str, text: RenderableType) -> TextGroup:
        """Create a TextGroup using this parser's formatter creator."""
        return TextGroup(title, text, self._get_formatter)

#### `` register

    register(registry_name, value, object)

Source code in `python3.14/argparse.py`

    1486
    1487
    1488

    def register(self, registry_name, value, object):
        registry = self._registries.setdefault(registry_name, {})
        registry[value] = object

#### `` set_defaults

    set_defaults(**kwargs)

Source code in `python3.14/argparse.py`

    1497
    1498
    1499
    1500
    1501
    1502
    1503
    1504

    def set_defaults(self, **kwargs):
        self._defaults.update(kwargs)

        # if these defaults match any existing arguments, replace
        # the previous default on the object with the new one
        for action in self._actions:
            if action.dest in kwargs:
                action.default = kwargs[action.dest]

#### `` get_default

    get_default(dest)

Source code in `python3.14/argparse.py`

    1506
    1507
    1508
    1509
    1510

    def get_default(self, dest):
        for action in self._actions:
            if action.dest == dest and action.default is not None:
                return action.default
        return self._defaults.get(dest, None)

#### `` add_argument

    add_argument(*args, **kwargs)

add_argument(dest, ..., name=value, ...) add_argument(option_string, option_string, ..., name=value, ...)

Source code in `python3.14/argparse.py`

    1517
    1518
    1519
    1520
    1521
    1522
    1523
    1524
    1525
    1526
    1527
    1528
    1529
    1530
    1531
    1532
    1533
    1534
    1535
    1536
    1537
    1538
    1539
    1540
    1541
    1542
    1543
    1544
    1545
    1546
    1547
    1548
    1549
    1550
    1551
    1552
    1553
    1554
    1555
    1556
    1557
    1558
    1559
    1560
    1561
    1562
    1563
    1564
    1565
    1566
    1567
    1568
    1569
    1570
    1571
    1572
    1573
    1574

    def add_argument(self, *args, **kwargs):
        """
        add_argument(dest, ..., name=value, ...)
        add_argument(option_string, option_string, ..., name=value, ...)
        """

        # if no positional args are supplied or only one is supplied and
        # it doesn't look like an option string, parse a positional
        # argument
        chars = self.prefix_chars
        if not args or len(args) == 1 and args[0][0] not in chars:
            if args and 'dest' in kwargs:
                raise TypeError('dest supplied twice for positional argument,'
                                ' did you mean metavar?')
            kwargs = self._get_positional_kwargs(*args, **kwargs)

        # otherwise, we're adding an optional argument
        else:
            kwargs = self._get_optional_kwargs(*args, **kwargs)

        # if no default was supplied, use the parser-level default
        if 'default' not in kwargs:
            dest = kwargs['dest']
            if dest in self._defaults:
                kwargs['default'] = self._defaults[dest]
            elif self.argument_default is not None:
                kwargs['default'] = self.argument_default

        # create the action object, and add it to the parser
        action_name = kwargs.get('action')
        action_class = self._pop_action_class(kwargs)
        if not callable(action_class):
            raise ValueError(f'unknown action {action_class!r}')
        action = action_class(**kwargs)

        # raise an error if action for positional argument does not
        # consume arguments
        if not action.option_strings and action.nargs == 0:
            raise ValueError(f'action {action_name!r} is not valid for positional arguments')

        # raise an error if the action type is not callable
        type_func = self._registry_get('type', action.type, action.type)
        if not callable(type_func):
            raise TypeError(f'{type_func!r} is not callable')

        if type_func is FileType:
            raise TypeError(f'{type_func!r} is a FileType class object, '
                            f'instance of it must be passed')

        # raise an error if the metavar does not match the type
        if hasattr(self, "_get_validation_formatter"):
            formatter = self._get_validation_formatter()
            try:
                formatter._format_args(action, None)
            except TypeError:
                raise ValueError("length of metavar tuple does not match nargs")
        self._check_help(action)
        return self._add_action(action)

#### `` add_argument_group

    add_argument_group(*args, **kwargs)

Source code in `python3.14/argparse.py`

    1576
    1577
    1578
    1579

    def add_argument_group(self, *args, **kwargs):
        group = _ArgumentGroup(self, *args, **kwargs)
        self._action_groups.append(group)
        return group

#### `` add_mutually_exclusive_group

    add_mutually_exclusive_group(**kwargs)

Source code in `python3.14/argparse.py`

    1581
    1582
    1583
    1584

    def add_mutually_exclusive_group(self, **kwargs):
        group = _MutuallyExclusiveGroup(self, **kwargs)
        self._mutually_exclusive_groups.append(group)
        return group

#### `` parse_args

    parse_args(args=None, namespace=None)

Source code in `python3.14/argparse.py`

    2015
    2016
    2017
    2018
    2019
    2020
    2021
    2022
    2023

    def parse_args(self, args=None, namespace=None):
        args, argv = self.parse_known_args(args, namespace)
        if argv:
            msg = _('unrecognized arguments: %s') % ' '.join(argv)
            if self.exit_on_error:
                self.error(msg)
            else:
                raise ArgumentError(None, msg)
        return args

#### `` parse_known_args

    parse_known_args(args=None, namespace=None)

Source code in `python3.14/argparse.py`

    2025
    2026

    def parse_known_args(self, args=None, namespace=None):
        return self._parse_known_args2(args, namespace, intermixed=False)

#### `` convert_arg_line_to_args

    convert_arg_line_to_args(arg_line)

Source code in `python3.14/argparse.py`

    2400
    2401

    def convert_arg_line_to_args(self, arg_line):
        return [arg_line]

#### `` parse_intermixed_args

    parse_intermixed_args(args=None, namespace=None)

Source code in `python3.14/argparse.py`

    2578
    2579
    2580
    2581
    2582
    2583
    2584
    2585
    2586

    def parse_intermixed_args(self, args=None, namespace=None):
        args, argv = self.parse_known_intermixed_args(args, namespace)
        if argv:
            msg = _('unrecognized arguments: %s') % ' '.join(argv)
            if self.exit_on_error:
                self.error(msg)
            else:
                raise ArgumentError(None, msg)
        return args

#### `` parse_known_intermixed_args

    parse_known_intermixed_args(args=None, namespace=None)

Source code in `python3.14/argparse.py`

    2588
    2589
    2590
    2591
    2592
    2593
    2594
    2595
    2596
    2597
    2598
    2599
    2600
    2601
    2602
    2603
    2604

    def parse_known_intermixed_args(self, args=None, namespace=None):
        # returns a namespace and list of extras
        #
        # positional can be freely intermixed with optionals.  optionals are
        # first parsed with all positional arguments deactivated.  The 'extras'
        # are then parsed.  If the parser definition is incompatible with the
        # intermixed assumptions (e.g. use of REMAINDER, subparsers) a
        # TypeError is raised.

        positionals = self._get_positional_actions()
        a = [action for action in positionals
             if action.nargs in [PARSER, REMAINDER]]
        if a:
            raise TypeError('parse_intermixed_args: positional arg'
                            ' with nargs=%s'%a[0].nargs)

        return self._parse_known_args2(args, namespace, intermixed=True)

#### `` format_usage

    format_usage()

Source code in `python3.14/argparse.py`

    2714
    2715
    2716
    2717
    2718

    def format_usage(self):
        formatter = self._get_formatter()
        formatter.add_usage(self.usage, self._actions,
                            self._mutually_exclusive_groups)
        return formatter.format_help()

#### `` print_usage

    print_usage(file=None)

Source code in `python3.14/argparse.py`

    2759
    2760
    2761
    2762

    def print_usage(self, file=None):
        if file is None:
            file = _sys.stdout
        self._print_message(self.format_usage(), file)

#### `` print_help

    print_help(file=None)

Source code in `python3.14/argparse.py`

    2764
    2765
    2766
    2767

    def print_help(self, file=None):
        if file is None:
            file = _sys.stdout
        self._print_message(self.format_help(), file)

#### `` exit

    exit(status=0, message=None)

Source code in `python3.14/argparse.py`

    2781
    2782
    2783
    2784

    def exit(self, status=0, message=None):
        if message:
            self._print_message(message, _sys.stderr)
        _sys.exit(status)

### `` Cmd2AttributeWrapper

    Cmd2AttributeWrapper(attribute)

Wraps a cmd2-specific attribute added to an argparse Namespace.

This makes it easy to know which attributes in a Namespace are arguments from a parser and which were added by cmd2.

Initialize Cmd2AttributeWrapper instances.

Source code in `cmd2/argparse_custom.py`

    1392
    1393
    1394

    def __init__(self, attribute: Any) -> None:
        """Initialize Cmd2AttributeWrapper instances."""
        self.__attribute = attribute

#### `` get

    get()

Get the value of the attribute.

Source code in `cmd2/argparse_custom.py`

    1396
    1397
    1398

    def get(self) -> Any:
        """Get the value of the attribute."""
        return self.__attribute

#### `` set

    set(new_val)

Set the value of the attribute.

Source code in `cmd2/argparse_custom.py`

    1400
    1401
    1402

    def set(self, new_val: Any) -> None:
        """Set the value of the attribute."""
        self.__attribute = new_val

### `` generate_range_error

    generate_range_error(range_min, range_max)

Generate an error message when the the number of arguments provided is not within the expected range.

Source code in `cmd2/argparse_custom.py`

    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325

    def generate_range_error(range_min: int, range_max: float) -> str:
        """Generate an error message when the the number of arguments provided is not within the expected range."""
        err_str = "expected "

        if range_max == constants.INFINITY:
            plural = '' if range_min == 1 else 's'
            err_str += f"at least {range_min}"
        else:
            plural = '' if range_max == 1 else 's'
            if range_min == range_max:
                err_str += f"{range_min}"
            else:
                err_str += f"{range_min} to {range_max}"

        err_str += f" argument{plural}"

        return err_str

### `` set_parser_prog

    set_parser_prog(parser, prog)

Recursively set prog attribute of a parser and all of its subparsers.

Does so that the root command is a command name and not sys.argv[0].

PARAMETER | DESCRIPTION
--- | ---
`parser` | the parser being edited TYPE: `ArgumentParser`
`prog` | new value for the parser's prog attribute TYPE: `str`

Source code in `cmd2/argparse_custom.py`

    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

    def set_parser_prog(parser: argparse.ArgumentParser, prog: str) -> None:
        """Recursively set prog attribute of a parser and all of its subparsers.

        Does so that the root command is a command name and not sys.argv[0].

        :param parser: the parser being edited
        :param prog: new value for the parser's prog attribute
        """
        # Set the prog value for this parser
        parser.prog = prog
        req_args: list[str] = []

        # Set the prog value for the parser's subcommands
        for action in parser._actions:
            if isinstance(action, argparse._SubParsersAction):
                # Set the _SubParsersAction's _prog_prefix value. That way if its add_parser() method is called later,
                # the correct prog value will be set on the parser being added.
                action._prog_prefix = parser.prog

                # The keys of action.choices are subcommand names as well as subcommand aliases. The aliases point to the
                # same parser as the actual subcommand. We want to avoid placing an alias into a parser's prog value.
                # Unfortunately there is nothing about an action.choices entry which tells us it's an alias. In most cases
                # we can filter out the aliases by checking the contents of action._choices_actions. This list only contains
                # help information and names for the subcommands and not aliases. However, subcommands without help text
                # won't show up in that list. Since dictionaries are ordered in Python 3.6 and above and argparse inserts the
                # subcommand name into choices dictionary before aliases, we should be OK assuming the first time we see a
                # parser, the dictionary key is a subcommand and not alias.
                processed_parsers = []

                # Set the prog value for each subcommand's parser
                for subcmd_name, subcmd_parser in action.choices.items():
                    # Check if we've already edited this parser
                    if subcmd_parser in processed_parsers:
                        continue

                    subcmd_prog = parser.prog
                    if req_args:
                        subcmd_prog += " " + " ".join(req_args)
                    subcmd_prog += " " + subcmd_name
                    set_parser_prog(subcmd_parser, subcmd_prog)
                    processed_parsers.append(subcmd_parser)

                # We can break since argparse only allows 1 group of subcommands per level
                break

            # Need to save required args so they can be prepended to the subcommand usage
            if action.required:
                req_args.append(action.dest)

### `` register_argparse_argument_parameter

    register_argparse_argument_parameter(param_name, param_type)

Register a custom argparse argument parameter.

The registered name will then be a recognized keyword parameter to the parser's `add_argument()` function.

An accessor functions will be added to the parameter's Action object in the form of: `get_{param_name}()` and `set_{param_name}(value)`.

PARAMETER | DESCRIPTION
--- | ---
`param_name` | Name of the parameter to add. TYPE: `str`
`param_type` | Type of the parameter to add. TYPE: `type[Any] | None`

Source code in `cmd2/argparse_custom.py`

    738
    739
    740
    741
    742
    743
    744
    745
    746
    747
    748
    749
    750
    751
    752
    753
    754
    755
    756
    757
    758
    759
    760
    761
    762
    763
    764
    765
    766
    767
    768
    769
    770
    771
    772
    773
    774
    775
    776
    777
    778
    779
    780
    781
    782
    783
    784
    785
    786
    787
    788
    789

    def register_argparse_argument_parameter(param_name: str, param_type: type[Any] | None) -> None:
        """Register a custom argparse argument parameter.

        The registered name will then be a recognized keyword parameter to the parser's `add_argument()` function.

        An accessor functions will be added to the parameter's Action object in the form of: ``get_{param_name}()``
        and ``set_{param_name}(value)``.

        :param param_name: Name of the parameter to add.
        :param param_type: Type of the parameter to add.
        """
        attr_name = f'{_CUSTOM_ATTRIB_PFX}{param_name}'
        if param_name in CUSTOM_ACTION_ATTRIBS or hasattr(argparse.Action, attr_name):
            raise KeyError(f'Custom parameter {param_name} already exists')
        if not re.search('^[A-Za-z_][A-Za-z0-9_]*$', param_name):
            raise KeyError(f'Invalid parameter name {param_name} - cannot be used as a python identifier')

        getter_name = f'get_{param_name}'

        def _action_get_custom_parameter(self: argparse.Action) -> Any:
            """Get the custom attribute of an argparse Action.

            This function is added by cmd2 as a method called ``get_<param_name>()`` to ``argparse.Action`` class.

            To call: ``action.get_<param_name>()``

            :param self: argparse Action being queried
            :return: The value of the custom attribute or None if attribute does not exist
            """
            return getattr(self, attr_name, None)

        setattr(argparse.Action, getter_name, _action_get_custom_parameter)

        setter_name = f'set_{param_name}'

        def _action_set_custom_parameter(self: argparse.Action, value: Any) -> None:
            """Set the custom attribute of an argparse Action.

            This function is added by cmd2 as a method called ``set_<param_name>()`` to ``argparse.Action`` class.

            To call: ``action.set_<param_name>(<param_value>)``

            :param self: argparse Action being updated
            :param value: value being assigned
            """
            if param_type and not isinstance(value, param_type):
                raise TypeError(f'{param_name} must be of type {param_type}, got: {value} ({type(value)})')
            setattr(self, attr_name, value)

        setattr(argparse.Action, setter_name, _action_set_custom_parameter)

        CUSTOM_ACTION_ATTRIBS.add(param_name)

### `` set_default_argument_parser_type

    set_default_argument_parser_type(parser_type)

Set the default ArgumentParser class for cmd2's built-in commands.

Since built-in commands rely on customizations made in Cmd2ArgumentParser, your custom parser class should inherit from Cmd2ArgumentParser.

This should be called prior to instantiating your CLI object.

See examples/custom_parser.py.

Source code in `cmd2/argparse_custom.py`

    1410
    1411
    1412
    1413
    1414
    1415
    1416
    1417
    1418
    1419
    1420
    1421

    def set_default_argument_parser_type(parser_type: type[Cmd2ArgumentParser]) -> None:
        """Set the default ArgumentParser class for cmd2's built-in commands.

        Since built-in commands rely on customizations made in Cmd2ArgumentParser,
        your custom parser class should inherit from Cmd2ArgumentParser.

        This should be called prior to instantiating your CLI object.

        See examples/custom_parser.py.
        """
        global DEFAULT_ARGUMENT_PARSER  # noqa: PLW0603
        DEFAULT_ARGUMENT_PARSER = parser_type