Skip to content

Commit ad4a3de

Browse files
committed
More updates to update_prog().
1 parent e07f44e commit ad4a3de

3 files changed

Lines changed: 12 additions & 17 deletions

File tree

cmd2/argparse_custom.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,7 @@ def _get_subparsers_action(self) -> argparse._SubParsersAction: # type: ignore[
893893
def update_prog(self, prog: str) -> None:
894894
"""Recursively update the prog attribute of this parser and all of its subparsers.
895895
896-
:param prog: new value for the parser's prog attribute
896+
:param prog: new value for this parser's prog attribute
897897
"""
898898
# Set the prog value for this parser
899899
self.prog = prog
@@ -916,26 +916,21 @@ def update_prog(self, prog: str) -> None:
916916
formatter.add_usage(self.usage, positionals, self._mutually_exclusive_groups, '')
917917
action._prog_prefix = formatter.format_help().strip()
918918

919-
# The keys of action.choices are subcommand names as well as subcommand aliases.
920-
# The aliases point to the same parser as the actual subcommand. We want to avoid
921-
# placing an alias into a parser's prog value. Unfortunately there is nothing about
922-
# an action.choices entry which tells us it's an alias. In most cases we can filter out
923-
# the aliases by checking the contents of action._choices_actions. This list only contains
924-
# help information and names for the subcommands and not aliases. However, subcommands
925-
# without help text won't show up in that list. Since dictionaries are ordered and
926-
# argparse inserts the subcommand name into choices dictionary before aliases, we should
927-
# be OK assuming the first time we see a parser, the dictionary key is a subcommand and
928-
# not an alias.
929-
processed_parsers: list[argparse.ArgumentParser] = []
919+
# Note: action.choices contains both subcommand names and aliases.
920+
# To ensure subcommands (and not aliases) are used in 'prog':
921+
# 1. We can't use action._choices_actions because it excludes subcommands without help text.
922+
# 2. Since dictionaries are ordered and argparse inserts the primary name before aliases,
923+
# we assume the first time we encounter a parser, the key is the true subcommand name.
924+
updated_parsers: set[argparse.ArgumentParser] = set()
930925

931926
# Set the prog value for each subcommand's parser
932927
for subcmd_name, subcmd_parser in action.choices.items():
933-
if subcmd_parser in processed_parsers:
928+
if subcmd_parser in updated_parsers:
934929
continue
935930

936931
subcmd_prog = f"{action._prog_prefix} {subcmd_name}"
937932
subcmd_parser.update_prog(subcmd_prog) # type: ignore[attr-defined]
938-
processed_parsers.append(subcmd_parser)
933+
updated_parsers.add(subcmd_parser)
939934

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

tests/test_argparse.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ def base_helpless(self, args) -> None:
335335
parser_bar.set_defaults(func=base_bar)
336336

337337
# create the parser for the "helpless" subcommand
338-
# This subcommand has aliases and no help text. It exists to prevent changes to set_parser_prog() which
338+
# This subcommand has aliases and no help text. It exists to prevent changes to update_prog() which
339339
# use an approach which relies on action._choices_actions list. See comment in that function for more
340340
# details.
341341
parser_helpless = base_subparsers.add_parser('helpless', aliases=['helpless_1', 'helpless_2'])
@@ -446,7 +446,7 @@ def test_subcommand_invalid_help(subcommand_app) -> None:
446446

447447

448448
def test_add_another_subcommand(subcommand_app) -> None:
449-
"""This tests makes sure set_parser_prog() sets _prog_prefix on every _SubParsersAction so that all future calls
449+
"""This tests makes sure update_prog() sets _prog_prefix on every _SubParsersAction so that all future calls
450450
to add_parser() write the correct prog value to the parser being added.
451451
"""
452452
base_parser = subcommand_app._command_parsers.get(subcommand_app.do_base)

tests/test_argparse_subcommands.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def base_helpless(self, args) -> None:
4545
parser_bar.set_defaults(func=base_bar)
4646

4747
# create the parser for the "helpless" subcommand
48-
# This subcommand has aliases and no help text. It exists to prevent changes to set_parser_prog() which
48+
# This subcommand has aliases and no help text. It exists to prevent changes to update_prog() which
4949
# use an approach which relies on action._choices_actions list. See comment in that function for more
5050
# details.
5151
parser_helpless = base_subparsers.add_parser('helpless', aliases=['helpless_1', 'helpless_2'])

0 commit comments

Comments
 (0)