Search code examples
pythondocopt

How to use the actual invocation of the program in Python docopt usages?


I am trying to use the docopt module in Python to parse command-line arguments for a script. This script may be called directly, or via a .exe built with PyInstaller. I want to use the actual name of the program in the usage section, which is different based on how the program is called (note that there are lots more mutually exclusive options here, that I have elided away):

> py script.py
  Usage:
      script.py -foo <foo>
      script.py -bar <bar>
      ...

> dist/script.exe
  Usage:
      dist/script.exe -foo <foo>
      dist/script.exe -bar <bar>
      ...

I can do this by using .format() on the string passed to docopt, but this gets ugly and prevents specifying the options as the module docstring:

doc = """
  Usage:
      {} -foo <foo>
      {} -bar <bar>
      ...
  """.format(sys.argv[0], sys.argv[0])
args = docopt(doc, ...)

Is there a better way?


Solution

  • As you can see from inspecting the code in the current master branch, there's not much docopt does to get the string it parses and displays.

    However the DocoptExit exception it raises is what actually shows the usage message so you could monkey patch that as shown in test.py below

    """ Usage:
        PROGRAM --foo <foo>
        PROGRAM --bar <bar>
    """
    import sys, docopt
    
    class MyExit(SystemExit):
        usage = ''
        def __init__(self, message=''):
            usage = self.usage.replace("PROGRAM",sys.argv[0])
            SystemExit.__init__(self, (message + '\n' + usage).strip())
    
    docopt.DocoptExit = MyExit
    args = docopt.docopt(__doc__, version='0.0.1-alpha')
    print str(args)
    

    Sample run

    bash-3.2$ python test.py
    Usage:
        test.py --foo <foo>
        test.py --bar <bar>
    
    bash-3.2$ python test.py --foo foo
    {'--bar': False,
     '--foo': True,
     '<bar>': None,
     '<foo>': 'foo'}