I have the following code in script.py
:
import argparse
parser = argparse.ArgumentParser()
sp = parser.add_subparsers(dest=\'command\')
sp.default
First a historical note - subparsers were not optional, and they still aren't in Python2. The fact that they are optional in Py3 is something of a bug that was introduced several years ago. There was a change in the test for required arguments, and subparsers (a kind of positional) fell through the cracks. If done right you should have had to explicitly set subparsers as not-required.
Subparsers don't behave like other non-required arguments, ones with nargs='?'
or flagged without the required
parameter.
In any case, your sp.default
defines the value that will be put in the command
dest, but it does not trigger the use of a_parser
. That command='a'
is never 'evaluated'.
The use of parse_known_args
might allow you to devaluate the remaining strings with a_parser
.
Without any arguments, we can do:
In [159]: args, extras = parser.parse_known_args([])
In [160]: args
Out[160]: Namespace(command='a')
In [161]: extras
Out[161]: []
Then conditionally run a_parser
(if command is 'a' but no 'thing')
In [163]: a_parser.parse_args(extras,namespace=args)
Out[163]: Namespace(command='a', thing='thing')
But if I try to include a --thing
value:
In [164]: args, extras = parser.parse_known_args('--thing ouch'.split())
usage: ipython3 [-h] {a,b} ...
ipython3: error: argument command: invalid choice: 'ouch' (choose from 'a', 'b')
It tries to parse the 'ouch' as subparser name. The main parser doesn't known anything about the --thing
argument.
As I explained in the other argparse
question today, the toplevel parser parses the inputs until it finds something that fits the 'subparsers' command (or in this example raises an error). Then it passes the parsing to the subparser. It does not resume parsing after.
Add top level argparse arguments after subparser args
How to Set a Default Subparser using Argparse Module with Python 2.7
My answer to this Py2 request might work for you. I first run a parse_known_args
with a parser that doesn't have subparsers, and conditionally run a second parser that handles the subparsers.
In [165]: firstp = argparse.ArgumentParser()
In [166]: args, extras = firstp.parse_known_args('--thing ouch'.split())
In [167]: args
Out[167]: Namespace()
If extras
doesn't have 'a' or 'b' call a_parser
(alternatively just look at sys.argv[1:]
directly):
In [168]: extras
Out[168]: ['--thing', 'ouch']
In [169]: a_parser.parse_args(extras)
Out[169]: Namespace(thing='ouch')
Or modify extras
to include the missing subparser command:
In [170]: extras = ['a']+extras
In [171]: parser.parse_args(extras)
Out[171]: Namespace(command='a', thing='ouch')
In any case, optional
subparsers is not well developed in argparse
. It's the side effect of a change made a while back, rather than a well thought out feature.