Skip to content

Commit a8b4dba

Browse files
committed
Refactored Cmd._build_parser() to accept a prog value.
Restored call to _set_parser_prog() in with_argparser() to preserve backward compatibility with cmd2 2.0 family.
1 parent 0ff58ac commit a8b4dba

File tree

2 files changed

+35
-25
lines changed

2 files changed

+35
-25
lines changed

cmd2/cmd2.py

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -254,17 +254,11 @@ def get(self, command_method: CommandFunc) -> Optional[argparse.ArgumentParser]:
254254
command = command_method.__name__[len(COMMAND_FUNC_PREFIX) :]
255255

256256
parser_builder = getattr(command_method, constants.CMD_ATTR_ARGPARSER, None)
257-
parent = self._cmd.find_commandset_for_command(command) or self._cmd
258-
parser = self._cmd._build_parser(parent, parser_builder)
259-
if parser is None:
257+
if parser_builder is None:
260258
return None
261259

262-
# argparser defaults the program name to sys.argv[0], but we want it to be the name of our command
263-
from .decorators import (
264-
_set_parser_prog,
265-
)
266-
267-
_set_parser_prog(parser, command)
260+
parent = self._cmd.find_commandset_for_command(command) or self._cmd
261+
parser = self._cmd._build_parser(parent, parser_builder, command)
268262

269263
# If the description has not been set, then use the method docstring if one exists
270264
if parser.description is None and hasattr(command_method, '__wrapped__') and command_method.__wrapped__.__doc__:
@@ -758,24 +752,39 @@ def register_command_set(self, cmdset: CommandSet) -> None:
758752
def _build_parser(
759753
self,
760754
parent: CommandParent,
761-
parser_builder: Optional[
762-
Union[
763-
argparse.ArgumentParser,
764-
Callable[[], argparse.ArgumentParser],
765-
StaticArgParseBuilder,
766-
ClassArgParseBuilder,
767-
]
755+
parser_builder: Union[
756+
argparse.ArgumentParser,
757+
Callable[[], argparse.ArgumentParser],
758+
StaticArgParseBuilder,
759+
ClassArgParseBuilder,
768760
],
769-
) -> Optional[argparse.ArgumentParser]:
770-
parser: Optional[argparse.ArgumentParser] = None
761+
prog: str,
762+
) -> argparse.ArgumentParser:
763+
"""Build argument parser for a command/subcommand.
764+
765+
:param parent: CommandParent object which owns the parser
766+
:param parser_builder: method used to build the parser
767+
:param prog: prog value to set in new parser
768+
:return: new parser
769+
:raises TypeError: if parser_builder is invalid type
770+
"""
771771
if isinstance(parser_builder, staticmethod):
772772
parser = parser_builder.__func__()
773773
elif isinstance(parser_builder, classmethod):
774-
parser = parser_builder.__func__(parent if not None else self) # type: ignore[arg-type]
774+
parser = parser_builder.__func__(parent.__class__)
775775
elif callable(parser_builder):
776776
parser = parser_builder()
777777
elif isinstance(parser_builder, argparse.ArgumentParser):
778778
parser = copy.deepcopy(parser_builder)
779+
else:
780+
raise TypeError(f"Invalid type for parser_builder: {type(parser_builder)}")
781+
782+
from .decorators import (
783+
_set_parser_prog,
784+
)
785+
786+
_set_parser_prog(parser, prog)
787+
779788
return parser
780789

781790
def _install_command_function(self, command_func_name: str, command_method: CommandFunc, context: str = '') -> None:
@@ -963,12 +972,7 @@ def find_subcommand(action: argparse.ArgumentParser, subcmd_names: list[str]) ->
963972

964973
target_parser = find_subcommand(command_parser, subcommand_names)
965974

966-
subcmd_parser = cast(argparse.ArgumentParser, self._build_parser(cmdset, subcmd_parser_builder))
967-
from .decorators import (
968-
_set_parser_prog,
969-
)
970-
971-
_set_parser_prog(subcmd_parser, f'{command_name} {subcommand_name}')
975+
subcmd_parser = self._build_parser(cmdset, subcmd_parser_builder, f'{command_name} {subcommand_name}')
972976
if subcmd_parser.description is None and method.__doc__:
973977
subcmd_parser.description = strip_doc_annotations(method.__doc__)
974978

cmd2/decorators.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,12 @@ def cmd_wrapper(*args: Any, **kwargs: dict[str, Any]) -> Optional[bool]:
393393

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

396+
if isinstance(parser, argparse.ArgumentParser):
397+
# Set parser's prog value for backward compatibility within the cmd2 2.0 family.
398+
# This will be removed in cmd2 3.0 since we never reference this parser's prog value.
399+
# We only set prog on the deep copy of this parser created in Cmd._build_parser().
400+
_set_parser_prog(parser, command_name)
401+
396402
# Set some custom attributes for this command
397403
setattr(cmd_wrapper, constants.CMD_ATTR_ARGPARSER, parser)
398404
setattr(cmd_wrapper, constants.CMD_ATTR_PRESERVE_QUOTES, preserve_quotes)

0 commit comments

Comments
 (0)