Skip to content
49 changes: 25 additions & 24 deletions docopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@
* Copyright (c) 2013 Vladimir Keleshev, [email protected]

"""

from __future__ import print_function

import re
import sys
import re


__all__ = ['docopt']
__version__ = '0.6.2'
__version__ = '0.6.2tj1'


class DocoptLanguageError(Exception):

"""Error in construction of usage-message by developer."""


class DocoptExit(SystemExit):

"""Exit in case user invoked program with incorrect arguments."""

usage = ''
Expand All @@ -30,6 +30,7 @@ def __init__(self, message=''):


class Pattern(object):

def __eq__(self, other):
return repr(self) == repr(other)

Expand Down Expand Up @@ -96,14 +97,11 @@ def transform(pattern):


class LeafPattern(Pattern):

"""Leaf/terminal node of a pattern tree."""
name = value = None

def __init__(self, name, value=None):
if name is not None:
self.name = name
if value is not None:
self.value = value
self.name, self.value = name, value

def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value)
Expand Down Expand Up @@ -133,6 +131,7 @@ def match(self, left, collected=None):


class BranchPattern(Pattern):

"""Branch/inner node of a pattern tree."""

def __init__(self, *children):
Expand All @@ -149,22 +148,23 @@ def flat(self, *types):


class Argument(LeafPattern):

def single_match(self, left):
for n, pattern in enumerate(left):
if type(pattern) is Argument:
return n, Argument(self.name, pattern.value)
return None, None

@classmethod
def parse(cls, source):
name = re.findall('(<\S*?>)', source)[0]
value = re.findall('\[default: (.*)\]', source, flags=re.I)
return cls(name, value[0] if value else None)
def parse(class_, source):
name = re.findall('(<\\S*?>)', source)[0]
value = re.findall('\\[default: (.*)\\]', source, flags=re.I)
return class_(name, value[0] if value else None)


class Command(Argument):

def __init__(self, name, value=False):
super(Command, self).__init__(name, value)
self.name, self.value = name, value

def single_match(self, left):
Expand All @@ -178,8 +178,8 @@ def single_match(self, left):


class Option(LeafPattern):

def __init__(self, short=None, long=None, argcount=0, value=False):
super(Option, self).__init__(None, value)
assert argcount in (0, 1)
self.short, self.long, self.argcount = short, long, argcount
self.value = None if value is False and argcount else value
Expand All @@ -197,7 +197,7 @@ def parse(class_, option_description):
else:
argcount = 1
if argcount:
matched = re.findall('\[default: (.*)\]', description, flags=re.I)
matched = re.findall('\\[default: (.*)\\]', description, flags=re.I)
value = matched[0] if matched else None
return class_(short, long, argcount, value)

Expand Down Expand Up @@ -275,20 +275,20 @@ def match(self, left, collected=None):
if matched:
outcomes.append(outcome)
if outcomes:
return min(outcomes, key=lambda _outcome: len(_outcome[1]))
return min(outcomes, key=lambda outcome: len(outcome[1]))
return False, left, collected


class Tokens(list):

def __init__(self, source, error=DocoptExit):
super(Tokens, self).__init__()
self += source.split() if hasattr(source, 'split') else source
self.error = error

@staticmethod
def from_pattern(source):
source = re.sub(r'([\[\]\(\)\|]|\.\.\.)', r' \1 ', source)
source = [s for s in re.split('\s+|(\S*<.*?>)', source) if s]
source = [s for s in re.split('\\s+|(\\S*<.*?>)', source) if s]
return Tokens(source, error=DocoptLanguageError)

def move(self):
Expand Down Expand Up @@ -404,6 +404,7 @@ def parse_atom(tokens, options):
| long | shorts | argument | command ;
"""
token = tokens.current()
result = []
if token in '([':
tokens.move()
matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token]
Expand Down Expand Up @@ -453,7 +454,7 @@ def parse_defaults(doc):
for s in parse_section('options:', doc):
# FIXME corner case "bla: options: --foo"
_, _, s = s.partition(':') # get rid of "options:"
split = re.split('\n[ \t]*(-\S+?)', '\n' + s)[1:]
split = re.split('\n[ \t]*(-\\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
Expand Down Expand Up @@ -520,7 +521,7 @@ def docopt(doc, argv=None, help=True, version=None, options_first=False):

Example
-------
>>> from docopt_c.docopt import docopt
>>> from docopt import docopt
>>> doc = '''
... Usage:
... my_program tcp <host> <port> [--timeout=<seconds>]
Expand Down Expand Up @@ -561,7 +562,7 @@ def docopt(doc, argv=None, help=True, version=None, options_first=False):
options = parse_defaults(doc)
pattern = parse_pattern(formal_usage(DocoptExit.usage), options)
# [default] syntax for argument is disabled
# for a in pattern.flat(Argument):
#for a in pattern.flat(Argument):
# same_name = [d for d in arguments if d.name == a.name]
# if same_name:
# a.value = same_name[0].value
Expand All @@ -570,7 +571,7 @@ def docopt(doc, argv=None, help=True, version=None, options_first=False):
for options_shortcut in pattern.flat(OptionsShortcut):
doc_options = parse_defaults(doc)
options_shortcut.children = list(set(doc_options) - pattern_options)
# if any_options:
#if any_options:
# options_shortcut.children += [Option(o.short, o.long, o.argcount)
# for o in argv if type(o) is Option]
extras(help, version, argv, doc)
Expand Down
Loading