By design argparse
is supposed to handle the optionals
in any order. They are parsed in the order that they are provided in the command line, but it does not keep any record of that order. positionals
without a keyword are handled in the order that they are defined by the add_argument
definitions.
All attributes are added to the args
Namespace with their defaults at the start of parsing. And it does this in the add_argument
order. In newer Pythons this order is preserved in the args.__dict__
(which vars(args)
also shows).
But in print(args)
, the attributes are displayed in sorted order. usage
also preserves the definition order for optionals
.
So for a sample parser:
In [11]: p.print_usage()
usage: ipython3 [-h] [--foo FOO] [--test TEST] [--aaa AAA] bar
In [12]: args=p.parse_args(['xxx'])
In [13]: args
Out[13]: Namespace(aaa=None, bar='xxx', foo=None, test=None) # sorted
In [14]: vars(args)
Out[14]: {'foo': None, 'bar': 'xxx', 'test': None, 'aaa': None} # definition
In [15]: [a.dest for a in p._actions]
Out[15]: ['help', 'foo', 'bar', 'test', 'aaa'] # definition order
So recording the order of occurrence of the arguments in the command line is, with just argparse
, awkward. In a sense it goes against the spirit of optionals
.
Alternatives:
work directly with the sys.argv
customize the action
class
customize the Namespace
class
One other trick comes to mind:
If the default
is argparse.SUPPRESS
, the default is not added to args
. Then I think the var(args)
order will be the order in which values are parsed and added.
In [16]: for a in p._actions: a.default=argparse.SUPPRESS
In [24]: args=p.parse_args(['--test', 'ttt','xxx','--foo=3'])
In [25]: args
Out[25]: Namespace(bar='xxx', foo='3', test='ttt')
In [26]: vars(args)
Out[26]: {'test': 'ttt', 'bar': 'xxx', 'foo': '3'}
Users may repeat optionals
, with repeats over writing previous values.