582 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			582 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | """Pythonic command-line interface parser that will make you smile.
 | ||
|  | 
 | ||
|  |  * http://docopt.org | ||
|  |  * Repository and issue-tracker: https://github.com/docopt/docopt | ||
|  |  * Licensed under terms of MIT license (see LICENSE-MIT) | ||
|  |  * Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com | ||
|  | 
 | ||
|  | """
 | ||
|  | import sys | ||
|  | import re | ||
|  | 
 | ||
|  | 
 | ||
|  | __all__ = ['docopt'] | ||
|  | __version__ = '0.6.2' | ||
|  | 
 | ||
|  | 
 | ||
|  | class DocoptLanguageError(Exception): | ||
|  | 
 | ||
|  |     """Error in construction of usage-message by developer.""" | ||
|  | 
 | ||
|  | 
 | ||
|  | class DocoptExit(SystemExit): | ||
|  | 
 | ||
|  |     """Exit in case user invoked program with incorrect arguments.""" | ||
|  | 
 | ||
|  |     usage = '' | ||
|  | 
 | ||
|  |     def __init__(self, message=''): | ||
|  |         SystemExit.__init__(self, (message + '\n' + self.usage).strip()) | ||
|  | 
 | ||
|  | 
 | ||
|  | class Pattern(object): | ||
|  | 
 | ||
|  |     def __eq__(self, other): | ||
|  |         return repr(self) == repr(other) | ||
|  | 
 | ||
|  |     def __hash__(self): | ||
|  |         return hash(repr(self)) | ||
|  | 
 | ||
|  |     def fix(self): | ||
|  |         self.fix_identities() | ||
|  |         self.fix_repeating_arguments() | ||
|  |         return self | ||
|  | 
 | ||
|  |     def fix_identities(self, uniq=None): | ||
|  |         """Make pattern-tree tips point to same object if they are equal.""" | ||
|  |         if not hasattr(self, 'children'): | ||
|  |             return self | ||
|  |         uniq = list(set(self.flat())) if uniq is None else uniq | ||
|  |         for i, child in enumerate(self.children): | ||
|  |             if not hasattr(child, 'children'): | ||
|  |                 assert child in uniq | ||
|  |                 self.children[i] = uniq[uniq.index(child)] | ||
|  |             else: | ||
|  |                 child.fix_identities(uniq) | ||
|  | 
 | ||
|  |     def fix_repeating_arguments(self): | ||
|  |         """Fix elements that should accumulate/increment values.""" | ||
|  |         either = [list(child.children) for child in transform(self).children] | ||
|  |         for case in either: | ||
|  |             for e in [child for child in case if case.count(child) > 1]: | ||
|  |                 if type(e) is Argument or type(e) is Option and e.argcount: | ||
|  |                     if e.value is None: | ||
|  |                         e.value = [] | ||
|  |                     elif type(e.value) is not list: | ||
|  |                         e.value = e.value.split() | ||
|  |                 if type(e) is Command or type(e) is Option and e.argcount == 0: | ||
|  |                     e.value = 0 | ||
|  |         return self | ||
|  | 
 | ||
|  | 
 | ||
|  | def transform(pattern): | ||
|  |     """Expand pattern into an (almost) equivalent one, but with single Either.
 | ||
|  | 
 | ||
|  |     Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d) | ||
|  |     Quirks: [-a] => (-a), (-a...) => (-a -a) | ||
|  | 
 | ||
|  |     """
 | ||
|  |     result = [] | ||
|  |     groups = [[pattern]] | ||
|  |     while groups: | ||
|  |         children = groups.pop(0) | ||
|  |         parents = [Required, Optional, OptionsShortcut, Either, OneOrMore] | ||
|  |         if any(t in map(type, children) for t in parents): | ||
|  |             child = [c for c in children if type(c) in parents][0] | ||
|  |             children.remove(child) | ||
|  |             if type(child) is Either: | ||
|  |                 for c in child.children: | ||
|  |                     groups.append([c] + children) | ||
|  |             elif type(child) is OneOrMore: | ||
|  |                 groups.append(child.children * 2 + children) | ||
|  |             else: | ||
|  |                 groups.append(child.children + children) | ||
|  |         else: | ||
|  |             result.append(children) | ||
|  |     return Either(*[Required(*e) for e in result]) | ||
|  | 
 | ||
