1

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

  • Not specify --files.
  • Specify --files with an argument.

So I'm missing:

  • The option to specify --files without an argument.
  • Distinguish if --files was specified, or if the user specified --files foo.txt
0

1 Answer 1

1

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 
Sign up to request clarification or add additional context in comments.

3 Comments

If I understand correctly in docopt I couldn’t have two such options, because of the use as argument? (But I could in argparse)
I didn't know docopt could even do this until I tried a bunch of things, so this is by no means a definitive answer, but from a quick try with myprog [--files [FNAME]] [--speed [SPD]] [options] as the usage string, it seems to work fine. Same pitfalls apply though - you can specify FNAME and SPD even without --files and --speed. The first argument goes to FNAME, the second to SPD. @TomdeGeus
python dopt.py --files abc.txt --speed 20 gives {'--files': True, '--help': False, '--speed': True, '--version': False, 'FNAME': 'abc.txt', 'SPD': '20'}

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.