EzDev.org

docopt

Pythonic command line arguments parser, that will make you smile docopt—language for description of command-line interfaces


docopt arguments parsing: how to avoid spaghetti code?

It's the first time I use docopt and I'm struggling with the args parsing for a small command line program I'm trying to realize.

    '''Usage:
    ovh_mails.py list [--ugly]
    ovh_mails.py add (<address> [--pswd=<password>][--description=<description>] | --file <filename>)
    ovh_mails.py remove (<address> | --file <filename>)
    ovh_mails.py (-h | --help)

Arguments:
    <password>                        Password to access the mailbox (if not provided it's random generated)
    <filename>                        Name of the files to process. Check README to see how to format it

Options: 
    -h, --help                        Show this help message
    -u, --ugly                        Print without nice tables
    -p, --pswd=<password>  Set the password to the one provided

Commands:
    list                              list all the email addresses currently configured
    add                               add one or more (configured in <filename>) email addresses
    remove                            remove one ore more (configured in <filename>) email addresses'''

The parsing of the arguments I have at the moment is:

if __name__ == '__main__':
args = docopt(__doc__)
#Validate args ---- TODO

# 'List' command parsing
if args['list']:
    if args['--ugly']:
        eman = EmailManager(niceoutput=False)
    else:
        eman = EmailManager()
    eman.list_emails()
# 'Add' command parsing
elif args['add']:
    if args['<address>']:
        eman = EmailManager()
        emails = (
                  {
                   'address': args['<address>'],
                   'password': None,
                   'description': None,
                   },
                  )
        if args['--description']:
            emails[0]['description'] = args['<description>']
        if args['--pswd']:
            emails[0]['password'] = args['<password>']
    if args['--file']:
        raise NotImplemented


    eman.add_emails(emails)


# 'remove' command parsing       
elif args['remove']:
    if args['<address>']:
        eman = EmailManager()
        emails = (
                  {
                   'address': args['<address>'],
                   },
                  )
    eman.remove_emails(emails)
    if args['--file']:
        raise NotImplemented

Is there a better, more pythonic ore more elegant way, to do this?


Source: (StackOverflow)

Parsing Docopt commands into components

I have a command which I use in docopt which looks like this:

cli.py argument_test (--required_id=<required_id> | --required_name=<required_name>) (--required=<required>) (--repeat=<repeat>)... (--required_named=<required_named> | <required_named>) (--thisflag | --thatflag) [--optionalflag] [--optional_named=<optional_named>]

I need some way of parsing this into a structure which could present the information like this (on a web page for example)

RequiredArguments: required_id or requiredname, required, repeat(list), required_named, thisflag(bool) or thatflag(bool)

OptionalArguments: optionalflag(bool), optionalnamed

bad ms paint image of what command might look like on a webpage

Docopt parses the pattern into an object with children which looks like the following:

Required(Command('argument_test', False), Required(Either(Option(None, '--required_id', 1, None), Option(None, '--required_name', 1, None))), Required(Option(None, '--required', 1, None)), OneOrMore(Required(Option(None, '--repeat', 1, []))), Required(Either(Option(None, '--required_named', 1, None), Argument('<required_named>', None))), Required(Either(Option(None, '--thisflag', 0, False), Option(None, '--thatflag', 0, False))), Optional(Option(None, '--optionalflag', 0, False)), Optional(Option(None, '--optional_named', 1, None)))))

I'm pretty stumped where to go from here in how I should go about parsing these. Any advice is appreciated!


Source: (StackOverflow)

Docopt - Golang - How to access repeated arguments?

I'm trying to understand how to access multiple input arguments from docopt.Parse() output.

Example:

package main

import (
    "fmt"
    "github.com/docopt/docopt-go"
)

func main() {
    usage := `blah.go

  Usage: 
    blah.go read <file> ...
    blah.go -h | --help | --version`

    arguments, _ := docopt.Parse(usage, nil, true, "blah 1.0", false)
    x := arguments["<file>"]
    fmt.Println(x)
    fmt.Println(x)
}

Command Line:

$ go run blah.go read file1 file2
[file1 file2]
[file1 file2]

I'd like to print out only file1 or file2.

When I try adding:

fmt.Println(x[0])

I get the following error:

$ go run blah.go read file1 file2
# command-line-arguments
./blah.go:19: invalid operation: x[0] (index of type interface {})