|  | 
 | ||
|  | class LeafPattern(Pattern): | ||
|  | 
 | ||
|  |     """Leaf/terminal node of a pattern tree.""" | ||
|  | 
 | ||
|  |     def __init__(self, name, value=None): | ||
|  |         self.name, self.value = name, value | ||
|  | 
 | ||
|  |     def __repr__(self): | ||
|  |         return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value) | ||
|  | 
 | ||
|  |     def flat(self, *types): | ||
|  |         return [self] if not types or type(self) in types else [] | ||
|  | 
 | ||
|  |     def match(self, left, collected=None): | ||
|  |         collected = [] if collected is None else collected | ||
|  |         pos, match = self.single_match(left) | ||
|  |         if match is None: | ||
|  |             return False, left, collected | ||
|  |         left_ = left[:pos] + left[pos + 1:] | ||
|  |         same_name = [a for a in collected if a.name == self.name] | ||
|  |         if type(self.value) in (int, list): | ||
|  |             if type(self.value) is int: | ||
|  |                 increment = 1 | ||
|  |             else: | ||
|  |                 increment = ([match.value] if type(match.value) is str | ||
|  |                              else match.value) | ||
|  |             if not same_name: | ||
|  |                 match.value = increment | ||
|  |                 return True, left_, collected + [match] | ||
|  |             same_name[0].value += increment | ||
|  |             return True, left_, collected | ||
|  |         return True, left_, collected + [match] | ||
|  | 
 | ||
|  | 
 | ||
|  | class BranchPattern(Pattern): | ||
|  | 
 | ||
|  |     """Branch/inner node of a pattern tree.""" | ||
|  | 
 | ||
|  |     def __init__(self, *children): | ||
|  |         self.children = list(children) | ||
|  | 
 | ||
|  |     def __repr__(self): | ||
|  |         return '%s(%s)' % (self.__class__.__name__, | ||
|  |                            ', '.join(repr(a) for a in self.children)) | ||
|  | 
 | ||
|  |     def flat(self, *types): | ||
|  |         if type(self) in types: | ||
|  |             return [self] | ||
|  |         return sum([child.flat(*types) for child in self.children], []) | ||
|  | 
 | ||
|  | 
 | ||
|  | 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(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): | ||
|  |         self.name, self.value = name, value | ||
|  | 
 | ||
|  |     def single_match(self, left): | ||
|  |         for n, pattern in enumerate(left): | ||
|  |             if type(pattern) is Argument: | ||
|  |                 if pattern.value == self.name: | ||
|  |                     return n, Command(self.name, True) | ||
|  |                 else: | ||
|  |                     break | ||
|  |         return None, None | ||
|  | 
 | ||
|  | 
 | ||
|  | class Option(LeafPattern): | ||
|  | 
 | ||
|  |     def __init__(self, short=None, long=None, argcount=0, value=False): | ||
|  |         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 | ||
|  | 
 | ||
|  |     @classmethod | ||
|  |     def parse(class_, option_description): | ||
|  |         short, long, argcount, value = None, None, 0, False | ||
|  |         options, _, description = option_description.strip().partition('  ') | ||
|  |         options = options.replace(',', ' ').replace('=', ' ') | ||
|  |         for s in options.split(): | ||
|  |             if s.startswith('--'): | ||
|  |                 long = s | ||
|  |             elif s.startswith('-'): | ||
|  |                 short = s | ||
|  |             else: | ||
|  |                 argcount = 1 | ||
|  |         if argcount: | ||
|  |             matched = re.findall('\[default: (.*)\]', description, flags=re.I) | ||
|  |             value = matched[0] if matched else None | ||
|  |         return class_(short, long, argcount, value) | ||
|  | 
 | ||
|  |     def single_match(self, left): | ||
|  |         for n, pattern in enumerate(left): | ||
|  |             if self.name == pattern.name: | ||
|  |                 return n, pattern | ||
|  |         return None, None | ||
|  | 
 | ||
