Coder Social home page Coder Social logo

docopt's Introduction

See docopt-ng

Please see docopt-ng for a compatible, updated fork of the original Python docopt.

This project is no longer maintained.

docopt creates beautiful command-line interfaces

image

image

Video introduction to docopt: PyCon UK 2012: Create *beautiful* command-line interfaces with Python

New in version 0.6.1:

  • Fix issue #85 which caused improper handling of [options] shortcut if it was present several times.

New in version 0.6.0:

  • New argument options_first, disallows interspersing options and arguments. If you supply options_first=True to docopt, it will interpret all arguments as positional arguments after first positional argument.
  • If option with argument could be repeated, its default value will be interpreted as space-separated list. E.g. with [default: ./here ./there] will be interpreted as ['./here', './there'].

Breaking changes:

  • Meaning of [options] shortcut slightly changed. Previously it meant "any known option". Now it means "any option not in usage-pattern". This avoids the situation when an option is allowed to be repeated unintentionally.
  • argv is None by default, not sys.argv[1:]. This allows docopt to always use the latest sys.argv, not sys.argv during import time.

Isn't it awesome how optparse and argparse generate help messages based on your code?!

Hell no! You know what's awesome? It's when the option parser is generated based on the beautiful help message that you write yourself! This way you don't need to write this stupid repeatable parser-code, and instead can write only the help message--the way you want it.

docopt helps you create most beautiful command-line interfaces easily:

"""Naval Fate.

Usage:
  naval_fate.py ship new <name>...
  naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
  naval_fate.py ship shoot <x> <y>
  naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
  naval_fate.py (-h | --help)
  naval_fate.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.

"""
from docopt import docopt


if __name__ == '__main__':
    arguments = docopt(__doc__, version='Naval Fate 2.0')
    print(arguments)

Beat that! The option parser is generated based on the docstring above that is passed to docopt function. docopt parses the usage pattern ("Usage: ...") and option descriptions (lines starting with dash "-") and ensures that the program invocation matches the usage pattern; it parses options, arguments and commands based on that. The basic idea is that a good help message has all necessary information in it to make a parser.

Also, PEP 257 recommends putting help message in the module docstrings.

Installation

Use pip or easy_install:

pip install docopt==0.6.2

Alternatively, you can just drop docopt.py file into your project--it is self-contained.

docopt is tested with Python 2.7, 3.4, 3.5, and 3.6.

Testing

You can run unit tests using the command:

python setup.py test

API

from docopt import docopt
docopt(doc, argv=None, help=True, version=None, options_first=False)

docopt takes 1 required and 4 optional arguments:

  • doc could be a module docstring (__doc__) or some other string that contains a help message that will be parsed to create the option parser. The simple rules of how to write such a help message are given in next sections. Here is a quick example of such a string:
"""Usage: my_program.py [-hso FILE] [--quiet | --verbose] [INPUT ...]

-h --help    show this
-s --sorted  sorted output
-o FILE      specify output file [default: ./test.txt]
--quiet      print less text
--verbose    print more text

"""
  • argv is an optional argument vector; by default docopt uses the argument vector passed to your program (sys.argv[1:]). Alternatively you can supply a list of strings like ['--verbose', '-o', 'hai.txt'].
  • help, by default True, specifies whether the parser should automatically print the help message (supplied as doc) and terminate, in case -h or --help option is encountered (options should exist in usage pattern, more on that below). If you want to handle -h or --help options manually (as other options), set help=False.
  • version, by default None, is an optional argument that specifies the version of your program. If supplied, then, (assuming --version option is mentioned in usage pattern) when parser encounters the --version option, it will print the supplied version and terminate. version could be any printable object, but most likely a string, e.g. "2.1.0rc1".

    Note, when docopt is set to automatically handle -h, --help and --version options, you still need to mention them in usage pattern for this to work. Also, for your users to know about them.

  • options_first, by default False. If set to True will disallow mixing options and positional argument. I.e. after first positional argument, all arguments will be interpreted as positional even if the look like options. This can be used for strict compatibility with POSIX, or if you want to dispatch your arguments to other programs.

The return value is a simple dictionary with options, arguments and commands as keys, spelled exactly like in your help message. Long versions of options are given priority. For example, if you invoke the top example as:

naval_fate.py ship Guardian move 100 150 --speed=15

the return dictionary will be:

{'--drifting': False,    'mine': False,
 '--help': False,        'move': True,
 '--moored': False,      'new': False,
 '--speed': '15',        'remove': False,
 '--version': False,     'set': False,
 '<name>': ['Guardian'], 'ship': True,
 '<x>': '100',           'shoot': False,
 '<y>': '150'}

Help message format

Help message consists of 2 parts:

  • Usage pattern, e.g.:

    Usage: my_program.py [-hso FILE] [--quiet | --verbose] [INPUT ...]
  • Option descriptions, e.g.:

    -h --help    show this
    -s --sorted  sorted output
    -o FILE      specify output file [default: ./test.txt]
    --quiet      print less text
    --verbose    print more text

Their format is described below; other text is ignored.

Usage pattern format