https://github.com/docopt/docopt.go


Source: (StackOverflow)

How can text in the options configuration of Docopt be wrapped?

I have a few detailed option specifications in the docstring used for configuration of Docopt. Some of the items are quite lengthy. Is there a way of wrapping the text to make it more legible or to make it fit to a line width more easily?

Let's say the relevant bit of text in the docstring is the following:

Usage:
    program [options]

Options:
    -h, --help                      Show this help message.
    -c, --configuration=CONF        Configuration (file) [default: None]
    -f, --files=FILESLIST           Comma-delimited list of input data files [default: 169888_ttH_el.root]
    -v, --variables=VARIABLESLIST   Comma-delimited list of variables to plot [default: trk_pt]
    -t, --tree=TREE                 Tree in input data files [default: mini]
    -u, --username=USERNAME         Username
    -t, --topanalysis=DIRECTORY     Directory of TopRootCore or TopAnalysis [default: /home/user/Dropbox/TopAnalysis]
    -s, --superlongoption=TEST      This is a very long option that requires a bit of text to explain it. [default: 101001011011101010010100110101010]
    --version                       Show the version and exit.

Would it be possible wrap the text in a style something like the following?

Usage:
    program [options]

Options:
    -h, --help                      Show this help message.
    -c, --configuration=CONF        Configuration (file) [default: None]
    -f, --files=FILESLIST           Comma-delimited list of input data files
                                    [default: 169888_ttH_el.root]
    -v, --variables=VARIABLESLIST   Comma-delimited list of variables to plot
                                    [default: trk_pt]
    -t, --tree=TREE                 Tree in input data files [default: mini]
    -u, --username=USERNAME         Username
    -t, --topanalysis=DIRECTORY     Directory of TopRootCore or TopAnalysis
                                    [default: /home/user/Dropbox/TopAnalysis]
    -s, --superlongoption=TEST      This is a very long option that requires a
                                    bit of text to explain it.
                                    [default: 101001011011101010010100110101010]
    --version                       Show the version and exit.

Source: (StackOverflow)

docopt in python3 argumentes definitions

I'm trying to use docopt.

i want to call my program like this:

python3 my_script -p argum1 -d argum2 -u faculm

The -u is not mandatory, but "-p" and "-d" are mandatory.

i have allready made this:

""" 
Usage:
    passwdcrack.py -p=<password>, 
    passwdcrack.py -d=<dicionario>       
    passwdcrack.py [-u=<user>]        

Options:
    -h --help       mostra este ecrã
    --version       Mostra a versão
    -p=<password>   indicar o caminho para o ficheiro tipo */etc/shadow
    -d=<dicionario> indicar o caminho para o ficheiro com lista de Passw
    -u=<user>       indica o utilizador para ser analisado
"""
import docopt

if __name__ == '__main__':
    arguments = docopt.docopt(__doc__, version='0.0001')
    print (arguments)

but when i call it it gives me this:

$ python3 passwdcrack.py -d papa -d pfpf -u madona Traceback (most recent call last): File "passwdcrack.py", line 17, in arguments = docopt.docopt(doc, version='0.0001') AttributeError: 'module' object has no attribute 'docopt'

Can some one help me? thanks


Source: (StackOverflow)

How do I make a custom system command out of my Python program?

If I have a command-line program written in Python (specifically using docopt which uses argparse I think), and I want to run it as my-program command1 args instead of python my-program.py command1 args, what do I have to do? For now, this is for Debian-based OS (e.g Ubuntu).

I normally make my module a package so I already have setup.py but that doesn't grant me a free command.

Thanks!


EDIT

@pyrospade gave a very good link below. I am going to share my result.

Suppose we have

top-level-directory
    setup.py
    package/
         __init__.py
         cli.py

You can use scripts=['package/cli.py'] if you want to access cli.py in the shell.

If you want to run as my-cli, you can use

entry_points={
    'console_scripts':
        ['my-cli=package.cli:main']
}

Since I use docopt, I have this

def dispatcher(...)
def fun1(....)


def main():    
    arguments = docopt(COMMAND, version="xxxx")
    dispatcher(arguments)

if __name__ == '__main__':
    main()

You can even put it under __init__.py by saying ['my-cli=package:main'] but again, you need a function called main(). But you can name it whatever you want. Just saying.