|  |     @property | ||
|  |     def name(self): | ||
|  |         return self.long or self.short | ||
|  | 
 | ||
|  |     def __repr__(self): | ||
|  |         return 'Option(%r, %r, %r, %r)' % (self.short, self.long, | ||
|  |                                            self.argcount, self.value) | ||
|  | 
 | ||
|  | 
 | ||
|  | class Required(BranchPattern): | ||
|  | 
 | ||
|  |     def match(self, left, collected=None): | ||
|  |         collected = [] if collected is None else collected | ||
|  |         l = left | ||
|  |         c = collected | ||
|  |         for pattern in self.children: | ||
|  |             matched, l, c = pattern.match(l, c) | ||
|  |             if not matched: | ||
|  |                 return False, left, collected | ||
|  |         return True, l, c | ||
|  | 
 | ||
|  | 
 | ||
|  | class Optional(BranchPattern): | ||
|  | 
 | ||
|  |     def match(self, left, collected=None): | ||
|  |         collected = [] if collected is None else collected | ||
|  |         for pattern in self.children: | ||
|  |             m, left, collected = pattern.match(left, collected) | ||
|  |         return True, left, collected | ||
|  | 
 | ||
|  | 
 | ||
|  | class OptionsShortcut(Optional): | ||
|  | 
 | ||
|  |     """Marker/placeholder for [options] shortcut.""" | ||
|  | 
 | ||
|  | 
 | ||
|  | class OneOrMore(BranchPattern): | ||
|  | 
 | ||
|  |     def match(self, left, collected=None): | ||
|  |         assert len(self.children) == 1 | ||
|  |         collected = [] if collected is None else collected | ||
|  |         l = left | ||
|  |         c = collected | ||
|  |         l_ = None | ||
|  |         matched = True | ||
|  |         times = 0 | ||
|  |         while matched: | ||
|  |             # could it be that something didn't match but changed l or c? | ||
|  |             matched, l, c = self.children[0].match(l, c) | ||
|  |             times += 1 if matched else 0 | ||
|  |             if l_ == l: | ||
|  |                 break | ||
|  |             l_ = l | ||
|  |         if times >= 1: | ||
|  |             return True, l, c | ||
|  |         return False, left, collected | ||
|  | 
 | ||
|  | 
 | ||
|  | class Either(BranchPattern): | ||
|  | 
 | ||
|  |     def match(self, left, collected=None): | ||
|  |         collected = [] if collected is None else collected | ||
|  |         outcomes = [] | ||
|  |         for pattern in self.children: | ||
|  |             matched, _, _ = outcome = pattern.match(left, collected) | ||
|  |             if matched: | ||
|  |                 outcomes.append(outcome) | ||
|  |         if outcomes: | ||
|  |             return min(outcomes, key=lambda outcome: len(outcome[1])) | ||
|  |         return False, left, collected | ||
|  | 
 | ||
|  | 
 | ||
|  | class Tokens(list): | ||
|  | 
 | ||
|  |     def __init__(self, source, error=DocoptExit): | ||
|  |         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] | ||
|  |         return Tokens(source, error=DocoptLanguageError) | ||
|  | 
 | ||
|  |     def move(self): | ||
|  |         return self.pop(0) if len(self) else None | ||
|  | 
 | ||
|  |     def current(self): | ||
|  |         return self[0] if len(self) else None | ||
|  | 
 | ||
|  | 
 | ||
