@@ -177,14 +177,38 @@ def cmd_wrapper(cmd2_app, statement: Union[Statement, str]):
177177 return arg_decorator
178178
179179
180- def with_argparser_and_unknown_args (argparser : argparse .ArgumentParser , * ,
180+ # noinspection PyProtectedMember
181+ def set_parser_prog (parser : argparse .ArgumentParser , prog : str ):
182+ """
183+ Recursively set prog attribute of a parser and all of its subparsers so that the root command
184+ is a command name and not sys.argv[0].
185+ :param parser: the parser being edited
186+ :param prog: value for the current parsers prog attribute
187+ """
188+ # Set the prog value for this parser
189+ parser .prog = prog
190+
191+ # Set the prog value for the parser's subcommands
192+ for action in parser ._actions :
193+ if isinstance (action , argparse ._SubParsersAction ):
194+
195+ # Set the prog value for each subcommand
196+ for sub_cmd , sub_cmd_parser in action .choices .items ():
197+ sub_cmd_prog = parser .prog + ' ' + sub_cmd
198+ set_parser_prog (sub_cmd_parser , sub_cmd_prog )
199+
200+ # We can break since argparse only allows 1 group of subcommands per level
201+ break
202+
203+
204+ def with_argparser_and_unknown_args (parser : argparse .ArgumentParser , * ,
181205 ns_provider : Optional [Callable [..., argparse .Namespace ]] = None ,
182206 preserve_quotes : bool = False ) -> \
183207 Callable [[argparse .Namespace , List ], Optional [bool ]]:
184208 """A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments with the given
185209 instance of argparse.ArgumentParser, but also returning unknown args as a list.
186210
187- :param argparser : unique instance of ArgumentParser
211+ :param parser : unique instance of ArgumentParser
188212 :param ns_provider: An optional function that accepts a cmd2.Cmd object as an argument and returns an
189213 argparse.Namespace. This is useful if the Namespace needs to be prepopulated with
190214 state data that affects parsing.
@@ -209,27 +233,26 @@ def cmd_wrapper(cmd2_app, statement: Union[Statement, str]):
209233 namespace = ns_provider (cmd2_app )
210234
211235 try :
212- args , unknown = argparser .parse_known_args (parsed_arglist , namespace )
236+ args , unknown = parser .parse_known_args (parsed_arglist , namespace )
213237 except SystemExit :
214238 return
215239 else :
216240 setattr (args , '__statement__' , statement )
217241 return func (cmd2_app , args , unknown )
218242
219- # argparser defaults the program name to sys.argv[0]
220- # we want it to be the name of our command
243+ # argparser defaults the program name to sys.argv[0], but we want it to be the name of our command
221244 command_name = func .__name__ [len (COMMAND_FUNC_PREFIX ):]
222- argparser . prog = command_name
245+ set_parser_prog ( parser , command_name )
223246
224247 # If the description has not been set, then use the method docstring if one exists
225- if argparser .description is None and func .__doc__ :
226- argparser .description = func .__doc__
248+ if parser .description is None and func .__doc__ :
249+ parser .description = func .__doc__
227250
228251 # Set the command's help text as argparser.description (which can be None)
229- cmd_wrapper .__doc__ = argparser .description
252+ cmd_wrapper .__doc__ = parser .description
230253
231254 # Set some custom attributes for this command
232- setattr (cmd_wrapper , CMD_ATTR_ARGPARSER , argparser )
255+ setattr (cmd_wrapper , CMD_ATTR_ARGPARSER , parser )
233256 setattr (cmd_wrapper , CMD_ATTR_PRESERVE_QUOTES , preserve_quotes )
234257
235258 return cmd_wrapper
@@ -238,13 +261,13 @@ def cmd_wrapper(cmd2_app, statement: Union[Statement, str]):
238261 return arg_decorator
239262
240263
241- def with_argparser (argparser : argparse .ArgumentParser , * ,
264+ def with_argparser (parser : argparse .ArgumentParser , * ,
242265 ns_provider : Optional [Callable [..., argparse .Namespace ]] = None ,
243266 preserve_quotes : bool = False ) -> Callable [[argparse .Namespace ], Optional [bool ]]:
244267 """A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments
245268 with the given instance of argparse.ArgumentParser.
246269
247- :param argparser : unique instance of ArgumentParser
270+ :param parser : unique instance of ArgumentParser
248271 :param ns_provider: An optional function that accepts a cmd2.Cmd object as an argument and returns an
249272 argparse.Namespace. This is useful if the Namespace needs to be prepopulated with
250273 state data that affects parsing.
@@ -268,27 +291,26 @@ def cmd_wrapper(cmd2_app, statement: Union[Statement, str]):
268291 namespace = ns_provider (cmd2_app )
269292
270293 try :
271- args = argparser .parse_args (parsed_arglist , namespace )
294+ args = parser .parse_args (parsed_arglist , namespace )
272295 except SystemExit :
273296 return
274297 else :
275298 setattr (args , '__statement__' , statement )
276299 return func (cmd2_app , args )
277300
278- # argparser defaults the program name to sys.argv[0]
279- # we want it to be the name of our command
301+ # argparser defaults the program name to sys.argv[0], but we want it to be the name of our command
280302 command_name = func .__name__ [len (COMMAND_FUNC_PREFIX ):]
281- argparser . prog = command_name
303+ set_parser_prog ( parser , command_name )
282304
283305 # If the description has not been set, then use the method docstring if one exists
284- if argparser .description is None and func .__doc__ :
285- argparser .description = func .__doc__
306+ if parser .description is None and func .__doc__ :
307+ parser .description = func .__doc__
286308
287309 # Set the command's help text as argparser.description (which can be None)
288- cmd_wrapper .__doc__ = argparser .description
310+ cmd_wrapper .__doc__ = parser .description
289311
290312 # Set some custom attributes for this command
291- setattr (cmd_wrapper , CMD_ATTR_ARGPARSER , argparser )
313+ setattr (cmd_wrapper , CMD_ATTR_ARGPARSER , parser )
292314 setattr (cmd_wrapper , CMD_ATTR_PRESERVE_QUOTES , preserve_quotes )
293315
294316 return cmd_wrapper
@@ -2396,7 +2418,7 @@ def _alias_list(self, args: argparse.Namespace) -> None:
23962418 "An alias is a command that enables replacement of a word by another string." )
23972419 alias_epilog = ("See also:\n "
23982420 " macro" )
2399- alias_parser = Cmd2ArgumentParser (description = alias_description , epilog = alias_epilog , prog = 'alias' )
2421+ alias_parser = Cmd2ArgumentParser (description = alias_description , epilog = alias_epilog )
24002422
24012423 # Add subcommands to alias
24022424 alias_subparsers = alias_parser .add_subparsers (dest = 'subcommand' )
@@ -2573,7 +2595,7 @@ def _macro_list(self, args: argparse.Namespace) -> None:
25732595 "A macro is similar to an alias, but it can contain argument placeholders." )
25742596 macro_epilog = ("See also:\n "
25752597 " alias" )
2576- macro_parser = Cmd2ArgumentParser (description = macro_description , epilog = macro_epilog , prog = 'macro' )
2598+ macro_parser = Cmd2ArgumentParser (description = macro_description , epilog = macro_epilog )
25772599
25782600 # Add subcommands to macro
25792601 macro_subparsers = macro_parser .add_subparsers (dest = 'subcommand' )
0 commit comments