moving files
This commit is contained in:
		
							
								
								
									
										581
									
								
								q1/01dec/docopt.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										581
									
								
								q1/01dec/docopt.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,581 @@ | ||||
| """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() | ||||
		Reference in New Issue
	
	Block a user