|  | def parse_long(tokens, options): | ||
|  |     """long ::= '--' chars [ ( ' ' | '=' ) chars ] ;""" | ||
|  |     long, eq, value = tokens.move().partition('=') | ||
|  |     assert long.startswith('--') | ||
|  |     value = None if eq == value == '' else value | ||
|  |     similar = [o for o in options if o.long == long] | ||
|  |     if tokens.error is DocoptExit and similar == []:  # if no exact match | ||
|  |         similar = [o for o in options if o.long and o.long.startswith(long)] | ||
|  |     if len(similar) > 1:  # might be simply specified ambiguously 2+ times? | ||
|  |         raise tokens.error('%s is not a unique prefix: %s?' % | ||
|  |                            (long, ', '.join(o.long for o in similar))) | ||
|  |     elif len(similar) < 1: | ||
|  |         argcount = 1 if eq == '=' else 0 | ||
|  |         o = Option(None, long, argcount) | ||
|  |         options.append(o) | ||
|  |         if tokens.error is DocoptExit: | ||
|  |             o = Option(None, long, argcount, value if argcount else True) | ||
|  |     else: | ||
|  |         o = Option(similar[0].short, similar[0].long, | ||
|  |                    similar[0].argcount, similar[0].value) | ||
|  |         if o.argcount == 0: | ||
|  |             if value is not None: | ||
|  |                 raise tokens.error('%s must not have an argument' % o.long) | ||
|  |         else: | ||
|  |             if value is None: | ||
|  |                 if tokens.current() in [None, '--']: | ||
|  |                     raise tokens.error('%s requires argument' % o.long) | ||
|  |                 value = tokens.move() | ||
|  |         if tokens.error is DocoptExit: | ||
|  |             o.value = value if value is not None else True | ||
|  |     return [o] | ||
|  | 
 | ||
|  | 
 | ||
|  | def parse_shorts(tokens, options): | ||
|  |     """shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;""" | ||
|  |     token = tokens.move() | ||
|  |     assert token.startswith('-') and not token.startswith('--') | ||
|  |     left = token.lstrip('-') | ||
|  |     parsed = [] | ||
|  |     while left != '': | ||
|  |         short, left = '-' + left[0], left[1:] | ||
|  |         similar = [o for o in options if o.short == short] | ||
|  |         if len(similar) > 1: | ||
|  |             raise tokens.error('%s is specified ambiguously %d times' % | ||
|  |                                (short, len(similar))) | ||
|  |         elif len(similar) < 1: | ||
|  |             o = Option(short, None, 0) | ||
|  |             options.append(o) | ||
|  |             if tokens.error is DocoptExit: | ||
|  |                 o = Option(short, None, 0, True) | ||
|  |         else:  # why copying is necessary here? | ||
|  |             o = Option(short, similar[0].long, | ||
|  |                        similar[0].argcount, similar[0].value) | ||
|  |             value = None | ||
|  |             if o.argcount != 0: | ||
|  |                 if left == '': | ||
|  |                     if tokens.current() in [None, '--']: | ||
|  |                         raise tokens.error('%s requires argument' % short) | ||
|  |                     value = tokens.move() | ||
|  |                 else: | ||
|  |                     value = left | ||
|  |                     left = '' | ||
|  |             if tokens.error is DocoptExit: | ||
|  |                 o.value = value if value is not None else True | ||
|  |         parsed.append(o) | ||
|  |     return parsed | ||
|  | 
 | ||
|  | 
 | ||
|  | def parse_pattern(source, options): | ||
|  |     tokens = Tokens.from_pattern(source) | ||
|  |     result = parse_expr(tokens, options) | ||
|  |     if tokens.current() is not None: | ||
|  |         raise tokens.error('unexpected ending: %r' % ' '.join(tokens)) | ||
|  |     return Required(*result) | ||
|  | 
 | ||
|  | 
 | ||
|  | def parse_expr(tokens, options): | ||
|  |     """expr ::= seq ( '|' seq )* ;""" | ||
|  |     seq = parse_seq(tokens, options) | ||
|  |     if tokens.current() != '|': | ||
|  |         return seq | ||
|  |     result = [Required(*seq)] if len(seq) > 1 else seq | ||
|  |     while tokens.current() == '|': | ||
|  |         tokens.move() | ||
|  |         seq = parse_seq(tokens, options) | ||
|  |         result += [Required(*seq)] if len(seq) > 1 else seq | ||
|  |     return [Either(*result)] if len(result) > 1 else result | ||
|  | 
 | ||
|  | 
 | ||
|  | def parse_seq(tokens, options): | ||
|  |     """seq ::= ( atom [ '...' ] )* ;""" | ||
|  |     result = [] | ||
|  |     while tokens.current() not in [None, ']', ')', '|']: | ||
|  |         atom = parse_atom(tokens, options) | ||
|  |         if tokens.current() == '...': | ||
|  |             atom = [OneOrMore(*atom)] | ||
|  |             tokens.move() | ||
|  |         result += atom | ||
|  |     return result | ||
|  | 
 | ||
