docopt / docopt Goto Github PK
View Code? Open in Web Editor NEWCreate *beautiful* command-line interfaces with Python
License: MIT License
Create *beautiful* command-line interfaces with Python
License: MIT License
I want to specify in usage-pattern that command accepts some options but I don't want to enumerate them all, so I just like to have [options]
shown (or [OPTIONS]
).
"""usage: command [options] arg1 arg2"""
exclussive
should read exclusive
.
This is the docstring for punt.
I would like to have multiple --watch
options, but I can't figure out a syntax that supports it.
"""Watches current path (or another path) for changes and executes
command(s) when a change is detected. Uses watchdog to track file changes.
Usage:
punt [-w <path> ...] [-l] [--] <commands>...
punt (-h | --help)
punt --version
Options:
-w <path> ..., --watch <path> ... Which path to watch [default: current directory]
-l, --local Only tracks local files (disables recusive)
"""
# blablabla
arguments = docopt(__doc__, version='punt v1.6')
print repr(arguments)
> punt -w dir1 dir2 -- make
{'--': False,
'--help': False,
'--local': False,
'--version': False,
'--watch': 'dir1',
'-h': False,
'<commands>': ['dir2', '--', 'make']}
> punt -w dir1 -w dir2 -- make
{'--': True,
'--help': False,
'--local': False,
'--version': False,
'--watch': 'dir2',
'-h': False,
'<commands>': ['make']}
I applied for a 20 minute talk about docopt at PyCon UK, Ireland and Finland, hoping that I will be accepted at at-least one of them. To my surprise the talk was accepted at all of them.
Highlighted are the days I'm giving the talk.
I still didn't get UK visa—but it's likely that I will get it in time.
It would be great to meet you guys there (at a conference, or after one), especially if you live close to the location.
/cc @alexspeller @ansible07 @sonwell @njoh @raskug @shabbyrobe @sonwell @Met48
What do you think on removing upper-case positional arguments convention?
It has several problems:
.isupper()
<arg>
is easier to parse and less ambiguousDocopt is really cool. It fits the way I write tools; I like.
I'm trying to express "at least one arg from each of two sets". I think I can do this with repeated "mutually exclusive" args:
Usage: foo (--x=x|--y=y)... (--a=a|--b=b)...
But, with docopt 0.5.0 and git fce1ee7, I run into a slight issue:
docopt("Usage: foo (--x=x|--y=y)...", "--x=1 --y=2")
{'--x': ['1'],
'--y': [['2']]}
I believe it should be:
{'--x': ['1'],
'--y': ['2']}
And If there's a better way of achieving my goal, I'm all ears.
When a program is invoked as a MacOS app, it is being passed an extra argument (process serial number "PSN") by the OS like so:
It is not in the common format --option=value thus can not be easily caught or anticipated, causing a
error.
[options] does not help in this case either, as it expects the listed options.
More generally, can our programs not break when given truly unanticipated arguments?
The current code uses:
def docopt(doc, argv=sys.argv[1:], help=True, version=None, any_options=False):
....
It should evaluate sys.argv when being called:
def docopt(doc, argv=None, help=True, version=None, any_options=False):
if argv is None:
argv = sys.argv[1:]
I've got some legacy code that calls into a programs main and sets up sys.argv before.
Sometimes I want to fail with some error and thus print usage line on stderr, but docopt misses that feature.
This usage:
Usage:
prog ship new <name>...
prog ship [<name>] move
Can return the following output:
{'<name>': ['Guardian'],
'move': True,
'new': False,
'ship': True}
Note that the <name>
attribute is a list, even though subcommand ship move
has only one occurrence of <name>
. This seems likely to cause problems when projects grow larger and new commands are added or the docstring is split.
It would be nice if an alternate syntax were available for an option's default value. Some of my options are long (urls and file paths), so I try to remove every character possible in order to avoid line wrap. Personally I could live with just square brackets, but if that causes trouble for some, perhaps double them up, [[..]]
?
Options:
-i,--ini=FILE use alternate setup.ini [[O:/etc/setup/setup.ini]]
-m,--mirror=URL use alternate mirror [[http://download.example.org/beta_release/testing/RC2]]
(aside: 'usage' is mispelt 'ussage' in a couple places in the docs)
Currently a command like naval_fate ship [<name>] move
will not parse correctly when the optional argument is omitted. This also occurs with repeating arguments.
If you add an invalid flag (such as --drifting) to the default command line at http://try.docopt.org/,
there is no error message as such, just a usage message.
Docopt could do better here, perhaps by reporting which thing it was trying to match at the time and the invalid part.
The PyPi entry for docopt does not have any version classifier. Is docopt working with Python 3?
It would be great if you add these classifiers as explained here.
shows only the usage spec, not the options
It's common pattern to use boolean options like --[no-]paginate. They are missing in docopt. Does it have any reasons not to include such functionality or it's just not yet implemented?
Maybe use the doc string within each function.
You know when you see something that you don't know how you ever lived without... We do mostly PHP where I work so rather than keep living without docopt I whipped up a quick and dirty transliteration.
It passes all of the tests in the language_agnostic_parser, but it hasn't really been given much of a workout yet and there could be some issues I haven't found.
https://github.com/shabbyrobe/docopt.php
The main issue at the moment is that DocoptExit doesn't quite work in PHP like it should - SystemExit has no obvious analog in PHP so it currently requires a catch block, but I'm working on that.
Thanks again for such a fantastic library, and a very, very big thanks for the language agnostic tester.
I'm experiencing IndexError while trying to produce parser (actually provide test case for issue #8) from simple docstring:
"""usage: serve
Options:
-p, --port PORT serve an app on specified port [default: 8000]
"""
from docopt import docopt
print docopt(__doc__)
gives me
Traceback (most recent call last):
File "test.py", line 9, in <module>
print docopt(__doc__)
File "/Users/andreypopp/.virtualenvs/.../lib/python2.7/site-packages/docopt.py", line 483, in docopt
if type(formal_pattern.children[0]) is Either:
IndexError: list index out of range
When handling this definition
Usage:
fubar [-f LEVEL] [--] <items>...
and call it thus:
% fubar -f -- 1 2
I'd expect the result to be an error since -f didn't receive an argument, right?
Hi,
docopt looks really nice!
How about a setup.py file to make it easily installable?
I could provide you one via a pull request but it needs contact informations and other infos that you would probably prefer to specify yourself.
Also it would be awesome if you published it on pypi but the README would be better if it was in rst format since that's what is supported by pypi.
Thanks
I have this test of options parsers, and almost every one fails, including docopt: Can I write 'xargs'?
Here's a simplified usage string:
"""Usage:
xargs.py [options] [COMMAND [ARG...]]
Options:
--null,-0 Input items terminated by 0
"""
The problem is that Docopt allows interspersed options, per the GNU(?) convention. (This is a good default! I don't dispute that.) This means that the command line xargs.py foo -0
produces this option dictionary:
{'--null': True,
'ARG': [],
'COMMAND': 'foo'}
instead of this one:
{'--null': False,
'ARG': ['-0'],
'COMMAND': 'foo'}
Obviously I could require that the user enter --
in there, but that is both obnoxious and not the typical situation for a program which runs another command passed on the command line. (E.g. time
, valgrind
, env
, etc.)
(Incidentally, --
handling IMO doesn't seem very good either: If I run xargs.py -- foo
what I get is
{'--null': False,
'ARG': ['foo'],
'COMMAND': '--'}
while standard convention is that the -- is basically transparent to the program's meaning and thus it seems like it should be transparent to the program's code, so forcing the programmer to manually shift the positional arguments down (or explicitly list a variant of the command with --
) seems wrong.)
There's another idiom I've seen around the place where a short can be multiplied to increase its weight. The only one that springs to mind right this second is ssh
:
-t Force pseudo-tty allocation. Blah blah blah. Multiple -t options force
tty allocation, etc.
-v Verbose mode. Yada yada yada. Multiple -v options increase the
verbosity. The maximum is 3.
Would this be something docopt could support, or do you think simply recommending people do -v 3
is enough?
It'd be nice to write options like:
--background <A> <C> <G> <T>
so that call
my_tool --background 1 2 3 4
yielded options { '--background' => {'A'=>1, 'C'=>2, 'G'=>3, 'T' => 4}} (sorry for ruby-like hash-notation)
or at least {'--background' => [1 ,2, 3, 4]}
It's very frustrating that I cannot specify multiple required arguments for an option.
I see that this works better in the current master. Still, there is an issue:
$ script.py --help
will always trigger the --help-commands action, when I would expect it to show the help.
I just learned about docopt from your video featured in Python Weekly, and I must say it looks amazing! However, I was surprised to find that there is no functionality provided for annotating or converting the types of arguments. Even though this has been raised and closed twice before --
-- I think it is an important feature and I want to revisit it to add my perspective and support.
The main arguments against adding this functionality seem to be:
I will address each of these in turn:
Yes, simplicity is valuable and one of the most compelling aspects of docopt. To preserve simplicity, let's consider allowing only the following type annotations: int
, float
, and string
. Everything is a string
unless otherwise annotated. This drastically reduces both implementation complexity and usage complexity as compared to a more featureful approach that would allow conversions to arbitrary, possibly user-defined Python objects.
I think int
and float
would cover a large fraction of use cases. The only other types I would even consider supporting would be boolean
and maybe file
. Since docopt is presenting a generic command line interface, it makes sense to me that it would only support data types that are somehow "native" to the Unix ecosystem rather than arbitrary Python-specific types that have no Unix analogue. Ranges or sets of the supported primitives would also be candidates for inclusion -- I think they would be worth adding, but I agree they add complexity (both implementation-wise and in the DSL). So, start with just the really simple stuff!
If an argument is required to be a particular type, I would like the command line help text to reflect that. If I've already specified the type in the usage message, why should I need to manually create a schema re-specifying portions of the interface?
Schema looks like a very nice tool! What if docopt used the information it parsed from the usage string to automatically create the Schema and perform validations/conversions for me (behind the scenes)? That would be big win for clarity and usability and incur only minor implementation complexity.
Anyway, I just wanted to add my two cents. Again, great job with docopt!
docopt 0.1 was able to infer option value type by looking at default, 0.2 seems to do nothing about this
It feels like #4 will solve this problem (though a solution for #4 does indeed seem like it will be anything but trivial), but when I try to provide optional arguments to a repeatable subcommand, the indexing makes it impossible to work out which command each argument belongs to.
The following example raises an IndexError on the third iteration because the --speed 999
gets interpreted as part of the second go
command rather than the third:
import docopt
arguments = docopt.docopt(
"usage: prog (go <direction> [--speed=<km/h>])...",
"go forwardnotbackward --speed 123 go upwardnotforward go twirling --speed 999"
)
for i in xrange(0, arguments['go']):
print arguments['<direction>'][i]
print arguments['--speed'][i]
There might be a need to allow multi-word program names as follows
Usage: python program.py [options]
or
Usage: python3 program.py [options]
or
Usage: python program.zip [options]
Even something like:
Usage: python -v program.py [options]
or
Usage: python -m module [options]
There is no obvious way to do it right now in docopt. Any feedback is appreciated.
I'm generating the doc string and using appdirs to show where my application searches for configuration files.
Usage: main.py [options]
failes when my doc string converts to unicode because of appdirs returning a unicode string.
It seemed that the parse_atom
method got each letter instead of each word, and so the elif token =='options':
was never true.
having flags instead of some kind of lookup adds some strange loops to figure whats actually going on with the cli
To ensure reliable behaviour and the same functionality across languages, you should add a full grammar. (as well as other formal definitions.)
def main(argv=None):
docopt(__doc__, argv=argv)
should work for wrapper functions
docopt/docopt.rb#12 shows that test-coverage is lower than 100%
seems like docopt wont add a --help command
Currently, docopt reports errors as follows:
-o is not recognized
--opt is not recognized
--o requires argument
--opt requires argument
--opt is not a unique prefix: --option, --optimum?
--opt must not have an argument
All followed by a listing of the usage patterns. However, a cursory sample of command-line utilities on my box use the following format:
program: invalid option -- 'o'
program: unrecognised option '--o'
program: option requires an argument -- 'o'
program: option '--option' requires an argument
program: option '--opt' is ambiguous; possibilities: '--option' '--optimum'
program: option '--option' doesn't allow an argument
All followed by Try 'program --help' for more information.
, which I think looks way better (although that might just be my Ubuntu bias talking.) At the very least, I think docopt should prefix program:
to the error messages it outputs, though this might cause some complications if #41 ever gets implemented. Opinions?
I thought about what docopt does right and where that approach has its drawbacks.
I came to the conclusion that it’s a little too fast for its own good when it comes to the speed at which you get something (the dictionary) out of it: docopt is designed to be used in 1 function call on 1 docstring you wrote, but since that one also needs to be human readable, it has its shortcomings.
My problem is type conversions: Though roundabout, after using argparse you have an object containing ints, files, and everything you can instantiate by feeding it strings.
Compare argparse’s options.x
with docopt’s int(options["<x>"])
. docopt is beautiful and easy until you have the dict, but ugly afterwards.
The other problem is “break early and often”: if you convert stuff whenever it is needed, it is there where your program will break if fed invalid options. If you decide to do this as early as possible (e.g. by converting your dictionary into a set of variables), you have to do if-else branches again and handle missing arguments (e.g. if an optional path to a file is not given, you have to do f = file(options['--path']) if options['--path'] else None
)
so what do i want?
what is “easy”?
not doing it manually, but letting docopt do it. either by another function, or by another keyword argument. This should of course handle lists by just mapping the conversion onto them.
(e.g. docopt(__doc__, conversions={'<x>': int, '<y>': int}
)
letting docopt handle missing optional arguments: if something isn’t given, it will yield None
in the dict, which docopt shouldn’t typeconvert, as mist constructors do silly stuff or break when given None
. (for lists, that’s easy. map(int, [])
ist still []
)
We can already do this:
Usage: go (north | west | east | south)
But this uses subcommands, which have an awkward access syntax, and issues #42 and #4 suggest to me they're meant for something rather more heavy-duty. Moreover, the set of legal values might be too verbose or too large to enumerate in an usage pattern. In such cases, something like this would be more useful:
Usage: go <direction>
<direction> The direction to go in (north, west, east, south)
"""
Usage:
bug.py [--opt num] [ARG]
Options:
--opt num An option with a value
Arguments:
ARG An arg with a default [default: A]
"""
from docopt import docopt
arguments = docopt(__doc__)
print arguments
I expect this to show:
{'--opt': None, 'ARG': 'A'}
What actually happens:
{'--opt': 'A', 'ARG': None}
I'll take a look into a fix tomorrow if I can.
Below is four examples, and IMO it's hard to predict parse result schema.
In the first three examples some flags are interpret as repeated flags.
Particulary in second example (which is IMO almost the right way) -t
and -x
are repeated, but -c
is not.
Also the value of -f
is different in all examples: wrapped in array, scalar and wrapped twice in array. Looks like there's some bug somewhere, especially the double array. Neither example accept multiple -f
flags thought, so value of -f
should be scalar in all examples.
But I agree the right way is to use commands instead of -crutx
flags, and then there is still -f
problem, see example 4.
cat example1.py
python
"""
Usage:
example -c [options] ...
example (-r | -u) -f=ARCHIVE [options] ...
example (-t | -x) [options] [...]
Options:
--help Print this message
-v Produce verbose output.
-z (c mode only) Compress the resulting archive with gzip(1).
-Z (c mode only) Compress the resulting archive with compress(1).
-j (c mode only) Compress the resulting archive with bzip2(1).
-c Create a new archive containing the specified items.
-r Like -c, but new entries are appended to the archive.
-u Like -r, but new entries are added only if they have a modification date newer than the corresponding entry in the archive.
-b BLOCKSIZE Specify the block size, in 512-byte records, for tape drive I/O.
-f ARCHIVE Read the archive from or write the archive to the specified file.
-t List archive contents to stdout.
-x Extract to disk from the archive.
"""
from docopt import docopt
if name == 'main':
arguments = docopt(doc, version='0.0.1')
print(arguments)
``` sh
python example1.py -tf example
{'--help': False,
'-Z': False,
'-b': None,
'-c': False,
'-f': ['example'],
'-j': False,
'-r': 0,
'-t': 1,
'-u': 0,
'-v': False,
'-x': 0,
'-z': False,
'<files>': [],
'<patterns>': []}
cat example2.py
"""
Usage:
example -c [options] <files>...
example (-r | -u) -f=ARCHIVE <files>...
example (-t | -x) [options] [<patterns>...]
Options:
--help Print this message
-v Produce verbose output.
-z (c mode only) Compress the resulting archive with gzip(1).
-Z (c mode only) Compress the resulting archive with compress(1).
-j (c mode only) Compress the resulting archive with bzip2(1).
-c Create a new archive containing the specified items.
-r Like -c, but new entries are appended to the archive.
-u Like -r, but new entries are added only if they have a modification date newer than the corresponding entry in the archive.
-b BLOCKSIZE Specify the block size, in 512-byte records, for tape drive I/O.
-f ARCHIVE Read the archive from or write the archive to the specified file.
-t List archive contents to stdout.
-x Extract to disk from the archive.
"""
from docopt import docopt
if __name__ == '__main__':
arguments = docopt(__doc__, version='0.0.1')
print(arguments)
python example2.py -tf example
{'--help': False,
'-Z': False,
'-b': None,
'-c': False,
'-f': 'example',
'-j': False,
'-r': False,
'-t': 1,
'-u': False,
'-v': False,
'-x': 0,
'-z': False,
'<files>': [],
'<patterns>': []}
cat example3.py
"""
Usage:
example -c [options] <files>...
example [-r | -u] -f=ARCHIVE [options] <files>...
example [-t | -x] [options] [<patterns>...]
Options:
--help Print this message
-v Produce verbose output.
-z (c mode only) Compress the resulting archive with gzip(1).
-Z (c mode only) Compress the resulting archive with compress(1).
-j (c mode only) Compress the resulting archive with bzip2(1).
-c Create a new archive containing the specified items.
-r Like -c, but new entries are appended to the archive.
-u Like -r, but new entries are added only if they have a modification date newer than the corresponding entry in the archive.
-b BLOCKSIZE Specify the block size, in 512-byte records, for tape drive I/O.
-f ARCHIVE Read the archive from or write the archive to the specified file.
-t List archive contents to stdout.
-x Extract to disk from the archive.
"""
from docopt import docopt
if __name__ == '__main__':
arguments = docopt(__doc__, version='0.0.1')
print(arguments)
python example3.py -tf example
{'--help': False,
'-Z': False,
'-b': None,
'-c': False,
'-f': [['example']],
'-j': False,
'-r': 0,
'-t': 1,
'-u': 0,
'-v': False,
'-x': 0,
'-z': False,
'<files>': [],
'<patterns>': []}
cat example4.py
"""
Usage:
example create [options] <files>...
example ( append | newer ) -f=ARCHIVE [options] <files>...
example ( test | extract ) [options] [<patterns>...]
Commands:
create Create a new archive containing the specified items.
append Like create, but new entries are appended to the archive.
newer Like append, but new entries are added only if they have a
modification date newer than the corresponding entry in the
archive.
test List archive contents to stdout.
extract Extract to disk from the archive.
Options:
--help Print this message
-v Produce verbose output.
-z (c mode only) Compress the resulting archive with gzip(1).
-Z (c mode only) Compress the resulting archive with compress(1).
-j (c mode only) Compress the resulting archive with bzip2(1).
-b BLOCKSIZE Specify the block size, in 512-byte records, for tape drive I/O.
-f ARCHIVE Read the archive from or write the archive to the specified file.
"""
from docopt import docopt
if __name__ == '__main__':
arguments = docopt(__doc__, version='0.0.1')
print(arguments)
python example4.py test -f example example1.txt
{'--help': False,
'-Z': False,
'-b': None,
'-f': ['example'],
'-j': False,
'-v': False,
'-z': False,
'<files>': [],
'<patterns>': [],
'append': False,
'create': False,
'extract': False,
'newer': False,
'test': True}
In some use cases (namely, my own docopts), it would be useful to handle the --help
and --version
options specially, but let docopt worry about parsing them. At present, when docopt() encounters --help
or --version
, it simply dumps the relevant string to standard output and calls sys.exit(). A better approach would be to raise some derivative of SystemExit with the relevant string as its argument; SystemExit itself won't do, as it interprets all string arguments as failure conditions, and DocoptExit tacks the usage pattern list to its arguments.
One could just invoke docopt() with help=False
and version=None
and handle the --help
and --version
entries in the return dictionary manually, but then they no longer get preferential treatment; e.g., with the following interface description:
Usage: prog
--help
--version
The following two invocations work if help=True
and version="Some version message here"
:
prog gekki gukki gakki --help
prog ooga booga --version
Whereas they don't if help=False
and version=None
.
shlex does shell quote spliting which allows to escape whitespac
I was thinking about how powerful that would be if docopt validated arguments based on regular expressions:
"""Usage: my_program <host> <port> --timeout=<s>
<host> /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
<port> /\d{1,5}/
--timeout=<s> /\d{1,5}/
"""
But this is just wrong.
Using 0.3, this docstring:
Usage:
script
script --flag
when given no arguments, show the usage text. I would expect it to return a dictionary if no options set.
IMHO empty arguments on the command line should be turned into empty strings in docopt's result dictionary, i.e. the following test should not fail:
def test_empty_value(): assert docopt('usage: prog --long=', '--long=') == \ {'--long': ['']}
Currently the result of parsing these options is:
{"--long": True}
Or is there a good reason why it's being converted to a boolean?
I would like to be able to specify that either no arguments, or a pair of arguments are required. Something like: """ Usage: prog [<a> <b>] """
would seem to make sense, but the parser allows prog one
to match, as well as prog one two
and prog
.
I have found that """ Usage: prog [(<a> <b>)] """
gives the behaviour I would like, but it seems like the extra parentheses shouldn't be required.
I've tried adjusting the matching of the Optional type, but it breaks a whole load of other things.
def test_paired_arguments:
with raises(DocoptExit):
docopt('usage: prog [<a> <b>]', 'one')
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.