@@ -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