|  | 
 | ||
|  | def parse_atom(tokens, options): | ||
|  |     """atom ::= '(' expr ')' | '[' expr ']' | 'options'
 | ||
|  |              | long | shorts | argument | command ; | ||
|  |     """
 | ||
|  |     token = tokens.current() | ||
|  |     result = [] | ||
|  |     if token in '([': | ||
|  |         tokens.move() | ||
|  |         matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token] | ||
|  |         result = pattern(*parse_expr(tokens, options)) | ||
|  |         if tokens.move() != matching: | ||
|  |             raise tokens.error("unmatched '%s'" % token) | ||
|  |         return [result] | ||
|  |     elif token == 'options': | ||
|  |         tokens.move() | ||
|  |         return [OptionsShortcut()] | ||
|  |     elif token.startswith('--') and token != '--': | ||
|  |         return parse_long(tokens, options) | ||
|  |     elif token.startswith('-') and token not in ('-', '--'): | ||
|  |         return parse_shorts(tokens, options) | ||
|  |     elif token.startswith('<') and token.endswith('>') or token.isupper(): | ||
|  |         return [Argument(tokens.move())] | ||
|  |     else: | ||
|  |         return [Command(tokens.move())] | ||
|  | 
 | ||
|  | 
 | ||
|  | def parse_argv(tokens, options, options_first=False): | ||
|  |     """Parse command-line argument vector.
 | ||
|  | 
 | ||
|  |     If options_first: | ||
|  |         argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ; | ||
|  |     else: | ||
|  |         argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ; | ||
|  | 
 | ||
|  |     """
 | ||
|  |     parsed = [] | ||
|  |     while tokens.current() is not None: | ||
|  |         if tokens.current() == '--': | ||
|  |             return parsed + [Argument(None, v) for v in tokens] | ||
|  |         elif tokens.current().startswith('--'): | ||
|  |             parsed += parse_long(tokens, options) | ||
|  |         elif tokens.current().startswith('-') and tokens.current() != '-': | ||
|  |             parsed += parse_shorts(tokens, options) | ||
|  |         elif options_first: | ||
|  |             return parsed + [Argument(None, v) for v in tokens] | ||
|  |         else: | ||
|  |             parsed.append(Argument(None, tokens.move())) | ||
|  |     return parsed | ||
|  | 
 | ||
|  | 
 | ||
|  | def parse_defaults(doc): | ||
|  |     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[ \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 | ||
|  |     return defaults | ||
|  | 
 | ||
|  | 
 | ||
|  | def parse_section(name, source): | ||
|  |     pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)', | ||
|  |                          re.IGNORECASE | re.MULTILINE) | ||
|  |     return [s.strip() for s in pattern.findall(source)] | ||
|  | 
 | ||
|  | 
 | ||
|  | def formal_usage(section): | ||
|  |     _, _, section = section.partition(':')  # drop "usage:" | ||
|  |     pu = section.split() | ||
|  |     return '( ' + ' '.join(') | (' if s == pu[0] else s for s in pu[1:]) + ' )' | ||
|  | 
 | ||
|  | 
 | ||
|  | def extras(help, version, options, doc): | ||
|  |     if help and any((o.name in ('-h', '--help')) and o.value for o in options): | ||
|  |         print(doc.strip("\n")) | ||
|  |         sys.exit() | ||
|  |     if version and any(o.name == '--version' and o.value for o in options): | ||
|  |         print(version) | ||
|  |         sys.exit() | ||
|  | 
 | ||
|  | 
 | ||
|  | class Dict(dict): | ||
|  |     def __repr__(self): | ||
|  |         return '{%s}' % ',\n '.join('%r: %r' % i for i in sorted(self.items())) | ||
|  | 
 | ||
|  | 
 | ||