Source: (StackOverflow)

docopt positional arguments not working

When followint the docopt README, I would expect the following file to produce some valid output:

#!/usr/bin/env python
"""Example file.

Usage:

  test_docopt.py test
  test_docopt.py (-h | --help)
  test_docopt.py --version

Options:
  -h --help                       Show this screen
  --version                       Show version.

"""

import pkg_resources
pkg_resources.require("docopt==0.6.1")
from docopt import docopt

if __name__ == '__main__':
    args = docopt(__doc__, version="Extend limb profiles 0.1")
    print(args)

However, when I call test_docopt.py, I only get a meaningless/empty Usage statement:

$ python test_docopt.py test
Usage:

My two questions are:

  • Why is docopt apparently failing to recognize the test command?
  • Why isn't the Usage pattern filled with the actual usage pattern?

Source: (StackOverflow)

Make docopt parse arguments containing spaces in unit tests

I am having issues getting docopt to parse arguments that contain spaces into a proper dictionary object for use with my unit tests.

Here is the code I'm currently using to construct the argument list for docopt to parse:

testargs = []

def clear_args():
    testargs[:] = []
    return

def add_testfiles(file1='defaultfile1.txt', file2='defaultfile2.txt'):
    clear_args()
    testargs.append('--debug')
    testargs.append(file1)
    testargs.append(file2)
    return

def parse_args(optlist):
    argstr = ' '.join(optlist)
    return docopt(downpost.__doc__, argv=argstr)

The code I am writing unit tests for has 2 tests that are separately given the following arguments:

-t <title>  # <title> can be any string (with spaces) inside quotation marks
"A Filename with Spaces.txt"  # any filename as long as it's in quotation marks

To add, for example, the -t argument, I would do:

def test_exampleunittest(self):
    add_testfiles()
    testargs.append('-t "This is the title I want"')
    self.args = parse_args(testargs)
    self.post = myPythonScript.process(self.args)
    self.assertEqual(self.post['Subject'], 'This is the title I want')

If I run the script I'm testing by itself with the said arguments, they are accepted without any problems and the output is as expected.

However, if I run the unit tests which use arguments containing spaces, I get the following:

DocoptExit: Usage: myPythonScript [options] <file_1> <file_2>

Other unit tests that require the same dict object (containing the same arguments) work fine.

What should I change in my code to make docopt parse the arguments as it normally does?


Source: (StackOverflow)

How to prevent docopt from swallowing an option?

I'm trying to create a command-line interface using docopt. Here is a simplified version of my file:

#!/usr/bin/env python
"""
Test program.

Usage:
  test.py [options]

Options:
  -a <input>    
  -b
  -c
  -d
"""
import docopt

print docopt.docopt(__doc__)

I essentially want to be able to specify any of the options, in any order. However, if I forget to specify the argument for the -a flag, then I get an output like this:

$ python test.py -a -b -c
{"-a": "-b",
 "-b": False,
 "-c": True,
 "-d": False}

Docopt is treating the -b flag as the argument for the -a flag, instead of rejecting the input as invalid. Is there some easy way to detect this, or make docopt refuse to accept this sort of malformed input?


Source: (StackOverflow)

How to pass more than one arguments with docopt

I want to pass two mandatory argument, one optional argument to my program using docopt. The code I am using is:

"""Setup

Usage: myprog.py server_name config [--help] [options] 

Arguments:
    SERVER_NAME        Server Name (a1, a2)
    CONFIG             Config file with full path

Options:
    -h --help
    -r --start      Start the server if yes [default: 'no']
"""

from docopt import docopt

class ServerSetup(object):
    def __init__(self, server_name, config_file, start_server):
        self.server = server_name
        self.config = config_file
        self.start_server = start_server

    def print_msg(self):
        print self.server
        print self.config
        print self.start_server

if __name__ == '__main__':
    args = docopt(__doc__)
    setup = ServerSetup(server_name=args['SERVER_NAME']),
                        config=args['CONFIG']
                        start_rig=args['-r'])
    setup.print_msg()

$python myprog.py a1 /abc/file1.txt

When I run above program using above command, I get error message displaying usage that I've written. What is going wrong here, how can I use more than one 'Arguments'?


Source: (StackOverflow)

