Skip to content

Commit 822bf1a

Browse files
committed
Added add_existing_parser() method to argparse._SubParsersAction.
1 parent 3a76fe6 commit 822bf1a

3 files changed

Lines changed: 57 additions & 16 deletions

File tree

cmd2/argparse_custom.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ def get_choices(self) -> Choices:
231231
sub-parser from a sub-parsers group. See _SubParsersAction_remove_parser for
232232
more details.
233233
234+
``argparse._SubParsersAction.add_existing_parser`` - new function which allows you to attach
235+
an existing ArgumentParser to a sub-parsers group. See _SubParsersAction_add_existing_parser
236+
for more details.
237+
234238
**Added accessor methods**
235239
236240
cmd2 has patched ``argparse.Action`` to include the following accessor methods
@@ -948,7 +952,10 @@ def _ArgumentParser_check_value(_self: argparse.ArgumentParser, action: argparse
948952
############################################################################################################
949953

950954

951-
def _SubParsersAction_remove_parser(self: argparse._SubParsersAction, name: str) -> None: # type: ignore[type-arg] # noqa: N802
955+
def _SubParsersAction_remove_parser( # noqa: N802
956+
self: argparse._SubParsersAction, # type: ignore[type-arg]
957+
name: str,
958+
) -> None:
952959
"""Remove a sub-parser from a sub-parsers group. Used to remove subcommands from a parser.
953960
954961
This function is added by cmd2 as a method called ``remove_parser()`` to ``argparse._SubParsersAction`` class.
@@ -977,6 +984,42 @@ def _SubParsersAction_remove_parser(self: argparse._SubParsersAction, name: str)
977984

978985
setattr(argparse._SubParsersAction, 'remove_parser', _SubParsersAction_remove_parser)
979986

987+
############################################################################################################
988+
# Patch argparse._SubParsersAction to add add_existing_parser function
989+
############################################################################################################
990+
991+
992+
def _SubParsersAction_add_existing_parser( # noqa: N802
993+
self: argparse._SubParsersAction, # type: ignore[type-arg]
994+
name: str,
995+
subcmd_parser: argparse.ArgumentParser,
996+
**add_parser_kwargs: Any,
997+
) -> None:
998+
"""Attach an existing ArgumentParser to a sub-parsers group.
999+
1000+
This is useful when a parser is pre-configured (e.g. by cmd2's subcommand decorator)
1001+
and needs to be attached to a parent parser.
1002+
1003+
This function is added by cmd2 as a method called ``add_existing_parser()``
1004+
to ``argparse._SubParsersAction`` class.
1005+
1006+
:param self: instance of the _SubParsersAction being edited
1007+
:param name: name of the subcommand to add
1008+
:param subcmd_parser: the parser for this new subcommand
1009+
:param add_parser_kwargs: registration-specific kwargs for add_parser() (e.g. help, aliases, deprecated)
1010+
"""
1011+
# Use add_parser to register the subcommand name and any aliases
1012+
self.add_parser(name, **add_parser_kwargs)
1013+
1014+
# Replace the parser created by add_parser() with our pre-configured one
1015+
self._name_parser_map[name] = subcmd_parser
1016+
1017+
# Remap any aliases to our pre-configured parser
1018+
for alias in add_parser_kwargs.get("aliases", []):
1019+
self._name_parser_map[alias] = subcmd_parser
1020+
1021+
1022+
setattr(argparse._SubParsersAction, 'add_existing_parser', _SubParsersAction_add_existing_parser)
9801023

9811024
############################################################################################################
9821025
# Unless otherwise noted, everything below this point are copied from Python's

cmd2/cmd2.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,19 +1129,15 @@ def find_subcommand(
11291129
# Find the argparse action that handles subcommands
11301130
for action in target_parser._actions:
11311131
if isinstance(action, argparse._SubParsersAction):
1132-
# Get the kwargs for add_parser()
1132+
# Get add_parser() kwargs (aliases, help, etc.) defined by the decorator
11331133
add_parser_kwargs = getattr(method, constants.SUBCMD_ATTR_ADD_PARSER_KWARGS, {})
11341134

1135-
# Use add_parser to register the subcommand name and any aliases
1136-
action.add_parser(subcommand_name, **add_parser_kwargs)
1137-
1138-
# Replace the parser created by add_parser() with our pre-configured one
1139-
action._name_parser_map[subcommand_name] = subcmd_parser
1140-
1141-
# Also remap any aliases to our pre-configured parser
1142-
for alias in add_parser_kwargs.get("aliases", []):
1143-
action._name_parser_map[alias] = subcmd_parser
1144-
1135+
# Add the existing parser as a subcommand
1136+
action.add_existing_parser( # type: ignore[attr-defined]
1137+
subcommand_name,
1138+
subcmd_parser,
1139+
**add_parser_kwargs,
1140+
)
11451141
break
11461142

11471143
def _unregister_subcommands(self, cmdset: Union[CommandSet, 'Cmd']) -> None:

cmd2/decorators.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ def as_subcommand_to(
353353
*,
354354
help: str | None = None, # noqa: A002
355355
aliases: Sequence[str] | None = None,
356+
**add_parser_kwargs: Any,
356357
) -> Callable[[ArgparseCommandFunc[CmdOrSet]], ArgparseCommandFunc[CmdOrSet]]:
357358
"""Tag this method as a subcommand to an existing argparse decorated command.
358359
@@ -363,6 +364,7 @@ def as_subcommand_to(
363364
This is passed as the help argument to subparsers.add_parser().
364365
:param aliases: Alternative names for this subcommand. This is passed as the alias argument to
365366
subparsers.add_parser().
367+
:param add_parser_kwargs: other registration-specific kwargs for add_parser() (e.g. deprecated)
366368
:return: Wrapper function that can receive an argparse.Namespace
367369
"""
368370

@@ -373,13 +375,13 @@ def arg_decorator(func: ArgparseCommandFunc[CmdOrSet]) -> ArgparseCommandFunc[Cm
373375
setattr(func, constants.SUBCMD_ATTR_NAME, subcommand)
374376

375377
# Keyword arguments for subparsers.add_parser()
376-
add_parser_kwargs: dict[str, Any] = {}
378+
final_kwargs: dict[str, Any] = dict(add_parser_kwargs)
377379
if help is not None:
378-
add_parser_kwargs['help'] = help
380+
final_kwargs['help'] = help
379381
if aliases:
380-
add_parser_kwargs['aliases'] = aliases[:]
382+
final_kwargs['aliases'] = tuple(aliases)
381383

382-
setattr(func, constants.SUBCMD_ATTR_ADD_PARSER_KWARGS, add_parser_kwargs)
384+
setattr(func, constants.SUBCMD_ATTR_ADD_PARSER_KWARGS, final_kwargs)
383385

384386
return func
385387

0 commit comments

Comments
 (0)