|  | def docopt(doc, argv=None, help=True, version=None, options_first=False): | ||
|  |     """Parse `argv` based on command-line interface described in `doc`.
 | ||
|  | 
 | ||
|  |     `docopt` creates your command-line interface based on its | ||
|  |     description that you pass as `doc`. Such description can contain | ||
|  |     --options, <positional-argument>, commands, which could be | ||
|  |     [optional], (required), (mutually | exclusive) or repeated... | ||
|  | 
 | ||
|  |     Parameters | ||
|  |     ---------- | ||
|  |     doc : str | ||
|  |         Description of your command-line interface. | ||
|  |     argv : list of str, optional | ||
|  |         Argument vector to be parsed. sys.argv[1:] is used if not | ||
|  |         provided. | ||
|  |     help : bool (default: True) | ||
|  |         Set to False to disable automatic help on -h or --help | ||
|  |         options. | ||
|  |     version : any object | ||
|  |         If passed, the object will be printed if --version is in | ||
|  |         `argv`. | ||
|  |     options_first : bool (default: False) | ||
|  |         Set to True to require options precede positional arguments, | ||
|  |         i.e. to forbid options and positional arguments intermix. | ||
|  | 
 | ||
|  |     Returns | ||
|  |     ------- | ||
|  |     args : dict | ||
|  |         A dictionary, where keys are names of command-line elements | ||
|  |         such as e.g. "--verbose" and "<path>", and values are the | ||
|  |         parsed values of those elements. | ||
|  | 
 | ||
|  |     Example | ||
|  |     ------- | ||
|  |     >>> from docopt import docopt | ||
|  |     >>> doc = '''
 | ||
|  |     ... Usage: | ||
|  |     ...     my_program tcp <host> <port> [--timeout=<seconds>] | ||
|  |     ...     my_program serial <port> [--baud=<n>] [--timeout=<seconds>] | ||
|  |     ...     my_program (-h | --help | --version) | ||
|  |     ... | ||
|  |     ... Options: | ||
|  |     ...     -h, --help  Show this screen and exit. | ||
|  |     ...     --baud=<n>  Baudrate [default: 9600] | ||
|  |     ... '''
 | ||
|  |     >>> argv = ['tcp', '127.0.0.1', '80', '--timeout', '30'] | ||
|  |     >>> docopt(doc, argv) | ||
|  |     {'--baud': '9600', | ||
|  |      '--help': False, | ||
|  |      '--timeout': '30', | ||
|  |      '--version': False, | ||
|  |      '<host>': '127.0.0.1', | ||
|  |      '<port>': '80', | ||
|  |      'serial': False, | ||
|  |      'tcp': True} | ||
|  | 
 | ||
|  |     See also | ||
|  |     -------- | ||
|  |     * For video introduction see http://docopt.org | ||
|  |     * Full documentation is available in README.rst as well as online | ||
|  |       at https://github.com/docopt/docopt#readme | ||
|  | 
 | ||
|  |     """
 | ||
|  |     argv = sys.argv[1:] if argv is None else argv | ||
|  | 
 | ||
|  |     usage_sections = parse_section('usage:', doc) | ||
|  |     if len(usage_sections) == 0: | ||
|  |         raise DocoptLanguageError('"usage:" (case-insensitive) not found.') | ||
|  |     if len(usage_sections) > 1: | ||
|  |         raise DocoptLanguageError('More than one "usage:" (case-insensitive).') | ||
|  |     DocoptExit.usage = usage_sections[0] | ||
|  | 
 | ||
|  |     options = parse_defaults(doc) | ||
|  |     pattern = parse_pattern(formal_usage(DocoptExit.usage), options) | ||
|  |     # [default] syntax for argument is disabled | ||
|  |     #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 | ||
|  |     argv = parse_argv(Tokens(argv), list(options), options_first) | ||
|  |     pattern_options = set(pattern.flat(Option)) | ||
|  |     for options_shortcut in pattern.flat(OptionsShortcut): | ||
|  |         doc_options = parse_defaults(doc) | ||
|  |         options_shortcut.children = list(set(doc_options) - pattern_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) | ||
|  |     matched, left, collected = pattern.fix().match(argv) | ||
|  |     if matched and left == []:  # better error message if left? | ||
|  |         return Dict((a.name, a.value) for a in (pattern.flat() + collected)) | ||
|  |     raise DocoptExit() |