Suppose that I create a parser with a default value for an argument, and then give it a subparser with a further default value for an argument.
In [1]: parser = argparse.ArgumentParser(description='test')
In [2]: parser.add_argument("--test", dest="test", default="hello")
Out[2]: _StoreAction(option_strings=['--test'], dest='test', nargs=None, const=None, default='hello', type=None, choices=None, help=None, metavar=None)
In [3]: parser.get_default("test")
Out[3]: 'hello'
In [4]: subparsers = parser.add_subparsers(dest="command")
In [5]: parser_other = subparsers.add_parser("other")
In [6]: parser_other.add_argument("--other-test", dest="other_test", default="world")
Out[6]: _StoreAction(option_strings=['--other-test'], dest='other_test', nargs=None, const=None, default='world', type=None, choices=None, help=None, metavar=None)
In [7]: parser_other.get_default("other_test")
Out[7]: 'world'
This is all fine. But suppose that I have a function which creates and returns the parent parser parser
from above, but with no direct access to the subparser.
How can I still print out defaults for the subparser arguments? Or get a handle to each subparser separately?
In [8]: parser._subparsers._defaults
Out[8]: {}
In [9]: parser._subparsers.get_default("other_test") # is None
There doesn't appear to be any more attributes or methods from parser._subparsers
or from parser
that could display defaults.
The overall problem is: how to programmatically access subparser defaults when you only have a handle to the parent parser?
You got it right. But maybe I can explain a few details.
add_argument
creates anAction
object (or actually a subclass depending on theaction
parameter). You can save a pointer to that object in your own environment. But that Action is also collected in theparse._actions
list. That's how theparser
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.is a specialized version of
add_argument
, creating and returning aargparse._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 ofparsers
, accessible as itschoices
attribute. The main parser does not have any record of those sub parsers.creates a parser, puts it in that
choices
map, and returns a reference for your own use (withadd_argument
etc). Each sub parser has its own_actions
list. (and its own_defaults
).Look at the code for the
get_defaults
method:It uses the
_actions
attribute. And looks at theaction.default
attribute of the Action.self._defaults
is the dictionary updated by theparser.set_defaults
method. That method also copies its parameters to the relevant Action objects.get_defaults
checks that in case thedest
is one of those defaults that isn't tied to a particular Action. https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.set_defaultsI haven't used the
parser._subparsers
attribute much. Looking at theparser.add_subparsers
method I see it is actually anargument_group
. Argument_groups are primarily ahelp
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:
The
_defaults
of_subparsers
is actually the same dictionary asparser._defaults
parser._subparsers._actions
is also identical toparser._actions
. But the group does maintain its own list actions (used in the help display).So you could use
parser._subparsers._group_actions[0]
to find thesubparsers
action object instead of searching theparsers._actions
list.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 toparser._positionals
, the argument group of all positional arguments. So you'd still need to verify the_SubParsersAction
class.Based on this answer it looks like it can be done as follows:
then
prints
"world"
as expected.