Coder Social home page Coder Social logo

docopt.go'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.go's People

Contributors

aviddiviner avatar kbatten avatar mboersma avatar mcandre avatar th0114nd 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

docopt.go's Issues

Recursive build and test fails

In a project which has docopt-go as a dependency, the examples directory in the root of the repository prevents building with go build ./... from working.

Running tests with go test ./... also fails. The reason it fails is that main is redeclared multiple times in the example files.

Building a project with go build ./... and testing with go test ./... seems to be the convention for many Go projects, and it's the default used by Travis CI.

map[string]interface{} is cumbersome in Go

I know that in Python a dict works well. But in Go a map[string]interface{} is actually cumbersome as you have to cast the value to string or int. map[string]string would be better as it eliminates the casting, but still creates a lot of unnecessary Unmarshal work.

I would suggest that docopt.go work more like Go's encoding packages (which uses reflect). Essentially something like this:

type Options struct {
    Command string `docopt:"<command>"`
    Speed int `docopt:"--speed"`
    Duration time.Duration `docopt:"--duration"` // Use time.ParseDuration
    Custom CustomValue `docopt:"--custom"` // Uses type UnmarshalDocopt interface
    Map map[string]string `docopt:""` // Put the rest of the options here
    Other string // Ignore this because it doesn't have docopt: tag
}

var doc = `...` // Ussage/Options text here

func main() {
    p := docopt.NewParser(doc, true, "App 2.0", false)
    var opt Options // can also be a map[string]string
    if err := p.Parse(&opt); err != nil {
        log.Fatal(err)
    }
}

This package does something similar to the above example, but all the options are specified in tags, which get really ...really... long. With this you simply specify the name in a tag as you would in the map.

I would also consider it bad practice in Go to do an os.Exit inside of Parse/NewParser. The package user should have complete control over logging and exiting behavior inside their main. So I would suggest the above example look like this:

func main() {
    p := docopt.NewParser(doc, false) // Removed two middle options
    var opt Options
    if err := p.Parse(&opt); err != nil {
        log.Fatal(err)
    }
    if opt.Version { // Added Version bool `docopt:"--version"` to Options
        println("App 2.0")
        return
    }
    if opt.Help { // Added Help bool `docopt:"--help"` to Options
        println(doc) // OR p.PrintHelp()
        return
    }
}

Another neat idea (and perhaps this should be in another issue) would be to have the ability to fill values from an ini file like this:

type Options struct {
    Config string `docopt:"--config,ini"` // The "ini" flag tells docopt to read this string as a filename
    // ...
}

Versioning of library