Usage pattern is a substring of doc that starts with usage: (case insensitive) and ends with a visibly empty line. Minimum example:

"""Usage: my_program.py

"""

The first word after usage: is interpreted as your program's name. You can specify your program's name several times to signify several exclusive patterns:

"""Usage: my_program.py FILE
          my_program.py COUNT FILE

"""

Each pattern can consist of the following elements:

  • <arguments>, ARGUMENTS. Arguments are specified as either upper-case words, e.g. my_program.py CONTENT-PATH or words surrounded by angular brackets: my_program.py <content-path>.
  • --options. Options are words started with dash (-), e.g. --output, -o. You can "stack" several of one-letter options, e.g. -oiv which will be the same as -o -i -v. The options can have arguments, e.g. --input=FILE or -i FILE or even -iFILE. However it is important that you specify option descriptions if you want your option to have an argument, a default value, or specify synonymous short/long versions of the option (see next section on option descriptions).
  • commands are words that do not follow the described above conventions of --options or <arguments> or ARGUMENTS, plus two special commands: dash "-" and double dash "--" (see below).

Use the following constructs to specify patterns:

  • [ ] (brackets) optional elements. e.g.: my_program.py [-hvqo FILE]
  • ( ) (parens) required elements. All elements that are not put in [ ] are also required, e.g.: my_program.py --path=<path> <file>... is the same as my_program.py (--path=<path> <file>...). (Note, "required options" might be not a good idea for your users).
  • | (pipe) mutually exclusive elements. Group them using ( ) if one of the mutually exclusive elements is required: my_program.py (--clockwise | --counter-clockwise) TIME. Group them using [ ] if none of the mutually-exclusive elements are required: my_program.py [--left | --right].
  • ... (ellipsis) one or more elements. To specify that arbitrary number of repeating elements could be accepted, use ellipsis (...), e.g. my_program.py FILE ... means one or more FILE-s are accepted. If you want to accept zero or more elements, use brackets, e.g.: my_program.py [FILE ...]. Ellipsis works as a unary operator on the expression to the left.
  • [options] (case sensitive) shortcut for any options. You can use it if you want to specify that the usage pattern could be provided with any options defined below in the option-descriptions and do not want to enumerate them all in usage-pattern.
  • "[--]". Double dash "--" is used by convention to separate positional arguments that can be mistaken for options. In order to support this convention add "[--]" to your usage patterns.
  • "[-]". Single dash "-" is used by convention to signify that stdin is used instead of a file. To support this add "[-]" to your usage patterns. "-" acts as a normal command.

If your pattern allows to match argument-less option (a flag) several times:

Usage: my_program.py [-v | -vv | -vvv]

then number of occurrences of the option will be counted. I.e. args['-v'] will be 2 if program was invoked as my_program -vv. Same works for commands.

If your usage patterns allows to match same-named option with argument or positional argument several times, the matched arguments will be collected into a list:

Usage: my_program.py <file> <file> --path=<path>...

I.e. invoked with my_program.py file1 file2 --path=./here --path=./there the returned dict will contain args['<file>'] == ['file1', 'file2'] and args['--path'] == ['./here', './there'].

Option descriptions format

Option descriptions consist of a list of options that you put below your usage patterns.

It is necessary to list option descriptions in order to specify:

  • synonymous short and long options,
  • if an option has an argument,
  • if option's argument has a default value.

The rules are as follows:

  • Every line in doc that starts with - or -- (not counting spaces) is treated as an option description, e.g.:

    Options:
      --verbose   # GOOD
      -o FILE     # GOOD
    Other: --bad  # BAD, line does not start with dash "-"
  • To specify that option has an argument, put a word describing that argument after space (or equals "=" sign) as shown below. Follow either <angular-brackets> or UPPER-CASE convention for options' arguments. You can use comma if you want to separate options. In the example below, both lines are valid, however you are recommended to stick to a single style.:

    -o FILE --output=FILE       # without comma, with "=" sign
    -i <file>, --input <file>   # with comma, without "=" sign
  • Use two spaces to separate options with their informal description:

    --verbose More text.   # BAD, will be treated as if verbose option had
                           # an argument "More", so use 2 spaces instead
    -q        Quit.        # GOOD
    -o FILE   Output file. # GOOD
    --stdout  Use stdout.  # GOOD, 2 spaces
  • If you want to set a default value for an option with an argument, put it into the option-description, in form [default: <my-default-value>]:

    --coefficient=K  The K coefficient [default: 2.95]
    --output=FILE    Output file [default: test.txt]
    --directory=DIR  Some directory [default: ./]
  • If the option is not repeatable, the value inside [default: ...] will be interpreted as string. If it is repeatable, it will be splited into a list on whitespace:

    Usage: my_program.py [--repeatable=<arg> --repeatable=<arg>]
                         [--another-repeatable=<arg>]...
                         [--not-repeatable=<arg>]
    
    # will be ['./here', './there']
    --repeatable=<arg>          [default: ./here ./there]
    
    # will be ['./here']
    --another-repeatable=<arg>  [default: ./here]
    
    # will be './here ./there', because it is not repeatable
    --not-repeatable=<arg>      [default: ./here ./there]

