Permalink
Browse files

Indentation-sensitive options-sections (see #102)

  • Loading branch information...
keleshev committed Mar 24, 2013
1 parent 9a4ea88 commit d5b96f878abbda51b62f1e28010d2b42b19a27dc
Showing with 130 additions and 150 deletions.
  1. +12 −12 docopt.py
  2. +25 −21 test_docopt.py
  3. +93 −117 testcases.docopt
View
@@ -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
@@ -345,31 +345,31 @@ 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')
with raises(DocoptExit):
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',
Oops, something went wrong.

0 comments on commit d5b96f8

Please sign in to comment.