how to get docopt default args when script loaded as module

I'm working on a large project with multiple authors, so I'm trying to avoid making too many changes. I have one script which uses docopt to pass options, and sets some of them to default values.

I loaded a class from that script by importing it as a module, but when I called a method, it failed because it expected a default option to be set by docopt, of which there are many. Is there a way to pull in the default options from docopt?


Source: (StackOverflow)

Docopt accepts multi args in middle?

I want my script accepts command line args like "cp" command does:

'''
Usage:
cp.py <source>... <directory>
cp.py -t <directory> <source>...
cp.py -s <source>... -t <directory>
'''

Those command line

$ python cp.py src/path/1 src/path/2 target/path
$ python cp.py -t target/path src/path/1 src/path/2
$ python cp.py -s src/path/1 src/path/2 -t target/path

will get the same result:

{'<source>':['src/path/1', 'src/path/2'],'<directory>': 'target/path'}

Thx very much. And sorry for my English:)


Source: (StackOverflow)

Optional parameter not working on schema

I am adding validation using schema for CLI that uses docopt, but I cannot seem to get optional to work. I want to validate that:

  • the input file exists
  • valid options are used
  • if the PATH is added that the directory exists.

Here is app so far

"""DVget

Usage:
    DVget [-s] FILE [PATH]

    Process a file, return data based on selection
    and write results to PATH/output-file

Arguments:
    FILE        specify input file
    PATH        specify output directory (default: ./)

Options:
    -s          returns sections
    -p          returns name-sets
    -m          returns modules

"""
import os

from docopt import docopt
from schema import Schema, And, Use, Optional, SchemaError

# START OF SCRIPT
if __name__ == "__main__":

    arguments = docopt(__doc__, version="0.1")

    #print(arguments)

    schema = Schema({
        'FILE': [Use(open, error='FILE should be readable')],
        Optional('PATH'): And(os.path.exists, error='PATH should exist'),
        '-': And(str, lambda s: s in ('s', 'p', 'm'))})

    try:
        arguments = schema.validate(arguments)
        # process(arguments)
    except SchemaError as e:
        exit(e)

running DVget -s "c:\test.txt" gives me the error message 'PATH should exist' even when using Optional in schema and docopt. Any suggestions?


Source: (StackOverflow)

Testing A Docopt command-line app In Unittest?

can anyone show me how can I test a cli app written in Docopt (Python)? Someone on GitHub posted this,

import unittest
from docopt import docopt
import your.entry.point.of.sum as sum

# you can import the doc string from the sum module
doc = sum.__doc__

# suppose now the doc is:
#     Sum two numbers.
#     Usage: summation.py <x> <y>

# then write your test cases
class TestCLIParser(unittest.TestCase):
    def test_sum(self):
        args = docopt(doc, ["1", "3"])
        self.assertEqual(args["<x>"], "1")
        self.assertEqual(args["<y>"], "3")

   def and_so_on(self):
        ...

I have got this but can someone show me how can I test the output of the program? This example only tests the arguments


Source: (StackOverflow)

How to have the docstring respect the PEP257, while usable with docopt to comply with i18n using gettext?

According to PEP 257 the docstring of command line script should be its usage message:

The docstring of a script (a stand-alone program) should be usable as its "usage" message, printed when the script is invoked with incorrect or missing arguments (or perhaps with a "-h" option, for "help"). Such a docstring should document the script's function and command line syntax, environment variables, and files. Usage messages can be fairly elaborate (several screens full) and should be sufficient for a new user to use the command properly, as well as a complete quick reference to all options and arguments for the sophisticated user.

And the the docstring shall be the first string as module level, before anything else, to be available as __doc__.

Now, I'm also using docopt as a usage message parser, so I just have to write the doc string, and it's building the command line parser on its own, and that's great.

_("""...""")

What's not that great, is that I can't find a way to mark the docstring as i18nable for gettext, so I can convert it to other languages when given to docopt. At the time being the only solution I got is to make the usage and help messages stay in english, when all the application's other strings are translated!

As the PEP 20 states:

There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.

What would be the best way to get around the limitation of not being able to mark the docstring as translatable elegantly?

N.B.: here we consider that I'm doing the gettext.install() in the __init__.py module so that _() is present in the builtins before even __doc__ is being parsed.


Source: (StackOverflow)