I want to prepare a package for Fedora and have a question - how versions will go? Do you want to follow python development and use the same versions or just to code without regular releases? Both answers are right (it's for nice implementation only) :) I can see 0.6.1 tag which is one year old and I'm not sure how it will continue.

Whitespace is killing me

For anyone interested in using docopt in Go, this seems like a great option. However, in it's current state, this package is harder to work with than the speed gains potentially provided by DocOpt. I am a firm believer in docopt and what it tries to do, so understand I really want this package to work, sadly it does not.

This issue is meant to steer people away until major issues are fixed.

The critical issues can be found by perusing the issues here in this repo, so I won't rehash them, but suffice to say i've wasted more hours debugging the parsing this package uses, then actually using it.

Ellipsis not documented

When greping through the repository I couldn't find a single occurrence of the word 'ellipsis'.

On the GoDoc page I found one example where Bind() is used to bind to a struct containing Host []string.

I didn't find any good way to get ellipsis options without binding to a struct. In
examples/fake-git/fakegit.go I found this line cmdArgs := args["<args>"].([]string), but any casting error can't be caught here. Is there a better way?

In my opinion all this should be documented. If you can tell me about any functionality I missed, I'd be happy to write a paragraph for the GoDoc documentation and create a pull request.

Wrong optional keyword match

It seems if a command-like argument (like a keyword) is use in an optional block, it matches the wrong thing.
In the following repro, "cmd" is matched with "opt1" if "keyword" is not specified. If it is specified it works fine.
Note that if instead of a keyword, an option (like "--option") is specified it works fine.

package main

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

func main() {
	usage := `To match optional with keyword.

Usage:
  keyword-match: [keyword <opt1>] [<args>...]
`
	args, err := docopt.ParseArgs(usage, []string{"cmd"}, "")
	fmt.Printf("%#v\n", args) // BUG prints: docopt.Opts{"<opt1>":"cmd", "keyword":false}
	fmt.Printf("%#v\n", err)  // prints: <nil>

	args, err = docopt.ParseArgs(usage, []string{"keyword", "opt1", "cmd"}, "")
	fmt.Printf("%#v\n", args) // prints: docopt.Opts{"<args>":[]string{"cmd"}, "<opt1>":"opt1", "keyword":true}
	fmt.Printf("%#v\n", err)  // prints: <nil>

	usage = `To match optional with option.

Usage:
  keyword-match: [--option=<opt1>] [<args>...]
`
	args, err = docopt.ParseArgs(usage, []string{"cmd"}, "")
	fmt.Printf("%#v\n", args) // prints: docopt.Opts{"--option":interface {}(nil), "<args>":[]string{"cmd"}}
	fmt.Printf("%#v\n", err)  // prints: <nil>

	args, err = docopt.ParseArgs(usage, []string{"--option", "opt1", "cmd"}, "")
	fmt.Printf("%#v\n", args) // prints: docopt.Opts{"--option":"opt1", "<args>":[]string{"cmd"}}
	fmt.Printf("%#v\n", err)  // prints: <nil>
}

Version tag is out of date.

With the upcoming move to go modules access to new features will be inaccessible. Can there be tags for the current stable version?

Mixing up paramethers

with my current config

usage := `buggy example

Usage:
  test -d (<from> <to>)
  test -d (<from> <to>) [--site <site>]
  test -d (<from> <to>)[--geocountry <geocountry>]
  test -d (<from> <to>)[--site <site>] [--geocountry <geocountry>]


Options:
  -h --help     Show this screen.
  -d --date     Dates of report.
  -s --site     Site id of report.
  -g --geocountry  Country code of site to load from geo-ip database.`

I execute it with params -d 123 456 -g DE -s 1 and got result like

{
  "--date": true, 
  "--geocountry": true, 
  "--site": true, 
  "<from>": "123", 
  "<geocountry>": "1", 
  "<site>": "DE", 
  "<to>": "456"
}

As you could see the parameters are mixed up. The question is it expected behavior or just a bug?

Playground based on try.docopt.org
http://try.docopt.org/?doc=buggy+example%0D%0A%0D%0AUsage%3A%0D%0A++test+-d+%28%3Cfrom%3E+%3Cto%3E%29%0D%0A++test+-d+%28%3Cfrom%3E+%3Cto%3E%29+%5B--site+%3Csite%3E%5D%0D%0A++test+-d+%28%3Cfrom%3E+%3Cto%3E%29%5B--geocountry+%3Cgeocountry%3E%5D%0D%0A++test+-d+%28%3Cfrom%3E+%3Cto%3E%29%5B--site+%3Csite%3E%5D+%5B--geocountry+%3Cgeocountry%3E%5D%0D%0A%0D%0A%0D%0AOptions%3A%0D%0A++-h+--help+++++Show+this+screen.%0D%0A++-d+--date+++++Dates+of+report.%0D%0A++-s+--site+++++Site+id+of+report.%0D%0A++-g+--geocountry++Country+code+of+site+to+load+from+geo-ip+database.%0D%0A%0D%0A&argv=-d+123+456+-g+DE+-s+1

Not returning ints

The documentation says "interface{} can be bool, int, string, []string.", implying (I guess?) that numeric arguments ought to be be an int in the arguments map. However, for options such as

  --scan-intv <s>  Repository scan interval, in seconds [default: 60]

the "60" is returned as a string-typed interface{}.

question: would you be interested in a code review of the Go implementation?

Hi there,

I like the concept of documenting CLI tools the same way we do for REST APIs, but I am also a Go expert with opinions on the subject and feel like the API for the library is far from idiomatic.

I could send a bunch of PRs / issues or I could also do a video review (plus a big PR at the end) as I've previously done for my YouTube series justforfunc.

Would you be interested on any of those? Is the API fixed or do you still accept non backwards compatible improvements?

Cheers,
Francesc

When a line follows `Options:` declaration, options don't parse correctly

Description

When a non-option line comes after the Options: declaration, parsing fails in weird ways. Not only are options missing, but -h and --help are now split. This doesn't happen at try.docopt.org.

Repro

  1. Start with the options shortcut example
package main

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

func main() {
	usage := `Example of program which uses [options] shortcut in pattern.

Usage:
  options_shortcut_example [options] <port>
  options_shortcut_example -h | --help

Options:
  -h --help                show this help message and exit
  --version                show version and exit
  -n, --number N           use N as a number
  -t, --timeout TIMEOUT    set timeout TIMEOUT seconds
  --apply                  apply changes to database
  -q                       operate in quiet mode`

	arguments, _ := docopt.Parse(usage, nil, false, "1.0.0rc2", false)
	fmt.Println(arguments)
}
  1. Modify as such
 Options:
+These are things
   -h --help                show this help message and exit

Expected Behavior

$ go run options_shortcut_example.go --help
map[<port>:<nil> --help:true --version:false --number:<nil> --timeout:<nil> --apply:false -q:false]

Actual Behavior

$ go run options_shortcut_example.go --help
map[<port>:<nil> -h:false --help:true]

Ellipsis not working for anything but strings

Suppose I have this program:

package main

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

func main() {
	opts, _ := docopt.ParseDoc(`
		Usage:
		    mytool --number=<num>...`)

	var conf struct {
		Number []int
	}
	if err := opts.Bind(&conf); err != nil {
		panic(err)
	}

	fmt.Printf("%v\n", conf)
}

When executing go run mytool.go I get this error:

panic: value of "--number" is not assignable to "Number" field

goroutine 1 [running]:
main.main()
        /<path_to_script>/mytool.go:17 +0x190
exit status 2

If I change []int to []string everything works.

would like support for "cmd <source>... <destination>"

I have a cp-like command that takes multiple sources and a single destination. I'm trying to replicate that with docopt, but it consumes all of the input args for the ... match and then fails to match . The parser either needs a way to put back input tokens and try again (back tracking) or it needs to consider the next token (look ahead).

I tool a look at the source, for match(). I couldn't see any way to make this change. If you have any suggestions, I would be happy to work on a patch.

Show example of repeatable flag

Does docopt support repeating flags, like hello -e name:bob -e hair:nil? If so, could the README please add an example of the docopt string syntax for enabling this for one or more named flags?

Saner indentation handling

Normally, the docopt spec would have usage strings indenting with two spaces. However, Go formatting overall prefers a hard tab, which often conflicts with styling tools like gofmt.

When the usage string (e.g., a Go multiline backticked string) has a mix of hard and soft tabs, docopt prints a misaligned help message. While this is a cosmetic error, this conflict risks causing parse errors.

For docopt-go, could we expand the parser to treat \t as equivalent to two spaces?

Enable option to disable/enable check for non-zero values during Bind

It would be great if it was possible to disable the non-zero check during a bind, so that we can pre-populate certain values and allow for DocOpt to over-ride when necessary.

My use case is i'm using another library to first parse environment variables, and would like program arguments to over-ride when necessary.

Maybe this logic would break other things, so if there's a better suggestion to combine Environment variables with DocOpt i'd love to hear them!

List flags

I was recently surprised to discover that while arguments can be repeated, flags cannot. e.g. these are acceptable docstrings, but they don't behave as expected:

-v  verbose (specify multiple times for more verbose)
-x=<pattern>...  exclude files that match <pattern>

Neither of these cases seem to be addressed, although they're pretty common. The "verbose" case I kind of understand, since it'd take some kind of docstring magic to indicate to the parse what's meant. But the "exclude" seems to fall within the documentation as written for docopt, and instead returns a parsing error.

field could not be overwrite

when opts bind to struct, if field has default value, it could not be overwrite, but in most cases, setting default value is better I think.

Parsing errors

Something not quite right here:

$ taskcluster-proxy -h
Usage: taskcluster-proxy [(-p|--port) <port>] [--client-id <clientId> --access-token <accessToken> [--certificate <certificate>]] ([-a|--all-scopes]|([-t|--task-id <taskId>] [-s|--scopes <scope>...]))
$ taskcluster-proxy -p 4 --access-token 43434

2018/03/01 19:44:01 {
  "--access-token": true,
  "--all-scopes": false,
  "--certificate": false,
  "--client-id": false,
  "--port": false,
  "--scopes": false,
  "--task-id": false,
  "-a": false,
  "-p": true,
  "-s": false,
  "-t": false,
  "\u003caccessToken\u003e": null,
  "\u003ccertificate\u003e": null,
  "\u003cclientId\u003e": "43434",
  "\u003cport\u003e": "4",
  "\u003cscope\u003e": [],
  "\u003ctaskId\u003e": null
}

As you see, the parsing should not succeed, since --access-token is specified without --client-id, yet the parsing succeeds, and assigns the access token to the <clientId> property.

Super old release tags

Was reading README.md on github.com and tried using it with dep ensure, but found out that the "latest" release tag is 0.6.2 (from 2016).

Quick fix for me was to add branch constraint in Gopkg.toml, but it would be nice having a more up-to-date release tag.

ParseArgs fails to compile [--speed=<kn>]

On commit ee0de3b

package main

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

func main() {
	usage := `Naval Fate.

Usage:
  naval_fate.py ship [--speed=<kn>]
  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].
`

	{
		// this works
		args, err := docopt.ParseDoc(usage)
		if err != nil { panic(err) }
		fmt.Printf("%+v\n", args)
		fmt.Println(args["--speed"])
	}

	{
		// this panics
		parser := &docopt.Parser{
			HelpHandler:  docopt.PrintHelpOnly,
			OptionsFirst: true,
		}
		args, err := parser.ParseArgs(usage, os.Args[1:], "naval_fate.py")
		if err != nil { panic(err) }
		fmt.Printf("%+v\n", args)
		fmt.Println(args["--speed"])
	}
}
∀ go run docopt_wat.go ship --speed 23
map[ship:true --speed:23 --help:false --version:false]
23
Usage:
  naval_fate.py ship [--speed=<kn>]
  naval_fate.py -h | --help
  naval_fate.py --version
panic: 

goroutine 1 [running]:
main.main()
	/home/pete/docopt_wat.go:36 +0x2f6
exit status 2

Git subcommands example uses Go binary

Is there a way to properly implement subcommands without using the Go binary to actually run them? The use case is building + shipping + deploying a binary to boxes that do not have Go installed on them.

Default values not being set

Doc:

`Paxos.

Usage:
  paxos <address> <address> <address>... [--chatty=<level>] [--latency=<milliseconds>]
  paxos -h | --help
  paxos --version

Options:
  <port>        Port on which to receive connections [default: 3410].
  -c --chatty   Logging level higher value displays more logs gives the [default: 0].
  -l --latency  Latency inserted between messages duration is randomized between n and 2n [default: 1000].
  -h --help     Show this screen.`

Default value for latency is always set to go's default value of 0

--key=VAL parsing broken in example

This should succeed at parsing and print its args, but fails instead:

$ go run examples/options/options_example.go --ignore=E,W6
Usage:
  options_example [-hvqrf NAME] [--exclude=PATTERNS]
                     [--select=ERRORS | --ignore=ERRORS] [--show-source]
                     [--statistics] [--count] [--benchmark] PATH...
  options_example (--doctest | --testsuite=DIR)
  options_example --version
exit status 1

It appears argument parsing of the format --key=VAL is broken generally in docopt-go.

Array values duplicated with OneOrMore options (...)

The following usage

Usage:
  %[1]s [-u <URL>...] (-D <DOMAINFILE> [-V <VOLUMEFILE> -p <STORAGEPOOL> --start])
  %[1]s [-u <URL>...] (-V <VOLUMEFILE> [-p <STORAGEPOOL>])
  %[1]s [-u <URL>...] (--domdata <DOMAINDATA> [--voldata <VOLUMEDATA> -p <STORAGEPOOL>] --start)
  %[1]s [-u <URL>...] (--voldata <VOLUMEDATA> [-p <STORAGEPOOL>])
  %[1]s [-u <URL>...] (-R <DOMAINNAME>)

Options:
  -u, --url <URL>            server url [default: qemu:///system]
*******

with cli opts -u qemu+ssh://user@hostname/system -u test
produces array with duplicated 2-nd value: [qemu+ssh://user@hostname/system test test test test test]

invalid arg information not given, source of errors totally unclear

Consider the trivial usage:

Usage:
  naval_fate.py new <name>

and a command line of: naval_fate.py new foo bar

The resulting output simply shows usage, with identifying the source of the problem.

Usage:
  naval_fate.py new <name>

It seems not only reasonable, but required that some context for the error be given.

After looking into the code, it is clear that it's available, but not bubbled up. PR coming shortly.

Unwanted os.Exit()

Hi,

I've just started a command line application that will act as an alternative for the 'man' command. One of the planned options is [--explain ]. For example,

gman --explain "rsync -avF src dest"

In the example above, I would like to use docopt to parse the rsync options. The problem is that docopt.Parse() may call os.Exit(). For this and other use cases, that is less than ideal. It would be nice to have a Parse method that did not call os.Exit(). Something like this would be great:

func ParseContinue(doc string, argv []string, help bool, version string, optionsFirst bool) (map[string]interface{}, string, error) {
    args, output, err := parse(doc, argv, help, version, optionsFirst)
    return args, output, err
}

If I need to, of course, I can fork and add the method for my purpose, but I at least wanted to make you aware of the use case.

FWIW, calling os.Exit() also prevents the call of any defer methods. I doubt that many people will call defer before parsing command line options, but it's another possible argument for such a method.

Thanks.

[enhancement] add an error message when launching without mandatory argument

reported in docopts (shell wrapper) docopt/docopts#17

I made a go example to reproduce the behavior:

https://github.com/Sylvain303/docopts/blob/debug-issues/issues/17-Empty-error-without-mandatory-argument/atest.go

$ go run atest.go
my error: 
Usage: atest.sh <arg>
       atest.sh ( -h | --help )
       atest.sh ( -V | --version )
exit status 1

Would it be possible to have help full error in err when receiving it in error Handler:

https://github.com/Sylvain303/docopts/blob/debug-issues/issues/17-Empty-error-without-mandatory-argument/atest.go#L41

`go get github.com/docopt/docopt.go` fails because of repo's name

go get fails because of the .go extension, as it expects the package path to be a directory: stat github.com/docopt/docopt.go: no such file or directory

Plus, this is smell and a bad practice in Go packaging. Please, rename it to docopt-go, go-docopt or similar.

boolean options don't seem to work

Boolean options don't seem to work the way they should. Here is an example:

package main

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

var usage string = `
Mytest.

Usage:
	mytest [options]

Options:
  --flag	a flag
`

func main() {
	opts, _ := docopt.ParseDoc(usage)
	flag, err := opts.Bool("--flag")
	fmt.Println(flag)
	fmt.Println(err)
}
$ go run mytest.go
false
key: "--flag" failed type conversion
$ go run mytest.go --flag
--flag requires argument
Usage:
	mytest [options]
exit status 1
$ go run mytest.go --flag=true
false
key: "--flag" failed type conversion
$ go version
go version go1.14.1 linux/amd64
$ 

-vvv looks broken

Hi. After the merge of the big rewrite about a week ago it seems that [-vvv] handling is broken.
Before, something along

Options:
  mybin [-vvv] act

With args[“-v”].(int) would take values from 0 to 3 for the inputs:

  • mybin act
  • mybin -v act
  • mybin -vv act
  • mybin -vvv act

Now with the new parser and the new API args.Int(“-v”) gives a value of 0 and an error along the lines of “this is uncastable” for any amount of Vs.

I will work on creating a minimal example that uses both APIs tomorrow. I don’t remember that I found this usage in the golang examples/tests.

But then I am maybe just misusing the newer api. What do you think?
cc @aviddiviner

Can not specify a flags 2+ times

Hello, I have this docopt help-usage:

package main

import (
    "fmt"

    "github.com/docopt/docopt-go"
)

const (
    usage = `
Tool 1.0

Usage:
    tool [options] -P (-a|-r) <package>
    tool [options] -S (-a|-r) <config>

Options:
    -P --package     Package mode
        -a --add     add package
        -r --remove  remove package
    -S --service     Service mode
        -a --add     add service
        -r --remove  remove service
`
)

func main() {
    args, err := docopt.Parse(usage, nil, true, "tool 1.0", true, false)
    if err != nil {
        panic(err)
    }
    fmt.Printf("XXXXXX a.go:32: args: %#v\n", blah)
}

But when I run docopt.Parse with this usage, I got:

panic: -a is specified ambiguously 2 times

goroutine 1 [running]:
main.main()
        /home/vxw/a.go:30 +0x10a

So, this is really correct behaviour? Why usage can't have a repetable flags?

Parse docopt even if no args are passed

Hi. I am looking for a future to parse docopt even if no args were given for parser.

Real use-case - I want to get all flags (by flag I mean all options a command can receive) and use them in autocomplete.

Basically I need only keys from opts map[string]interface{} map of flags. I then filter them as I need.

Example:

doc = `Usage: somecli run [--debug] [--config=<config>] <app>
Options:
  --debug                   Run app with debug
  --config=<config>         Run with a config file
  <app>                     An app to run
`
onlyFlage := true
opts, err := docopt.ParseDoc(doc, onlyFlags)

The values of flags doesn't matter in this case and can be default values (bool, empty string, nil, etc)

I am ready to implement this feature if you are okay with an idea.
Thanks!

allow newline after Usage / Options

it just outputs the help, no error or anything, kinda confusing, having them squished looks weird but that whitespace should be optional and left up to the author anyway. Same with leading/trailing whitespace, I'm not a fan of stdio that is shoved against the terminal edges so a tool forcing me to do that is a little meh

Pre-built binaries

Could you please provide binaries for ARM? (in particular, Android and Raspberry Pi, if possible)
Thanks!

Bug: Help: false with `--help` does not return options when there are required arguments

Description

setting help: false, and providing --help, and having a required position argument at the command line causes docopt to not return any options/args and also print usage (without options)

Repro Steps

This is taken directly from the options_shortcut_example.

package main

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

func main() {
	usage := `Example of program which uses [options] shortcut in pattern.

Usage:
  options_shortcut_example [options] <port>

Options:
  -h --help                show this help message and exit
  --version                show version and exit
  -n, --number N           use N as a number
  -t, --timeout TIMEOUT    set timeout TIMEOUT seconds
  --apply                  apply changes to database
  -q                       operate in quiet mode`

	fmt.Println("*** About To Parse ****")
	arguments, _ := docopt.Parse(usage, nil, false, "1.0.0rc2", false)
	fmt.Println("*** Done Parsing ****")
	fmt.Println(arguments)
}

Expected Output

( with exit: false and help: false )

$ go run options_shortcut_example.go --help
*** About To Parse ****
*** Done Parsing ****
map[--number:<nil> --timeout:<nil> --apply:false -q:false <port>:<nil> --help:true --version:false]

From reading the docs, I expect to get options back from the parser when --help is on the commandline and help: false parse option is used. The whole idea of help: false is so I can print a custom message, or perform other work.

Actual Output

$ go run options_shortcut_example.go --help
*** About To Parse ****
Usage:
  options_shortcut_example [options] <port>
exit status 1

$ go run options_shortcut_example.go
*** About To Parse ****
Usage:
  options_shortcut_example [options] <port>
exit status 1

When exit is set to false, you can also see there are no options:

$ go run options_shortcut_example.go --help
*** About To Parse ****
Usage:
  options_shortcut_example [options] <port>
*** Done Parsing ****
map[]

This is most likely because the <port> positional argument is required, and because it wasn't provided. But that means that --help is no longer special.

Workaround

Usage:
   options_shortcut_example [-h|--help]|([options] <port>)

This seems to work, as it indicates you can either get help, or run the actual command. But this is super hacky, and defeats the elegance of this framework. It also means --help is no longer special.

Strange error when parsing usage: unmatched '(', expected: ')' got: ''

package main

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

func main() {
    usage := `cprint

Enhance terminal output without typing ANSI escape patterns

Usage:
  cprint [--attr=<attr>] --color=<color> <word>...
  cprint --showattrs
  cprint --showcolors

Options:
  --attr=<attr>     Attribute
  --color=<color>      Desired color
  --showattrs       Show attributes
  --showcolors          Show colors`

    args, err := docopt.Parse(usage, nil, true, "0.1", false)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    fmt.Println(args)
}

Print statements would indicate that somewhere along the way a parseSeq call attempts to call parseAtom and it fails because the ([ and )] characters are unbalanced in the tokenList. There is an extra ).

If I add a ) char after --showcolors the error goes away.

Bind() does not work if usage have "--" separator

If command is declared with usage like this:

command --flag -- <args>...

Bind() invokation will fail with following error:

mapping of "--" is not found in given struct, or is an unexported field

I guess -- should be filtered out when binding args.

cc @aviddiviner

Issue with short arg parsing using optional space

The docent standard supports an optional space after short args like -c 50 and should be parsed into a "-c": "50" but it currently is parsed into "-c": " 50" which has the side effect of not being able to be coerced into a int type because of the space

From docent.org

Short options can have arguments specified after optional space:
-f FILE is equivalent to -fFILE.

Unknown command locally, but works in try docopt

I have the following docopt spec:

poisson

	Usage:
		poisson
		poisson start --project=<project> [--norestore]
		poisson populatebq --project=<project>
		poisson populatemongo --project=<project>

	Options:
		-h --help			Show this screen.
		--version			Show version.
		--project=<project>	Project id.
		--norestore			If specified restore of db will not take place.

Note that this is the first time using docopt, so I could probably do something differently. With that said. When I try to run the app that has this specification with the following line:

go run main.go start --norestore --project=projectname

It fails with unknown command. If I remove the last line --norestore... from the spec if works fine. The first one also works on try docopt: http://try.docopt.org/?doc=poisson%0D%0A%0D%0A%09Usage%3A%0D%0A%09%09poisson%0D%0A%09%09poisson+start+--project%3D%3Cproject%3E+%5B--norestore%5D%0D%0A%09%09poisson+populatebq+--project%3D%3Cproject%3E%0D%0A%09%09poisson+populatemongo+--project%3D%3Cproject%3E%0D%0A%0D%0A%09Options%3A%0D%0A%09%09-h+--help%09%09%09Show+this+screen.%0D%0A%09%09--version%09%09%09Show+version.%0D%0A%09%09--project%3D%3Cproject%3E%09Project+id.%0D%0A%09%09--norestore%09%09%09If+specified+restore+of+db+will+not+take+place.&argv=start+--project%3Dasd+--norestore

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.