Skip to content

Commit 179e119

Browse files
committed
Fixing logic in update_prog().
1 parent 043d225 commit 179e119

1 file changed

Lines changed: 18 additions & 12 deletions

File tree

cmd2/argparse_custom.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -902,15 +902,24 @@ def update_prog(self, prog: str) -> None:
902902
# This parser has no subcommands
903903
return
904904

905-
# Required args which come before the subcommand
906-
req_args: list[str] = []
905+
# argparse includes positional arguments that appear before the subcommand in its
906+
# subparser prog strings. We must track these while iterating through actions.
907+
positionals: list[str] = []
908+
909+
# Use a formatter to get the same argument strings that argparse uses.
910+
formatter = self._get_formatter()
907911

908912
# Set the prog value for the parser's subcommands
909913
for action in self._actions:
910914
if isinstance(action, argparse._SubParsersAction):
915+
# Build the prefix that should be prepended to subcommand names
916+
prefix = self.prog
917+
if positionals:
918+
prefix += " " + " ".join(positionals)
919+
911920
# Set the _SubParsersAction's _prog_prefix value. That way if its add_parser()
912921
# method is called later, the correct prog value will be set on the parser being added.
913-
action._prog_prefix = self.prog
922+
action._prog_prefix = prefix
914923

915924
# The keys of action.choices are subcommand names as well as subcommand aliases.
916925
# The aliases point to the same parser as the actual subcommand. We want to avoid
@@ -922,26 +931,23 @@ def update_prog(self, prog: str) -> None:
922931
# argparse inserts the subcommand name into choices dictionary before aliases, we should
923932
# be OK assuming the first time we see a parser, the dictionary key is a subcommand and
924933
# not an alias.
925-
processed_parsers = []
934+
processed_parsers: list[argparse.ArgumentParser] = []
926935

927936
# Set the prog value for each subcommand's parser
928937
for subcmd_name, subcmd_parser in action.choices.items():
929938
if subcmd_parser in processed_parsers:
930939
continue
931940

932-
subcmd_prog = self.prog
933-
if req_args:
934-
subcmd_prog += " " + " ".join(req_args)
935-
subcmd_prog += " " + subcmd_name
936-
subcmd_parser.update_prog(subcmd_prog)
941+
subcmd_prog = f"{prefix} {subcmd_name}"
942+
subcmd_parser.update_prog(subcmd_prog) # type: ignore[attr-defined]
937943
processed_parsers.append(subcmd_parser)
938944

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

942-
# Need to save required args so they can be prepended to the subcommand usage
943-
if action.required:
944-
req_args.append(action.dest)
948+
# Save positional argument
949+
if not action.option_strings:
950+
positionals.append(formatter._format_args(action, action.dest))
945951

946952
def _find_parser(self, subcommand_path: Iterable[str]) -> 'Cmd2ArgumentParser':
947953
"""Find a parser in the hierarchy based on a sequence of subcommand names.

0 commit comments

Comments
 (0)