Is there a proper, or at least better, way to get which command-line argument was used to set an Namespace argument (attribute) value?
I am currently using something like this:
>>> import argparse
>>>
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--do-a', '-a',
... default=False, action='store_true',
... dest='process_foo',
... help="Do some awesome a to the thing.")
>>> args = parser.parse_args()
>>>
>>> def get_argument(parser, dest):
... for action in parser._actions:
... if action.dest == dest:
... return action.option_strings[0], action.help
... return dest, ''
...
>>> get_argument(parser, 'process_foo')
('--do-a', 'Do some awesome a to the thing.')
This will probably work in 99% of cases; however, if more than one command-line argument can set process_foo
, this wont work, and accessing a 'hidden' instance attribute (parser._actions
) is kludgy at best. Is there a better way to do this?
I'm adding this to a module that all data science processes inherit which logs environment and other things so that we have better reproducibility. The module in question already auto-logs settings, parameters, command-line arguments, etc. but is not very user friendly in some aspects.
Don't worry about the "hiddenness" of _actions
. That is the primary list where references to all Actions
created by add_argument
are stored. You shouldn't fiddle with the list, but you certainly can use it to collect information.
add_argument
creates an Action
object, puts it in _actions
(via the _add_action
method), and also returns it. If you don't like using _actions
you can collect your own list of references, using the object returned by add_argument
.
I see from _add_action
that it also puts flagged actions in a self._option_string_actions
dict, making it easier to pair an option string with its action
.
Parsing does not make any changes to the parser
, its attributes or the actions
. While it has various local variables (in the _parse_known_args
method), the only thing that is changed is the args
Namespace.
It keeps the access to args
as generic as possible, with getattr
, setattr
and hasattr
. This includes setting the defaults at the start of parsing. The parser does not maintain a record of which option-string triggered a particular take_action
and subsequent setattr
. However the __call__
of an Action
does get the string. For the most common 'store_action' the call is
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, values)
I think all defined Action
subclasses use the self.dest
, but user defined ones don't have to. They can even set other namespace attributes, or none (e.g. help doesn't set anything). They could also record the option_string
.
It is also possible to set a namespace attribute without going through the defined Actions. Your dest
test won't help with these.
https://docs.python.org/3/library/argparse.html#parser-defaults
shows how attributes can be set without defining them in an argument. Subcommands shows how this can be used to define a function that will be used with a particular parser.
https://docs.python.org/3/library/argparse.html#the-namespace-object also shows that it's possible to supply a partially initialize namespace.