How To Obtain Argparse Subparsers From A Parent Parser (to Inspect Defaults)
Solution 1:
You got it right. But maybe I can explain a few details.
a = parser.add_argument(...)
add_argument
creates an Action
object (or actually a subclass depending on the action
parameter). You can save a pointer to that object in your own environment. But that Action is also collected in the parse._actions
list. That's how the parser
keeps tracks of its arguments.
Reading _actions
should always be safe. Modifying it risks breaking breaking the parser. argument_groups
have access to the list.
subparsers = parser.add_subparsers(dest="command")
is a specialized version of add_argument
, creating and returning a argparse._SubParsersAction
object. subparsers
is that object. And as noted from the earlier answer, you can find it in the _actions
list by searching for the correct subclass. (To the main parser, subparsers
is just another positional argument.)
subparsers
maintains its own specialized dictionary of parsers
, accessible as its choices
attribute. The main parser does not have any record of those sub parsers.
parser_other = subparsers.add_parser("other")
creates a parser, puts it in that choices
map, and returns a reference for your own use (with add_argument
etc). Each sub parser has its own _actions
list. (and its own _defaults
).
Look at the code for the get_defaults
method:
defget_default(self, dest):
for action in self._actions:
if action.dest == dest and action.default isnotNone:
return action.default
return self._defaults.get(dest, None)
It uses the _actions
attribute. And looks at the action.default
attribute of the Action.
self._defaults
is the dictionary updated by the parser.set_defaults
method. That method also copies its parameters to the relevant Action objects. get_defaults
checks that in case the dest
is one of those defaults that isn't tied to a particular Action. https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.set_defaults
I haven't used the parser._subparsers
attribute much. Looking at the parser.add_subparsers
method I see it is actually an argument_group
. Argument_groups are primarily a help
tool, used to group help lines. The relationship between a parser object and its argument_groups is a little tricky, and probably not something you want to use.
Here's an example, with more (too much) detail:
In [22]: parser = argparse.ArgumentParser()
In [23]: sp = parser.add_subparsers(title='subparsers', dest='cmd')
In [24]: sp1 = sp.add_parser('cmd1')
In [25]: sp2 = sp.add_parser('cmd2')
In [26]: parser.print_help()
usage: ipython3 [-h] {cmd1,cmd2} ...
optional arguments:
-h, --help show this help message and exit
subparsers:
{cmd1,cmd2}
In [28]: [a.dest for a in parser._actions]
Out[28]: ['help', 'cmd']
In [29]: parser._action_groups
Out[29]:
[<argparse._ArgumentGroup at 0xaf86bf2c>,
<argparse._ArgumentGroup at 0xaf86bdcc>,
<argparse._ArgumentGroup at 0xac99fa6c>]
In [30]: [g.title for g in parser._action_groups]
Out[30]: ['positional arguments', 'optional arguments', 'subparsers']
In [31]: parser._subparsers
Out[31]: <argparse._ArgumentGroup at 0xac99fa6c>
The _defaults
of _subparsers
is actually the same dictionary as parser._defaults
In [32]: parser.set_defaults(extra='foobar')
In [33]: parser._defaults
Out[33]: {'extra': 'foobar'}
In [34]: parser._subparsers._defaults
Out[34]: {'extra': 'foobar'}
parser._subparsers._actions
is also identical to parser._actions
. But the group does maintain its own list actions (used in the help display).
In [35]: parser._subparsers._group_actions
Out[35]: [_SubParsersAction(option_strings=[], dest='cmd', nargs='A...', const=None,
default=None, type=None, choices=OrderedDict([...]), help=None, metavar=None)]
So you could use parser._subparsers._group_actions[0]
to find the subparsers
action object instead of searching the parsers._actions
list.
In [37]: parser._subparsers._group_actions[0].choices
Out[37]:
OrderedDict([('cmd1',
ArgumentParser(prog='ipython3 cmd1', usage=None, description=None,...)),
('cmd2',
ArgumentParser(prog='ipython3 cmd2', usage=None, description=None,...))])
On second thought, parser._subparsers._group_actions
might not be so useful. If you don't give it a special title, then it is identical to parser._positionals
, the argument group of all positional arguments. So you'd still need to verify the _SubParsersAction
class.
Solution 2:
Based on this answer it looks like it can be done as follows:
subparsers = [
subparser
for action in parser._actions
if isinstance(action, argparse._SubParsersAction)
for _, subparser in action.choices.items()
]
then
subparsers[0].get_default("other_test")
prints "world"
as expected.
Post a Comment for "How To Obtain Argparse Subparsers From A Parent Parser (to Inspect Defaults)"