Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Indentation-sensitive options-sections (see #102)

  • Loading branch information...
commit d5b96f878abbda51b62f1e28010d2b42b19a27dc 1 parent 9a4ea88
@keleshev keleshev authored
Showing with 130 additions and 150 deletions.
  1. +12 −12 docopt.py
  2. +25 −21 test_docopt.py
  3. +93 −117 testcases.docopt
View
24 docopt.py
@@ -187,8 +187,7 @@ class Option(ChildPattern):
def __init__(self, short=None, long=None, argcount=0, value=False):
assert argcount in (0, 1)
- self.short, self.long = short, long
- self.argcount, self.value = argcount, value
+ self.short, self.long, self.argcount = short, long, argcount
self.value = None if value is False and argcount else value
@classmethod
@@ -457,17 +456,19 @@ def parse_argv(tokens, options, options_first=False):
def parse_defaults(doc):
- # in python < 2.7 you can't pass flags=re.MULTILINE
- split = re.split('\n *(<\S+?>|-\S+?)', doc)[1:]
- split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])]
- options = [Option.parse(s) for s in split if s.startswith('-')]
- #arguments = [Argument.parse(s) for s in split if s.startswith('<')]
- #return options, arguments
- return options
+ defaults = []
+ for s in parse_section('options:', doc):
+ # FIXME corner case "bla: options: --foo"
+ _, _, s = s.partition(':') # get rid of "options:"
+ split = re.split('\n *(-\S+?)', '\n' + s)[1:]
+ split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])]
+ options = [Option.parse(s) for s in split if s.startswith('-')]
+ defaults += options
+ return defaults
def parse_section(name, source):
- pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?\n)*)',
+ pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)',
re.IGNORECASE | re.MULTILINE)
return [s.strip() for s in pattern.findall(source)]
@@ -555,8 +556,7 @@ def docopt(doc, argv=None, help=True, version=None, options_first=False):
at https://github.com/docopt/docopt#readme
"""
- if argv is None:
- argv = sys.argv[1:]
+ argv = sys.argv[1:] if argv is None else argv
usage_sections = parse_section('usage:', doc)
if len(usage_sections) == 0:
View
46 test_docopt.py
@@ -345,21 +345,21 @@ def test_long_options_error_handling():
with raises(DocoptExit):
docopt('Usage: prog', '--non-existent')
with raises(DocoptExit):
- docopt('''Usage: prog [--version --verbose]\n\n
- --version\n--verbose''', '--ver')
+ docopt('Usage: prog [--version --verbose]\n',
+ 'Options: --version\n --verbose', '--ver')
with raises(DocoptLanguageError):
- docopt('Usage: prog --long\n\n--long ARG')
+ docopt('Usage: prog --long\nOptions: --long ARG')
with raises(DocoptExit):
- docopt('Usage: prog --long ARG\n\n--long ARG', '--long')
+ docopt('Usage: prog --long ARG\nOptions: --long ARG', '--long')
with raises(DocoptLanguageError):
- docopt('Usage: prog --long=ARG\n\n--long')
+ docopt('Usage: prog --long=ARG\nOptions: --long')
with raises(DocoptExit):
- docopt('Usage: prog --long\n\n--long', '--long=ARG')
+ docopt('Usage: prog --long\nOptions: --long', '--long=ARG')
def test_short_options_error_handling():
with raises(DocoptLanguageError):
- docopt('Usage: prog -x\n\n-x this\n-x that')
+ docopt('Usage: prog -x\nOptions: -x this\n -x that')
# with raises(DocoptLanguageError):
# docopt('Usage: prog -x')
@@ -367,9 +367,9 @@ def test_short_options_error_handling():
docopt('Usage: prog', '-x')
with raises(DocoptLanguageError):
- docopt('Usage: prog -o\n\n-o ARG')
+ docopt('Usage: prog -o\nOptions: -o ARG')
with raises(DocoptExit):
- docopt('Usage: prog -o ARG\n\n-o ARG', '-o')
+ docopt('Usage: prog -o ARG\nOptions: -o ARG', '-o')
def test_matching_paren():
@@ -380,18 +380,18 @@ def test_matching_paren():
def test_allow_double_dash():
- assert docopt('usage: prog [-o] [--] <arg>\n\n-o',
+ assert docopt('usage: prog [-o] [--] <arg>\nkptions: -o',
'-- -o') == {'-o': False, '<arg>': '-o', '--': True}
- assert docopt('usage: prog [-o] [--] <arg>\n\n-o',
+ assert docopt('usage: prog [-o] [--] <arg>\nkptions: -o',
'-o 1') == {'-o': True, '<arg>': '1', '--': False}
- with raises(DocoptExit):
- docopt('usage: prog [-o] <arg>\n\n-o', '-- -o') # '--' not allowed
+ with raises(DocoptExit): # "--" is not allowed; FIXME?
+ docopt('usage: prog [-o] <arg>\noptions:-o', '-- -o')
def test_docopt():
doc = '''Usage: prog [-v] A
- -v Be verbose.'''
+ Options: -v Be verbose.'''
assert docopt(doc, 'arg') == {'-v': False, 'A': 'arg'}
assert docopt(doc, '-v arg') == {'-v': True, 'A': 'arg'}
@@ -500,10 +500,10 @@ def test_any_options_parameter():
def test_options_shortcut_does_not_add_options_to_patter_second_time():
- assert docopt('usage: prog [options] [-a]\n\n-a -b', '-a') == \
+ assert docopt('usage: prog [options] [-a]\noptions: -a\n -b', '-a') == \
{'-a': True, '-b': False}
with raises(DocoptExit):
- docopt('usage: prog [options] [-a]\n\n-a -b', '-aa')
+ docopt('usage: prog [options] [-a]\noptions: -a\n -b', '-aa')
def test_default_value_for_positional_arguments():
@@ -521,7 +521,7 @@ def test_default_value_for_positional_arguments():
#def test_parse_defaults():
# assert parse_defaults("""usage: prog
-#
+# options:
# -o, --option <o>
# --another <a> description
# [default: x]
@@ -543,7 +543,8 @@ def test_default_value_for_positional_arguments():
def test_issue_59():
assert docopt('usage: prog --long=<a>', '--long=') == {'--long': ''}
- assert docopt('usage: prog -l <a>\n\n-l <a>', ['-l', '']) == {'-l': ''}
+ assert docopt('usage: prog -l <a>\n'
+ 'options: -l <a>', ['-l', '']) == {'-l': ''}
def test_options_first():
@@ -560,7 +561,8 @@ def test_options_first():
def test_issue_68_options_shortcut_does_not_include_options_in_usage_pattern():
- args = docopt('usage: prog [-ab] [options]\n\n-x\n-y', '-ax')
+ args = docopt('usage: prog [-ab] [options]\n'
+ 'options: -x\n -y', '-ax')
# Need to use `is` (not `==`) since we want to make sure
# that they are not 1/0, but strictly True/False:
assert args['-a'] is True
@@ -581,7 +583,8 @@ def test_issue_71_double_dash_is_not_a_valid_option_argument():
with raises(DocoptExit):
docopt('usage: prog [--log=LEVEL] [--] <args>...', '--log -- 1 2')
with raises(DocoptExit):
- docopt('usage: prog [-l LEVEL] [--] <args>...\n\n-l LEVEL', '-l -- 1 2')
+ docopt('''usage: prog [-l LEVEL] [--] <args>...
+ options: -l LEVEL', '-l -- 1 2''')
usage = '''usage: this
@@ -606,7 +609,8 @@ def test_issue_71_double_dash_is_not_a_valid_option_argument():
def test_parse_section():
assert parse_section('usage:', 'foo bar fizz buzz') == []
assert parse_section('usage:', 'usage: prog') == ['usage: prog']
- print parse_section('usage:', usage)
+ assert parse_section('usage:',
+ 'usage: -x\n -y') == ['usage: -x\n -y']
assert parse_section('usage:', usage) == [
'usage: this',
'usage:hai',
View
210 testcases.docopt
@@ -10,7 +10,7 @@ $ prog --xxx
r"""Usage: prog [options]
--a All.
+Options: -a All.
"""
$ prog
@@ -25,7 +25,7 @@ $ prog -x
r"""Usage: prog [options]
---all All.
+Options: --all All.
"""
$ prog
@@ -40,7 +40,7 @@ $ prog --xxx
r"""Usage: prog [options]
--v, --verbose Verbose.
+Options: -v, --verbose Verbose.
"""
$ prog --verbose
@@ -55,7 +55,7 @@ $ prog -v
r"""Usage: prog [options]
--p PATH
+Options: -p PATH
"""
$ prog -p home/
@@ -70,7 +70,7 @@ $ prog -p
r"""Usage: prog [options]
---path <path>
+Options: --path <path>
"""
$ prog --path home/
@@ -91,7 +91,7 @@ $ prog --path
r"""Usage: prog [options]
--p PATH, --path=<path> Path to files.
+Options: -p PATH, --path=<path> Path to files.
"""
$ prog -proot
@@ -100,7 +100,7 @@ $ prog -proot
r"""Usage: prog [options]
- -p --path PATH Path to files.
+Options: -p --path PATH Path to files.
"""
$ prog -p root
@@ -112,7 +112,8 @@ $ prog --path root
r"""Usage: prog [options]
--p PATH Path to files [default: ./]
+Options:
+ -p PATH Path to files [default: ./]
"""
$ prog
@@ -124,7 +125,7 @@ $ prog -phome
r"""UsAgE: prog [options]
---path=<files> Path to files
+OpTiOnS: --path=<files> Path to files
[dEfAuLt: /root]
"""
@@ -137,9 +138,10 @@ $ prog --path=home
r"""usage: prog [options]
--a Add
--r Remote
--m <msg> Message
+options:
+ -a Add
+ -r Remote
+ -m <msg> Message
"""
$ prog -a -r -m Hello
@@ -160,8 +162,8 @@ $ prog -a -r
r"""Usage: prog [options]
---version
---verbose
+Options: --version
+ --verbose
"""
$ prog --version
@@ -182,9 +184,10 @@ $ prog --verb
r"""usage: prog [-a -r -m <msg>]
--a Add
--r Remote
--m <msg> Message
+options:
+ -a Add
+ -r Remote
+ -m <msg> Message
"""
$ prog -armyourass
@@ -195,9 +198,9 @@ $ prog -armyourass
r"""usage: prog [-armmsg]
--a Add
--r Remote
--m <msg> Message
+options: -a Add
+ -r Remote
+ -m <msg> Message
"""
$ prog -a -r -m Hello
@@ -208,8 +211,9 @@ $ prog -a -r -m Hello
r"""usage: prog -a -b
--a
--b
+options:
+ -a
+ -b
"""
$ prog -a -b
@@ -227,8 +231,8 @@ $ prog
r"""usage: prog (-a -b)
--a
--b
+options: -a
+ -b
"""
$ prog -a -b
@@ -246,8 +250,8 @@ $ prog
r"""usage: prog [-a] -b
--a
--b
+options: -a
+ -b
"""
$ prog -a -b
@@ -268,8 +272,8 @@ $ prog
r"""usage: prog [(-a -b)]
--a
--b
+options: -a
+ -b
"""
$ prog -a -b
@@ -290,8 +294,8 @@ $ prog
r"""usage: prog (-a|-b)
--a
--b
+options: -a
+ -b
"""
$ prog -a -b
@@ -309,8 +313,8 @@ $ prog -b
r"""usage: prog [ -a | -b ]
--a
--b
+options: -a
+ -b
"""
$ prog -a -b
@@ -326,9 +330,7 @@ $ prog -b
{"-a": false, "-b": true}
-r"""usage: prog <arg>
-
-"""
+r"""usage: prog <arg>"""
$ prog 10
{"<arg>": "10"}
@@ -339,9 +341,7 @@ $ prog
"user-error"
-r"""usage: prog [<arg>]
-
-"""
+r"""usage: prog [<arg>]"""
$ prog 10
{"<arg>": "10"}
@@ -352,9 +352,7 @@ $ prog
{"<arg>": null}
-r"""usage: prog <kind> <name> <type>
-
-"""
+r"""usage: prog <kind> <name> <type>"""
$ prog 10 20 40
{"<kind>": "10", "<name>": "20", "<type>": "40"}
@@ -365,9 +363,7 @@ $ prog
"user-error"
-r"""usage: prog <kind> [<name> <type>]
-
-"""
+r"""usage: prog <kind> [<name> <type>]"""
$ prog 10 20 40
{"<kind>": "10", "<name>": "20", "<type>": "40"}
@@ -378,9 +374,7 @@ $ prog
"user-error"
-r"""usage: prog [<kind> | <name> <type>]
-
-"""
+r"""usage: prog [<kind> | <name> <type>]"""
$ prog 10 20 40
"user-error"
@@ -393,7 +387,8 @@ $ prog
r"""usage: prog (<kind> --all | <name>)
---all
+options:
+ --all
"""
$ prog 10 --all
@@ -406,9 +401,7 @@ $ prog
"user-error"
-r"""usage: prog [<name> <name>]
-
-"""
+r"""usage: prog [<name> <name>]"""
$ prog 10 20
{"<name>": ["10", "20"]}
@@ -419,9 +412,7 @@ $ prog
{"<name>": []}
-r"""usage: prog [(<name> <name>)]
-
-"""
+r"""usage: prog [(<name> <name>)]"""
$ prog 10 20
{"<name>": ["10", "20"]}
@@ -432,9 +423,7 @@ $ prog
{"<name>": []}
-r"""usage: prog NAME...
-
-"""
+r"""usage: prog NAME..."""
$ prog 10 20
{"NAME": ["10", "20"]}
@@ -445,9 +434,7 @@ $ prog
"user-error"
-r"""usage: prog [NAME]...
-
-"""
+r"""usage: prog [NAME]..."""
$ prog 10 20
{"NAME": ["10", "20"]}
@@ -458,9 +445,7 @@ $ prog
{"NAME": []}
-r"""usage: prog [NAME...]
-
-"""
+r"""usage: prog [NAME...]"""
$ prog 10 20
{"NAME": ["10", "20"]}
@@ -471,9 +456,7 @@ $ prog
{"NAME": []}
-r"""usage: prog [NAME [NAME ...]]
-
-"""
+r"""usage: prog [NAME [NAME ...]]"""
$ prog 10 20
{"NAME": ["10", "20"]}
@@ -486,7 +469,7 @@ $ prog
r"""usage: prog (NAME | --foo NAME)
---foo
+options: --foo
"""
$ prog 10
@@ -501,8 +484,8 @@ $ prog --foo=10
r"""usage: prog (NAME | --foo) [--bar | NAME]
---foo
---bar
+options: --foo
+options: --bar
"""
$ prog 10
@@ -551,16 +534,12 @@ $ prog ship Guardian move 150 300 --speed=20
"shoot": false}
-r"""usage: prog --hello
-
-"""
+r"""usage: prog --hello"""
$ prog --hello
{"--hello": true}
-r"""usage: prog [--hello=<world>]
-
-"""
+r"""usage: prog [--hello=<world>]"""
$ prog
{"--hello": null}
@@ -568,9 +547,7 @@ $ prog --hello wrld
{"--hello": "wrld"}
-r"""usage: prog [-o]
-
-"""
+r"""usage: prog [-o]"""
$ prog
{"-o": false}
@@ -578,16 +555,12 @@ $ prog -o
{"-o": true}
-r"""usage: prog [-opr]
-
-"""
+r"""usage: prog [-opr]"""
$ prog -op
{"-o": true, "-p": true, "-r": false}
-r"""usage: prog --aabb | --aa
-
-"""
+r"""usage: prog --aabb | --aa"""
$ prog --aa
{"--aabb": false, "--aa": true}
@@ -598,16 +571,12 @@ $ prog --a
# Counting number of flags
#
-r"""Usage: prog -v
-
-"""
+r"""Usage: prog -v"""
$ prog -v
{"-v": true}
-r"""Usage: prog [-v -v]
-
-"""
+r"""Usage: prog [-v -v]"""
$ prog
{"-v": 0}
@@ -618,9 +587,7 @@ $ prog -vv
{"-v": 2}
-r"""Usage: prog -v ...
-
-"""
+r"""Usage: prog -v ..."""
$ prog
"user-error"
@@ -652,9 +619,7 @@ $ prog -vvvv
"user-error"
-r"""usage: prog [--ver --ver]
-
-"""
+r"""usage: prog [--ver --ver]"""
$ prog --ver --ver
{"--ver": 2}
@@ -663,16 +628,12 @@ $ prog --ver --ver
# Counting commands
#
-r"""usage: prog [go]
-
-"""
+r"""usage: prog [go]"""
$ prog go
{"go": true}
-r"""usage: prog [go go]
-
-"""
+r"""usage: prog [go go]"""
$ prog
{"go": 0}
@@ -685,9 +646,7 @@ $ prog go go
$ prog go go go
"user-error"
-r"""usage: prog go...
-
-"""
+r"""usage: prog go..."""
$ prog go go go go go
{"go": 5}
@@ -696,7 +655,7 @@ $ prog go go go go go
#
r"""Usage: prog [options] A
-
+Options:
-q Be quiet
-v Be verbose.
@@ -739,9 +698,9 @@ $ prog
#
r"""usage: prog [options]
-
--a Add
--m <msg> Message
+options:
+ -a Add
+ -m <msg> Message
"""
$ prog -a
@@ -809,13 +768,12 @@ $ prog
#
r"""usage: prog [--file=<f>]"""
-
$ prog
{"--file": null}
r"""usage: prog [--file=<f>]
---file <a>
+options: --file <a>
"""
$ prog
@@ -823,7 +781,7 @@ $ prog
r"""Usage: prog [-a <host:port>]
--a, --address <host:port> TCP address [default: localhost:6283].
+Options: -a, --address <host:port> TCP address [default: localhost:6283].
"""
$ prog
@@ -856,7 +814,7 @@ $ prog go left --speed=5 go right --speed=9
r"""usage: prog [options] -a
--a
+options: -a
"""
$ prog -a
@@ -868,7 +826,7 @@ $ prog -a
r"""usage: prog [-o <o>]...
--o <o> [default: x]
+options: -o <o> [default: x]
"""
$ prog -o this -o that
@@ -879,7 +837,7 @@ $ prog
r"""usage: prog [-o <o>]...
--o <o> [default: x y]
+options: -o <o> [default: x y]
"""
$ prog -o this
@@ -894,7 +852,7 @@ $ prog
r"""usage: prog -pPATH
--p PATH
+options: -p PATH
"""
$ prog -pHOME
@@ -927,7 +885,7 @@ $ prog --input a.txt --input=b.txt
r"""usage: prog good [options]
prog fail [options]
---loglevel=N
+options: --loglevel=N
"""
$ prog fail --loglevel 5
@@ -965,3 +923,21 @@ r"""Usage:
NOT PART OF SECTION"""
$ prog --foo
{"--foo": true, "--bar": false}
+
+#
+# Options-section syntax
+#
+
+r"""Usage: prog [options]
+
+global options: --foo
+local options: --baz
+ --bar
+other options:
+ --egg
+ --spam
+-not-an-option-
+
+"""
+$ prog --baz --egg
+{"--foo": false, "--baz": true, "--bar": false, "--egg": true, "--spam": false}
Please sign in to comment.
Something went wrong with that request. Please try again.