Examples

We have an extensive list of examples which cover every aspect of functionality of docopt. Try them out, read the source if in doubt.

Subparsers, multi-level help and huge applications (like git)

If you want to split your usage-pattern into several, implement multi-level help (with separate help-screen for each subcommand), want to interface with existing scripts that don't use docopt, or you're building the next "git", you will need the new options_first parameter (described in API section above). To get you started quickly we implemented a subset of git command-line interface as an example: examples/git

Data validation

docopt does one thing and does it well: it implements your command-line interface. However it does not validate the input data. On the other hand there are libraries like python schema which make validating data a breeze. Take a look at validation_example.py which uses schema to validate data and report an error to the user.

Using docopt with config-files

Often configuration files are used to provide default values which could be overriden by command-line arguments. Since docopt returns a simple dictionary it is very easy to integrate with config-files written in JSON, YAML or INI formats. config_file_example.py provides and example of how to use docopt with JSON or INI config-file.

Development

We would love to hear what you think about docopt on our issues page

Make pull requests, report bugs, suggest ideas and discuss docopt. You can also drop a line directly to <[email protected]>.

Porting docopt to other languages

We think docopt is so good, we want to share it beyond the Python community! All official docopt ports to other languages can be found under the docopt organization page on GitHub.

If your favourite language isn't among then, you can always create a port for it! You are encouraged to use the Python version as a reference implementation. A Language-agnostic test suite is bundled with Python implementation.

Porting discussion is on issues page.

Changelog

docopt follows semantic versioning. The first release with stable API will be 1.0.0 (soon). Until then, you are encouraged to specify explicitly the version in your dependency tools, e.g.:

pip install docopt==0.6.2
  • 0.6.2 Bugfix release.
  • 0.6.1 Bugfix release.
  • 0.6.0 options_first parameter. Breaking changes: Corrected [options] meaning. argv defaults to None.
  • 0.5.0 Repeated options/commands are counted or accumulated into a list.
  • 0.4.2 Bugfix release.
  • 0.4.0 Option descriptions become optional, support for "--" and "-" commands.
  • 0.3.0 Support for (sub)commands like git remote add. Introduce [options] shortcut for any options. Breaking changes: docopt returns dictionary.
  • 0.2.0 Usage pattern matching. Positional arguments parsing based on usage patterns. Breaking changes: docopt returns namespace (for arguments), not list. Usage pattern is formalized.
  • 0.1.0 Initial release. Options-parsing only (based on options description).

docopt's People

Contributors

alex avatar andreypopp avatar bagrow avatar dankeder avatar eventh avatar graingert avatar jeffrimko avatar johari avatar jonlundy avatar kbatten avatar keleshev avatar laurentb avatar lenesk avatar mboersma avatar met48 avatar mgedmin avatar msabramo avatar nicolaisi avatar parlarjb avatar renesac avatar schmir avatar shabbyrobe avatar thewawar avatar tubaman avatar vlad-shcherbina avatar vorontsovie avatar xiaq avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

docopt's Issues

Complex option arguments

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.

IndexError on simple parser declaration

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

docopt should evaluate sys.argv when being called, not when being imported

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.

Repeat arguments affect return value for other subcommands

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.

PHP port, all tests pass

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.

Repeatable commands with optional arguments doesn't work in experimental branch

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]

Mac.app -psn argument "not recognized"

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:

-psn_0_2740893

It is not in the common format --option=value thus can not be easily caught or anticipated, causing a

-p is not recognized

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?

empty arguments are turned into True

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?

Optional argument groupings match if any of the arguments in the group match

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')

Make -- really separate options and args

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?

Add support for [no-]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?

Default action (no arguments)

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.

Multi-word program name

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.

  • Is this needed?
  • If yes, what should API look like?

DocoptExit raised when doc string is unicode

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.

Repeated mutually exclusive args give nested lists... sometimes.

Docopt 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.

Change the docopt error message format

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?

alternate syntax for [default: ...]?

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)

PyCon UK, Ireland, Finland

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

Regexps in DSL

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.

Make it possible to write 'xargs'

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.)

setup.py installer

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

Flag schema problem.

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.

Example 1

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>': []}

Example 2

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>': []}

Example 3

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>': []}

Example 4

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}

Raise an exception on --help and --version

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.

Support for counted short flags, a-la ssh -vvv?

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?

Type conversions

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?

  • a way to easily typeconvert and validate while directly after docopting.

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 [])

Seems like this is supported, but I can't get multiple arguments to work

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']}

Deprecate and remove ARG convention in favor of <arg>

What do you think on removing upper-case positional arguments convention?

It has several problems:

  • each language has it's own understanding on what is .isupper()
  • each person has his own understanding on what is upper-case
  • "there should be one obvious way to do it"
  • <arg> is easier to parse and less ambiguous

Default value for argument gets assigned to option

"""
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.

--help and --help-commands

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.

Another request for type annotations and conversions

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 --

#8
#58

-- 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:

  1. Simplicity is valuable.
  2. You can easily perform conversions elsewhere using Schema

I will address each of these in turn:

  1. 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!

  2. 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!

Add support for enumerations

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.