Suppose that I have
My program
Usage:
myprog [options]
Options:
-h, --help Show this screen.
--version Show version.
--files=<arg> Files. [default: foo.txt]
I would like to distinguish in my code:
--files
not specified.--files
specified, but with no argument to accept the default.--files myfile
, i.e. --files
specified with custom argument.With the current docstring I can either
--files
.--files
with an argument.So I'm missing:
--files
without an argument.--files
was specified, or if the user specified --files foo.txt
You will need to specify the --files
argument in the main usage string. For example:
# dopt.py
from docopt import docopt
dstr = """My program
Usage:
myprog [--files [FNAME]] [options]
Options:
-h, --help Show this screen.
--version Show version.
"""
if __name__ == '__main__':
arguments = docopt(dstr)
print(arguments)
This essentially makes --files
a true/false argument and adds another argument FNAME
to hold the file name.
Usage:
$ python dopt.py
{'--files': False,
'--help': False,
'--version': False,
'FNAME': None}
$ python dopt.py --files
{'--files': True,
'--help': False,
'--version': False,
'FNAME': None}
$ python dopt.py --files abc.txt
{'--files': True,
'--help': False,
'--version': False,
'FNAME': 'abc.txt'}
Then, you can use the value of --files
and FNAME
from the returned dict
to infer what to do:
if not arguments['--files']:
print("Not using files")
elif not arguments['FNAME']:
print("Using default file foo.txt")
else:
print(f"Using file {arguments['FNAME']}")
A pitfall to remember: you can also specify FNAME
independently of --files
. So this also works, and it might interfere with other arguments, so be sure to test all combinations thoroughly:
$ python dopt.py abc.txt
{'--files': False,
'--help': False,
'--version': False,
'FNAME': 'abc.txt'}
Not using files
Personally, I prefer using argparse
because it's less ambiguous. It builds the doc string from the prescribed arguments, not the other way round.
In argparse
, an argument can have a default value and you can specify that it can take zero or one argument using nargs="?"
. Then, you can specify a const="foo.txt"
value which the argument will take if no values are given. For example:
# dopt.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--files", required=False, default=None, nargs="?", const="foo.txt")
p = parser.parse_args()
print(p)
And running this:
$ python dopt.py
Namespace(files=None)
$ python dopt.py --files
Namespace(files='foo.txt')
$ python dopt.py --files abc.txt
Namespace(files='abc.txt')
And it even handles the "no --files
" case correctly:
$ python dopt.py abc.txt
usage: dopt.py [-h] [--files [FILES]]
dopt.py: error: unrecognized arguments: abc.txt