git repos / blockattack-game

blame: source/misc/embedded_libs/fmt-8.1.1/support/docopt.py

normal view · raw

f8deb5b9 Poul Sander 2022-01-15 13:55 1
"""Pythonic command-line interface parser that will make you smile.
f8deb5b9 Poul Sander 2022-01-15 13:55 2
f8deb5b9 Poul Sander 2022-01-15 13:55 3
 * http://docopt.org
f8deb5b9 Poul Sander 2022-01-15 13:55 4
 * Repository and issue-tracker: https://github.com/docopt/docopt
f8deb5b9 Poul Sander 2022-01-15 13:55 5
 * Licensed under terms of MIT license (see LICENSE-MIT)
f8deb5b9 Poul Sander 2022-01-15 13:55 6
 * Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com
f8deb5b9 Poul Sander 2022-01-15 13:55 7
f8deb5b9 Poul Sander 2022-01-15 13:55 8
"""
f8deb5b9 Poul Sander 2022-01-15 13:55 9
import sys
f8deb5b9 Poul Sander 2022-01-15 13:55 10
import re
f8deb5b9 Poul Sander 2022-01-15 13:55 11
f8deb5b9 Poul Sander 2022-01-15 13:55 12
f8deb5b9 Poul Sander 2022-01-15 13:55 13
__all__ = ['docopt']
f8deb5b9 Poul Sander 2022-01-15 13:55 14
__version__ = '0.6.1'
f8deb5b9 Poul Sander 2022-01-15 13:55 15
f8deb5b9 Poul Sander 2022-01-15 13:55 16
f8deb5b9 Poul Sander 2022-01-15 13:55 17
class DocoptLanguageError(Exception):
f8deb5b9 Poul Sander 2022-01-15 13:55 18
f8deb5b9 Poul Sander 2022-01-15 13:55 19
    """Error in construction of usage-message by developer."""
f8deb5b9 Poul Sander 2022-01-15 13:55 20
f8deb5b9 Poul Sander 2022-01-15 13:55 21
f8deb5b9 Poul Sander 2022-01-15 13:55 22
class DocoptExit(SystemExit):
f8deb5b9 Poul Sander 2022-01-15 13:55 23
f8deb5b9 Poul Sander 2022-01-15 13:55 24
    """Exit in case user invoked program with incorrect arguments."""
f8deb5b9 Poul Sander 2022-01-15 13:55 25
f8deb5b9 Poul Sander 2022-01-15 13:55 26
    usage = ''
f8deb5b9 Poul Sander 2022-01-15 13:55 27
f8deb5b9 Poul Sander 2022-01-15 13:55 28
    def __init__(self, message=''):
f8deb5b9 Poul Sander 2022-01-15 13:55 29
        SystemExit.__init__(self, (message + '\n' + self.usage).strip())
f8deb5b9 Poul Sander 2022-01-15 13:55 30
f8deb5b9 Poul Sander 2022-01-15 13:55 31
f8deb5b9 Poul Sander 2022-01-15 13:55 32
class Pattern(object):
f8deb5b9 Poul Sander 2022-01-15 13:55 33
f8deb5b9 Poul Sander 2022-01-15 13:55 34
    def __eq__(self, other):
f8deb5b9 Poul Sander 2022-01-15 13:55 35
        return repr(self) == repr(other)
f8deb5b9 Poul Sander 2022-01-15 13:55 36
f8deb5b9 Poul Sander 2022-01-15 13:55 37
    def __hash__(self):
f8deb5b9 Poul Sander 2022-01-15 13:55 38
        return hash(repr(self))
f8deb5b9 Poul Sander 2022-01-15 13:55 39
f8deb5b9 Poul Sander 2022-01-15 13:55 40
    def fix(self):
f8deb5b9 Poul Sander 2022-01-15 13:55 41
        self.fix_identities()
f8deb5b9 Poul Sander 2022-01-15 13:55 42
        self.fix_repeating_arguments()
f8deb5b9 Poul Sander 2022-01-15 13:55 43
        return self
f8deb5b9 Poul Sander 2022-01-15 13:55 44
f8deb5b9 Poul Sander 2022-01-15 13:55 45
    def fix_identities(self, uniq=None):
f8deb5b9 Poul Sander 2022-01-15 13:55 46
        """Make pattern-tree tips point to same object if they are equal."""
f8deb5b9 Poul Sander 2022-01-15 13:55 47
        if not hasattr(self, 'children'):
f8deb5b9 Poul Sander 2022-01-15 13:55 48
            return self
f8deb5b9 Poul Sander 2022-01-15 13:55 49
        uniq = list(set(self.flat())) if uniq is None else uniq
f8deb5b9 Poul Sander 2022-01-15 13:55 50
        for i, child in enumerate(self.children):
f8deb5b9 Poul Sander 2022-01-15 13:55 51
            if not hasattr(child, 'children'):
f8deb5b9 Poul Sander 2022-01-15 13:55 52
                assert child in uniq
f8deb5b9 Poul Sander 2022-01-15 13:55 53
                self.children[i] = uniq[uniq.index(child)]
f8deb5b9 Poul Sander 2022-01-15 13:55 54
            else:
f8deb5b9 Poul Sander 2022-01-15 13:55 55
                child.fix_identities(uniq)
f8deb5b9 Poul Sander 2022-01-15 13:55 56
f8deb5b9 Poul Sander 2022-01-15 13:55 57
    def fix_repeating_arguments(self):
f8deb5b9 Poul Sander 2022-01-15 13:55 58
        """Fix elements that should accumulate/increment values."""
f8deb5b9 Poul Sander 2022-01-15 13:55 59
        either = [list(child.children) for child in transform(self).children]
f8deb5b9 Poul Sander 2022-01-15 13:55 60
        for case in either:
f8deb5b9 Poul Sander 2022-01-15 13:55 61
            for e in [child for child in case if case.count(child) > 1]:
f8deb5b9 Poul Sander 2022-01-15 13:55 62
                if type(e) is Argument or type(e) is Option and e.argcount:
f8deb5b9 Poul Sander 2022-01-15 13:55 63
                    if e.value is None:
f8deb5b9 Poul Sander 2022-01-15 13:55 64
                        e.value = []
f8deb5b9 Poul Sander 2022-01-15 13:55 65
                    elif type(e.value) is not list:
f8deb5b9 Poul Sander 2022-01-15 13:55 66
                        e.value = e.value.split()
f8deb5b9 Poul Sander 2022-01-15 13:55 67
                if type(e) is Command or type(e) is Option and e.argcount == 0:
f8deb5b9 Poul Sander 2022-01-15 13:55 68
                    e.value = 0
f8deb5b9 Poul Sander 2022-01-15 13:55 69
        return self
f8deb5b9 Poul Sander 2022-01-15 13:55 70
f8deb5b9 Poul Sander 2022-01-15 13:55 71
f8deb5b9 Poul Sander 2022-01-15 13:55 72
def transform(pattern):
f8deb5b9 Poul Sander 2022-01-15 13:55 73
    """Expand pattern into an (almost) equivalent one, but with single Either.
f8deb5b9 Poul Sander 2022-01-15 13:55 74
f8deb5b9 Poul Sander 2022-01-15 13:55 75
    Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d)
f8deb5b9 Poul Sander 2022-01-15 13:55 76
    Quirks: [-a] => (-a), (-a...) => (-a -a)
f8deb5b9 Poul Sander 2022-01-15 13:55 77
f8deb5b9 Poul Sander 2022-01-15 13:55 78
    """
f8deb5b9 Poul Sander 2022-01-15 13:55 79
    result = []
f8deb5b9 Poul Sander 2022-01-15 13:55 80
    groups = [[pattern]]
f8deb5b9 Poul Sander 2022-01-15 13:55 81
    while groups:
f8deb5b9 Poul Sander 2022-01-15 13:55 82
        children = groups.pop(0)
f8deb5b9 Poul Sander 2022-01-15 13:55 83
        parents = [Required, Optional, OptionsShortcut, Either, OneOrMore]
f8deb5b9 Poul Sander 2022-01-15 13:55 84
        if any(t in map(type, children) for t in parents):
f8deb5b9 Poul Sander 2022-01-15 13:55 85
            child = [c for c in children if type(c) in parents][0]
f8deb5b9 Poul Sander 2022-01-15 13:55 86
            children.remove(child)
f8deb5b9 Poul Sander 2022-01-15 13:55 87
            if type(child) is Either:
f8deb5b9 Poul Sander 2022-01-15 13:55 88
                for c in child.children:
f8deb5b9 Poul Sander 2022-01-15 13:55 89
                    groups.append([c] + children)
f8deb5b9 Poul Sander 2022-01-15 13:55 90
            elif type(child) is OneOrMore:
f8deb5b9 Poul Sander 2022-01-15 13:55 91
                groups.append(child.children * 2 + children)
f8deb5b9 Poul Sander 2022-01-15 13:55 92
            else:
f8deb5b9 Poul Sander 2022-01-15 13:55 93
                groups.append(child.children + children)
f8deb5b9 Poul Sander 2022-01-15 13:55 94
        else:
f8deb5b9 Poul Sander 2022-01-15 13:55 95
            result.append(children)
f8deb5b9 Poul Sander 2022-01-15 13:55 96
    return Either(*[Required(*e) for e in result])
f8deb5b9 Poul Sander 2022-01-15 13:55 97
f8deb5b9 Poul Sander 2022-01-15 13:55 98
f8deb5b9 Poul Sander 2022-01-15 13:55 99
class LeafPattern(Pattern):
f8deb5b9 Poul Sander 2022-01-15 13:55 100
f8deb5b9 Poul Sander 2022-01-15 13:55 101
    """Leaf/terminal node of a pattern tree."""
f8deb5b9 Poul Sander 2022-01-15 13:55 102
f8deb5b9 Poul Sander 2022-01-15 13:55 103
    def __init__(self, name, value=None):
f8deb5b9 Poul Sander 2022-01-15 13:55 104
        self.name, self.value = name, value
f8deb5b9 Poul Sander 2022-01-15 13:55 105
f8deb5b9 Poul Sander 2022-01-15 13:55 106
    def __repr__(self):
f8deb5b9 Poul Sander 2022-01-15 13:55 107
        return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value)
f8deb5b9 Poul Sander 2022-01-15 13:55 108
f8deb5b9 Poul Sander 2022-01-15 13:55 109
    def flat(self, *types):
f8deb5b9 Poul Sander 2022-01-15 13:55 110
        return [self] if not types or type(self) in types else []
f8deb5b9 Poul Sander 2022-01-15 13:55 111
f8deb5b9 Poul Sander 2022-01-15 13:55 112
    def match(self, left, collected=None):
f8deb5b9 Poul Sander 2022-01-15 13:55 113
        collected = [] if collected is None else collected
f8deb5b9 Poul Sander 2022-01-15 13:55 114
        pos, match = self.single_match(left)
f8deb5b9 Poul Sander 2022-01-15 13:55 115
        if match is None:
f8deb5b9 Poul Sander 2022-01-15 13:55 116
            return False, left, collected
f8deb5b9 Poul Sander 2022-01-15 13:55 117
        left_ = left[:pos] + left[pos + 1:]
f8deb5b9 Poul Sander 2022-01-15 13:55 118
        same_name = [a for a in collected if a.name == self.name]
f8deb5b9 Poul Sander 2022-01-15 13:55 119
        if type(self.value) in (int, list):
f8deb5b9 Poul Sander 2022-01-15 13:55 120
            if type(self.value) is int:
f8deb5b9 Poul Sander 2022-01-15 13:55 121
                increment = 1
f8deb5b9 Poul Sander 2022-01-15 13:55 122
            else:
f8deb5b9 Poul Sander 2022-01-15 13:55 123
                increment = ([match.value] if type(match.value) is str
f8deb5b9 Poul Sander 2022-01-15 13:55 124
                             else match.value)
f8deb5b9 Poul Sander 2022-01-15 13:55 125
            if not same_name:
f8deb5b9 Poul Sander 2022-01-15 13:55 126
                match.value = increment
f8deb5b9 Poul Sander 2022-01-15 13:55 127
                return True, left_, collected + [match]
f8deb5b9 Poul Sander 2022-01-15 13:55 128
            same_name[0].value += increment
f8deb5b9 Poul Sander 2022-01-15 13:55 129
            return True, left_, collected
f8deb5b9 Poul Sander 2022-01-15 13:55 130
        return True, left_, collected + [match]
f8deb5b9 Poul Sander 2022-01-15 13:55 131
f8deb5b9 Poul Sander 2022-01-15 13:55 132
f8deb5b9 Poul Sander 2022-01-15 13:55 133
class BranchPattern(Pattern):
f8deb5b9 Poul Sander 2022-01-15 13:55 134
f8deb5b9 Poul Sander 2022-01-15 13:55 135
    """Branch/inner node of a pattern tree."""
f8deb5b9 Poul Sander 2022-01-15 13:55 136
f8deb5b9 Poul Sander 2022-01-15 13:55 137
    def __init__(self, *children):
f8deb5b9 Poul Sander 2022-01-15 13:55 138
        self.children = list(children)
f8deb5b9 Poul Sander 2022-01-15 13:55 139
f8deb5b9 Poul Sander 2022-01-15 13:55 140
    def __repr__(self):
f8deb5b9 Poul Sander 2022-01-15 13:55 141
        return '%s(%s)' % (self.__class__.__name__,
f8deb5b9 Poul Sander 2022-01-15 13:55 142
                           ', '.join(repr(a) for a in self.children))
f8deb5b9 Poul Sander 2022-01-15 13:55 143
f8deb5b9 Poul Sander 2022-01-15 13:55 144
    def flat(self, *types):
f8deb5b9 Poul Sander 2022-01-15 13:55 145
        if type(self) in types:
f8deb5b9 Poul Sander 2022-01-15 13:55 146
            return [self]
f8deb5b9 Poul Sander 2022-01-15 13:55 147
        return sum([child.flat(*types) for child in self.children], [])
f8deb5b9 Poul Sander 2022-01-15 13:55 148
f8deb5b9 Poul Sander 2022-01-15 13:55 149
f8deb5b9 Poul Sander 2022-01-15 13:55 150
class Argument(LeafPattern):
f8deb5b9 Poul Sander 2022-01-15 13:55 151
f8deb5b9 Poul Sander 2022-01-15 13:55 152
    def single_match(self, left):
f8deb5b9 Poul Sander 2022-01-15 13:55 153
        for n, pattern in enumerate(left):
f8deb5b9 Poul Sander 2022-01-15 13:55 154
            if type(pattern) is Argument:
f8deb5b9 Poul Sander 2022-01-15 13:55 155
                return n, Argument(self.name, pattern.value)
f8deb5b9 Poul Sander 2022-01-15 13:55 156
        return None, None
f8deb5b9 Poul Sander 2022-01-15 13:55 157
f8deb5b9 Poul Sander 2022-01-15 13:55 158
    @classmethod
f8deb5b9 Poul Sander 2022-01-15 13:55 159
    def parse(class_, source):
f8deb5b9 Poul Sander 2022-01-15 13:55 160
        name = re.findall('(<\S*?>)', source)[0]
f8deb5b9 Poul Sander 2022-01-15 13:55 161
        value = re.findall('\[default: (.*)\]', source, flags=re.I)
f8deb5b9 Poul Sander 2022-01-15 13:55 162
        return class_(name, value[0] if value else None)
f8deb5b9 Poul Sander 2022-01-15 13:55 163
f8deb5b9 Poul Sander 2022-01-15 13:55 164
f8deb5b9 Poul Sander 2022-01-15 13:55 165
class Command(Argument):
f8deb5b9 Poul Sander 2022-01-15 13:55 166
f8deb5b9 Poul Sander 2022-01-15 13:55 167
    def __init__(self, name, value=False):
f8deb5b9 Poul Sander 2022-01-15 13:55 168
        self.name, self.value = name, value
f8deb5b9 Poul Sander 2022-01-15 13:55 169
f8deb5b9 Poul Sander 2022-01-15 13:55 170
    def single_match(self, left):
f8deb5b9 Poul Sander 2022-01-15 13:55 171
        for n, pattern in enumerate(left):
f8deb5b9 Poul Sander 2022-01-15 13:55 172
            if type(pattern) is Argument:
f8deb5b9 Poul Sander 2022-01-15 13:55 173
                if pattern.value == self.name:
f8deb5b9 Poul Sander 2022-01-15 13:55 174
                    return n, Command(self.name, True)
f8deb5b9 Poul Sander 2022-01-15 13:55 175
                else:
f8deb5b9 Poul Sander 2022-01-15 13:55 176
                    break
f8deb5b9 Poul Sander 2022-01-15 13:55 177
        return None, None
f8deb5b9 Poul Sander 2022-01-15 13:55 178
f8deb5b9 Poul Sander 2022-01-15 13:55 179
f8deb5b9 Poul Sander 2022-01-15 13:55 180
class Option(LeafPattern):
f8deb5b9 Poul Sander 2022-01-15 13:55 181
f8deb5b9 Poul Sander 2022-01-15 13:55 182
    def __init__(self, short=None, long=None, argcount=0, value=False):
f8deb5b9 Poul Sander 2022-01-15 13:55 183
        assert argcount in (0, 1)
f8deb5b9 Poul Sander 2022-01-15 13:55 184
        self.short, self.long, self.argcount = short, long, argcount
f8deb5b9 Poul Sander 2022-01-15 13:55 185
        self.value = None if value is False and argcount else value
f8deb5b9 Poul Sander 2022-01-15 13:55 186
f8deb5b9 Poul Sander 2022-01-15 13:55 187
    @classmethod
f8deb5b9 Poul Sander 2022-01-15 13:55 188
    def parse(class_, option_description):
f8deb5b9 Poul Sander 2022-01-15 13:55 189
        short, long, argcount, value = None, None, 0, False
f8deb5b9 Poul Sander 2022-01-15 13:55 190
        options, _, description = option_description.strip().partition('  ')
f8deb5b9 Poul Sander 2022-01-15 13:55 191
        options = options.replace(',', ' ').replace('=', ' ')
f8deb5b9 Poul Sander 2022-01-15 13:55 192
        for s in options.split():
f8deb5b9 Poul Sander 2022-01-15 13:55 193
            if s.startswith('--'):
f8deb5b9 Poul Sander 2022-01-15 13:55 194
                long = s
f8deb5b9 Poul Sander 2022-01-15 13:55 195
            elif s.startswith('-'):
f8deb5b9 Poul Sander 2022-01-15 13:55 196
                short = s
f8deb5b9 Poul Sander 2022-01-15 13:55 197
            else:
f8deb5b9 Poul Sander 2022-01-15 13:55 198
                argcount = 1
f8deb5b9 Poul Sander 2022-01-15 13:55 199
        if argcount:
f8deb5b9 Poul Sander 2022-01-15 13:55 200
            matched = re.findall('\[default: (.*)\]', description, flags=re.I)
f8deb5b9 Poul Sander 2022-01-15 13:55 201
            value = matched[0] if matched else None
f8deb5b9 Poul Sander 2022-01-15 13:55 202
        return class_(short, long, argcount, value)
f8deb5b9 Poul Sander 2022-01-15 13:55 203
f8deb5b9 Poul Sander 2022-01-15 13:55 204
    def single_match(self, left):
f8deb5b9 Poul Sander 2022-01-15 13:55 205
        for n, pattern in enumerate(left):
f8deb5b9 Poul Sander 2022-01-15 13:55 206
            if self.name == pattern.name:
f8deb5b9 Poul Sander 2022-01-15 13:55 207
                return n, pattern
f8deb5b9 Poul Sander 2022-01-15 13:55 208
        return None, None
f8deb5b9 Poul Sander 2022-01-15 13:55 209
f8deb5b9 Poul Sander 2022-01-15 13:55 210
    @property
f8deb5b9 Poul Sander 2022-01-15 13:55 211
    def name(self):
f8deb5b9 Poul Sander 2022-01-15 13:55 212
        return self.long or self.short
f8deb5b9 Poul Sander 2022-01-15 13:55 213
f8deb5b9 Poul Sander 2022-01-15 13:55 214
    def __repr__(self):
f8deb5b9 Poul Sander 2022-01-15 13:55 215
        return 'Option(%r, %r, %r, %r)' % (self.short, self.long,
f8deb5b9 Poul Sander 2022-01-15 13:55 216
                                           self.argcount, self.value)
f8deb5b9 Poul Sander 2022-01-15 13:55 217
f8deb5b9 Poul Sander 2022-01-15 13:55 218
f8deb5b9 Poul Sander 2022-01-15 13:55 219
class Required(BranchPattern):
f8deb5b9 Poul Sander 2022-01-15 13:55 220
f8deb5b9 Poul Sander 2022-01-15 13:55 221
    def match(self, left, collected=None):
f8deb5b9 Poul Sander 2022-01-15 13:55 222
        collected = [] if collected is None else collected
f8deb5b9 Poul Sander 2022-01-15 13:55 223
        l = left
f8deb5b9 Poul Sander 2022-01-15 13:55 224
        c = collected
f8deb5b9 Poul Sander 2022-01-15 13:55 225
        for pattern in self.children:
f8deb5b9 Poul Sander 2022-01-15 13:55 226
            matched, l, c = pattern.match(l, c)
f8deb5b9 Poul Sander 2022-01-15 13:55 227
            if not matched:
f8deb5b9 Poul Sander 2022-01-15 13:55 228
                return False, left, collected
f8deb5b9 Poul Sander 2022-01-15 13:55 229
        return True, l, c
f8deb5b9 Poul Sander 2022-01-15 13:55 230
f8deb5b9 Poul Sander 2022-01-15 13:55 231
f8deb5b9 Poul Sander 2022-01-15 13:55 232
class Optional(BranchPattern):
f8deb5b9 Poul Sander 2022-01-15 13:55 233
f8deb5b9 Poul Sander 2022-01-15 13:55 234
    def match(self, left, collected=None):
f8deb5b9 Poul Sander 2022-01-15 13:55 235
        collected = [] if collected is None else collected
f8deb5b9 Poul Sander 2022-01-15 13:55 236
        for pattern in self.children:
f8deb5b9 Poul Sander 2022-01-15 13:55 237
            m, left, collected = pattern.match(left, collected)
f8deb5b9 Poul Sander 2022-01-15 13:55 238
        return True, left, collected
f8deb5b9 Poul Sander 2022-01-15 13:55 239
f8deb5b9 Poul Sander 2022-01-15 13:55 240
f8deb5b9 Poul Sander 2022-01-15 13:55 241
class OptionsShortcut(Optional):
f8deb5b9 Poul Sander 2022-01-15 13:55 242
f8deb5b9 Poul Sander 2022-01-15 13:55 243
    """Marker/placeholder for [options] shortcut."""
f8deb5b9 Poul Sander 2022-01-15 13:55 244
f8deb5b9 Poul Sander 2022-01-15 13:55 245
f8deb5b9 Poul Sander 2022-01-15 13:55 246
class OneOrMore(BranchPattern):
f8deb5b9 Poul Sander 2022-01-15 13:55 247
f8deb5b9 Poul Sander 2022-01-15 13:55 248
    def match(self, left, collected=None):
f8deb5b9 Poul Sander 2022-01-15 13:55 249
        assert len(self.children) == 1
f8deb5b9 Poul Sander 2022-01-15 13:55 250
        collected = [] if collected is None else collected
f8deb5b9 Poul Sander 2022-01-15 13:55 251
        l = left
f8deb5b9 Poul Sander 2022-01-15 13:55 252
        c = collected
f8deb5b9 Poul Sander 2022-01-15 13:55 253
        l_ = None
f8deb5b9 Poul Sander 2022-01-15 13:55 254
        matched = True
f8deb5b9 Poul Sander 2022-01-15 13:55 255
        times = 0
f8deb5b9 Poul Sander 2022-01-15 13:55 256
        while matched:
f8deb5b9 Poul Sander 2022-01-15 13:55 257
            # could it be that something didn't match but changed l or c?
f8deb5b9 Poul Sander 2022-01-15 13:55 258
            matched, l, c = self.children[0].match(l, c)
f8deb5b9 Poul Sander 2022-01-15 13:55 259
            times += 1 if matched else 0
f8deb5b9 Poul Sander 2022-01-15 13:55 260
            if l_ == l:
f8deb5b9 Poul Sander 2022-01-15 13:55 261
                break
f8deb5b9 Poul Sander 2022-01-15 13:55 262
            l_ = l
f8deb5b9 Poul Sander 2022-01-15 13:55 263
        if times >= 1:
f8deb5b9 Poul Sander 2022-01-15 13:55 264
            return True, l, c
f8deb5b9 Poul Sander 2022-01-15 13:55 265
        return False, left, collected
f8deb5b9 Poul Sander 2022-01-15 13:55 266
f8deb5b9 Poul Sander 2022-01-15 13:55 267
f8deb5b9 Poul Sander 2022-01-15 13:55 268
class Either(BranchPattern):
f8deb5b9 Poul Sander 2022-01-15 13:55 269
f8deb5b9 Poul Sander 2022-01-15 13:55 270
    def match(self, left, collected=None):
f8deb5b9 Poul Sander 2022-01-15 13:55 271
        collected = [] if collected is None else collected
f8deb5b9 Poul Sander 2022-01-15 13:55 272
        outcomes = []
f8deb5b9 Poul Sander 2022-01-15 13:55 273
        for pattern in self.children:
f8deb5b9 Poul Sander 2022-01-15 13:55 274
            matched, _, _ = outcome = pattern.match(left, collected)
f8deb5b9 Poul Sander 2022-01-15 13:55 275
            if matched:
f8deb5b9 Poul Sander 2022-01-15 13:55 276
                outcomes.append(outcome)
f8deb5b9 Poul Sander 2022-01-15 13:55 277
        if outcomes:
f8deb5b9 Poul Sander 2022-01-15 13:55 278
            return min(outcomes, key=lambda outcome: len(outcome[1]))
f8deb5b9 Poul Sander 2022-01-15 13:55 279
        return False, left, collected
f8deb5b9 Poul Sander 2022-01-15 13:55 280
f8deb5b9 Poul Sander 2022-01-15 13:55 281
f8deb5b9 Poul Sander 2022-01-15 13:55 282
class Tokens(list):
f8deb5b9 Poul Sander 2022-01-15 13:55 283
f8deb5b9 Poul Sander 2022-01-15 13:55 284
    def __init__(self, source, error=DocoptExit):
f8deb5b9 Poul Sander 2022-01-15 13:55 285
        self += source.split() if hasattr(source, 'split') else source
f8deb5b9 Poul Sander 2022-01-15 13:55 286
        self.error = error
f8deb5b9 Poul Sander 2022-01-15 13:55 287
f8deb5b9 Poul Sander 2022-01-15 13:55 288
    @staticmethod
f8deb5b9 Poul Sander 2022-01-15 13:55 289
    def from_pattern(source):
f8deb5b9 Poul Sander 2022-01-15 13:55 290
        source = re.sub(r'([\[\]\(\)\|]|\.\.\.)', r' \1 ', source)
f8deb5b9 Poul Sander 2022-01-15 13:55 291
        source = [s for s in re.split('\s+|(\S*<.*?>)', source) if s]
f8deb5b9 Poul Sander 2022-01-15 13:55 292
        return Tokens(source, error=DocoptLanguageError)
f8deb5b9 Poul Sander 2022-01-15 13:55 293
f8deb5b9 Poul Sander 2022-01-15 13:55 294
    def move(self):
f8deb5b9 Poul Sander 2022-01-15 13:55 295
        return self.pop(0) if len(self) else None
f8deb5b9 Poul Sander 2022-01-15 13:55 296
f8deb5b9 Poul Sander 2022-01-15 13:55 297
    def current(self):
f8deb5b9 Poul Sander 2022-01-15 13:55 298
        return self[0] if len(self) else None
f8deb5b9 Poul Sander 2022-01-15 13:55 299
f8deb5b9 Poul Sander 2022-01-15 13:55 300
f8deb5b9 Poul Sander 2022-01-15 13:55 301
def parse_long(tokens, options):
f8deb5b9 Poul Sander 2022-01-15 13:55 302
    """long ::= '--' chars [ ( ' ' | '=' ) chars ] ;"""
f8deb5b9 Poul Sander 2022-01-15 13:55 303
    long, eq, value = tokens.move().partition('=')
f8deb5b9 Poul Sander 2022-01-15 13:55 304
    assert long.startswith('--')
f8deb5b9 Poul Sander 2022-01-15 13:55 305
    value = None if eq == value == '' else value
f8deb5b9 Poul Sander 2022-01-15 13:55 306
    similar = [o for o in options if o.long == long]
f8deb5b9 Poul Sander 2022-01-15 13:55 307
    if tokens.error is DocoptExit and similar == []:  # if no exact match
f8deb5b9 Poul Sander 2022-01-15 13:55 308
        similar = [o for o in options if o.long and o.long.startswith(long)]
f8deb5b9 Poul Sander 2022-01-15 13:55 309
    if len(similar) > 1:  # might be simply specified ambiguously 2+ times?
f8deb5b9 Poul Sander 2022-01-15 13:55 310
        raise tokens.error('%s is not a unique prefix: %s?' %
f8deb5b9 Poul Sander 2022-01-15 13:55 311
                           (long, ', '.join(o.long for o in similar)))
f8deb5b9 Poul Sander 2022-01-15 13:55 312
    elif len(similar) < 1:
f8deb5b9 Poul Sander 2022-01-15 13:55 313
        argcount = 1 if eq == '=' else 0
f8deb5b9 Poul Sander 2022-01-15 13:55 314
        o = Option(None, long, argcount)
f8deb5b9 Poul Sander 2022-01-15 13:55 315
        options.append(o)
f8deb5b9 Poul Sander 2022-01-15 13:55 316
        if tokens.error is DocoptExit:
f8deb5b9 Poul Sander 2022-01-15 13:55 317
            o = Option(None, long, argcount, value if argcount else True)
f8deb5b9 Poul Sander 2022-01-15 13:55 318
    else:
f8deb5b9 Poul Sander 2022-01-15 13:55 319
        o = Option(similar[0].short, similar[0].long,
f8deb5b9 Poul Sander 2022-01-15 13:55 320
                   similar[0].argcount, similar[0].value)
f8deb5b9 Poul Sander 2022-01-15 13:55 321
        if o.argcount == 0:
f8deb5b9 Poul Sander 2022-01-15 13:55 322
            if value is not None:
f8deb5b9 Poul Sander 2022-01-15 13:55 323
                raise tokens.error('%s must not have an argument' % o.long)
f8deb5b9 Poul Sander 2022-01-15 13:55 324
        else:
f8deb5b9 Poul Sander 2022-01-15 13:55 325
            if value is None:
f8deb5b9 Poul Sander 2022-01-15 13:55 326
                if tokens.current() in [None, '--']:
f8deb5b9 Poul Sander 2022-01-15 13:55 327
                    raise tokens.error('%s requires argument' % o.long)
f8deb5b9 Poul Sander 2022-01-15 13:55 328
                value = tokens.move()
f8deb5b9 Poul Sander 2022-01-15 13:55 329
        if tokens.error is DocoptExit:
f8deb5b9 Poul Sander 2022-01-15 13:55 330
            o.value = value if value is not None else True
f8deb5b9 Poul Sander 2022-01-15 13:55 331
    return [o]
f8deb5b9 Poul Sander 2022-01-15 13:55 332
f8deb5b9 Poul Sander 2022-01-15 13:55 333
f8deb5b9 Poul Sander 2022-01-15 13:55 334
def parse_shorts(tokens, options):
f8deb5b9 Poul Sander 2022-01-15 13:55 335
    """shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;"""
f8deb5b9 Poul Sander 2022-01-15 13:55 336
    token = tokens.move()
f8deb5b9 Poul Sander 2022-01-15 13:55 337
    assert token.startswith('-') and not token.startswith('--')
f8deb5b9 Poul Sander 2022-01-15 13:55 338
    left = token.lstrip('-')
f8deb5b9 Poul Sander 2022-01-15 13:55 339
    parsed = []
f8deb5b9 Poul Sander 2022-01-15 13:55 340
    while left != '':
f8deb5b9 Poul Sander 2022-01-15 13:55 341
        short, left = '-' + left[0], left[1:]
f8deb5b9 Poul Sander 2022-01-15 13:55 342
        similar = [o for o in options if o.short == short]
f8deb5b9 Poul Sander 2022-01-15 13:55 343
        if len(similar) > 1:
f8deb5b9 Poul Sander 2022-01-15 13:55 344
            raise tokens.error('%s is specified ambiguously %d times' %
f8deb5b9 Poul Sander 2022-01-15 13:55 345
                               (short, len(similar)))
f8deb5b9 Poul Sander 2022-01-15 13:55 346
        elif len(similar) < 1:
f8deb5b9 Poul Sander 2022-01-15 13:55 347
            o = Option(short, None, 0)
f8deb5b9 Poul Sander 2022-01-15 13:55 348
            options.append(o)
f8deb5b9 Poul Sander 2022-01-15 13:55 349
            if tokens.error is DocoptExit:
f8deb5b9 Poul Sander 2022-01-15 13:55 350
                o = Option(short, None, 0, True)
f8deb5b9 Poul Sander 2022-01-15 13:55 351
        else:  # why copying is necessary here?
f8deb5b9 Poul Sander 2022-01-15 13:55 352
            o = Option(short, similar[0].long,
f8deb5b9 Poul Sander 2022-01-15 13:55 353
                       similar[0].argcount, similar[0].value)
f8deb5b9 Poul Sander 2022-01-15 13:55 354
            value = None
f8deb5b9 Poul Sander 2022-01-15 13:55 355
            if o.argcount != 0:
f8deb5b9 Poul Sander 2022-01-15 13:55 356
                if left == '':
f8deb5b9 Poul Sander 2022-01-15 13:55 357
                    if tokens.current() in [None, '--']:
f8deb5b9 Poul Sander 2022-01-15 13:55 358
                        raise tokens.error('%s requires argument' % short)
f8deb5b9 Poul Sander 2022-01-15 13:55 359
                    value = tokens.move()
f8deb5b9 Poul Sander 2022-01-15 13:55 360
                else:
f8deb5b9 Poul Sander 2022-01-15 13:55 361
                    value = left
f8deb5b9 Poul Sander 2022-01-15 13:55 362
                    left = ''
f8deb5b9 Poul Sander 2022-01-15 13:55 363
            if tokens.error is DocoptExit:
f8deb5b9 Poul Sander 2022-01-15 13:55 364
                o.value = value if value is not None else True
f8deb5b9 Poul Sander 2022-01-15 13:55 365
        parsed.append(o)
f8deb5b9 Poul Sander 2022-01-15 13:55 366
    return parsed
f8deb5b9 Poul Sander 2022-01-15 13:55 367
f8deb5b9 Poul Sander 2022-01-15 13:55 368
f8deb5b9 Poul Sander 2022-01-15 13:55 369
def parse_pattern(source, options):
f8deb5b9 Poul Sander 2022-01-15 13:55 370
    tokens = Tokens.from_pattern(source)
f8deb5b9 Poul Sander 2022-01-15 13:55 371
    result = parse_expr(tokens, options)
f8deb5b9 Poul Sander 2022-01-15 13:55 372
    if tokens.current() is not None:
f8deb5b9 Poul Sander 2022-01-15 13:55 373
        raise tokens.error('unexpected ending: %r' % ' '.join(tokens))
f8deb5b9 Poul Sander 2022-01-15 13:55 374
    return Required(*result)
f8deb5b9 Poul Sander 2022-01-15 13:55 375
f8deb5b9 Poul Sander 2022-01-15 13:55 376
f8deb5b9 Poul Sander 2022-01-15 13:55 377
def parse_expr(tokens, options):
f8deb5b9 Poul Sander 2022-01-15 13:55 378
    """expr ::= seq ( '|' seq )* ;"""
f8deb5b9 Poul Sander 2022-01-15 13:55 379
    seq = parse_seq(tokens, options)
f8deb5b9 Poul Sander 2022-01-15 13:55 380
    if tokens.current() != '|':
f8deb5b9 Poul Sander 2022-01-15 13:55 381
        return seq
f8deb5b9 Poul Sander 2022-01-15 13:55 382
    result = [Required(*seq)] if len(seq) > 1 else seq
f8deb5b9 Poul Sander 2022-01-15 13:55 383
    while tokens.current() == '|':
f8deb5b9 Poul Sander 2022-01-15 13:55 384
        tokens.move()
f8deb5b9 Poul Sander 2022-01-15 13:55 385
        seq = parse_seq(tokens, options)
f8deb5b9 Poul Sander 2022-01-15 13:55 386
        result += [Required(*seq)] if len(seq) > 1 else seq
f8deb5b9 Poul Sander 2022-01-15 13:55 387
    return [Either(*result)] if len(result) > 1 else result
f8deb5b9 Poul Sander 2022-01-15 13:55 388
f8deb5b9 Poul Sander 2022-01-15 13:55 389
f8deb5b9 Poul Sander 2022-01-15 13:55 390
def parse_seq(tokens, options):
f8deb5b9 Poul Sander 2022-01-15 13:55 391
    """seq ::= ( atom [ '...' ] )* ;"""
f8deb5b9 Poul Sander 2022-01-15 13:55 392
    result = []
f8deb5b9 Poul Sander 2022-01-15 13:55 393
    while tokens.current() not in [None, ']', ')', '|']:
f8deb5b9 Poul Sander 2022-01-15 13:55 394
        atom = parse_atom(tokens, options)
f8deb5b9 Poul Sander 2022-01-15 13:55 395
        if tokens.current() == '...':
f8deb5b9 Poul Sander 2022-01-15 13:55 396
            atom = [OneOrMore(*atom)]
f8deb5b9 Poul Sander 2022-01-15 13:55 397
            tokens.move()
f8deb5b9 Poul Sander 2022-01-15 13:55 398
        result += atom
f8deb5b9 Poul Sander 2022-01-15 13:55 399
    return result
f8deb5b9 Poul Sander 2022-01-15 13:55 400
f8deb5b9 Poul Sander 2022-01-15 13:55 401
f8deb5b9 Poul Sander 2022-01-15 13:55 402
def parse_atom(tokens, options):
f8deb5b9 Poul Sander 2022-01-15 13:55 403
    """atom ::= '(' expr ')' | '[' expr ']' | 'options'
f8deb5b9 Poul Sander 2022-01-15 13:55 404
             | long | shorts | argument | command ;
f8deb5b9 Poul Sander 2022-01-15 13:55 405
    """
f8deb5b9 Poul Sander 2022-01-15 13:55 406
    token = tokens.current()
f8deb5b9 Poul Sander 2022-01-15 13:55 407
    result = []
f8deb5b9 Poul Sander 2022-01-15 13:55 408
    if token in '([':
f8deb5b9 Poul Sander 2022-01-15 13:55 409
        tokens.move()
f8deb5b9 Poul Sander 2022-01-15 13:55 410
        matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token]
f8deb5b9 Poul Sander 2022-01-15 13:55 411
        result = pattern(*parse_expr(tokens, options))
f8deb5b9 Poul Sander 2022-01-15 13:55 412
        if tokens.move() != matching:
f8deb5b9 Poul Sander 2022-01-15 13:55 413
            raise tokens.error("unmatched '%s'" % token)
f8deb5b9 Poul Sander 2022-01-15 13:55 414
        return [result]
f8deb5b9 Poul Sander 2022-01-15 13:55 415
    elif token == 'options':
f8deb5b9 Poul Sander 2022-01-15 13:55 416
        tokens.move()
f8deb5b9 Poul Sander 2022-01-15 13:55 417
        return [OptionsShortcut()]
f8deb5b9 Poul Sander 2022-01-15 13:55 418
    elif token.startswith('--') and token != '--':
f8deb5b9 Poul Sander 2022-01-15 13:55 419
        return parse_long(tokens, options)
f8deb5b9 Poul Sander 2022-01-15 13:55 420
    elif token.startswith('-') and token not in ('-', '--'):
f8deb5b9 Poul Sander 2022-01-15 13:55 421
        return parse_shorts(tokens, options)
f8deb5b9 Poul Sander 2022-01-15 13:55 422
    elif token.startswith('<') and token.endswith('>') or token.isupper():
f8deb5b9 Poul Sander 2022-01-15 13:55 423
        return [Argument(tokens.move())]
f8deb5b9 Poul Sander 2022-01-15 13:55 424
    else:
f8deb5b9 Poul Sander 2022-01-15 13:55 425
        return [Command(tokens.move())]
f8deb5b9 Poul Sander 2022-01-15 13:55 426
f8deb5b9 Poul Sander 2022-01-15 13:55 427
f8deb5b9 Poul Sander 2022-01-15 13:55 428
def parse_argv(tokens, options, options_first=False):
f8deb5b9 Poul Sander 2022-01-15 13:55 429
    """Parse command-line argument vector.
f8deb5b9 Poul Sander 2022-01-15 13:55 430
f8deb5b9 Poul Sander 2022-01-15 13:55 431
    If options_first:
f8deb5b9 Poul Sander 2022-01-15 13:55 432
        argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ;
f8deb5b9 Poul Sander 2022-01-15 13:55 433
    else:
f8deb5b9 Poul Sander 2022-01-15 13:55 434
        argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ;
f8deb5b9 Poul Sander 2022-01-15 13:55 435
f8deb5b9 Poul Sander 2022-01-15 13:55 436
    """
f8deb5b9 Poul Sander 2022-01-15 13:55 437
    parsed = []
f8deb5b9 Poul Sander 2022-01-15 13:55 438
    while tokens.current() is not None:
f8deb5b9 Poul Sander 2022-01-15 13:55 439
        if tokens.current() == '--':
f8deb5b9 Poul Sander 2022-01-15 13:55 440
            return parsed + [Argument(None, v) for v in tokens]
f8deb5b9 Poul Sander 2022-01-15 13:55 441
        elif tokens.current().startswith('--'):
f8deb5b9 Poul Sander 2022-01-15 13:55 442
            parsed += parse_long(tokens, options)
f8deb5b9 Poul Sander 2022-01-15 13:55 443
        elif tokens.current().startswith('-') and tokens.current() != '-':
f8deb5b9 Poul Sander 2022-01-15 13:55 444
            parsed += parse_shorts(tokens, options)
f8deb5b9 Poul Sander 2022-01-15 13:55 445
        elif options_first:
f8deb5b9 Poul Sander 2022-01-15 13:55 446
            return parsed + [Argument(None, v) for v in tokens]
f8deb5b9 Poul Sander 2022-01-15 13:55 447
        else:
f8deb5b9 Poul Sander 2022-01-15 13:55 448
            parsed.append(Argument(None, tokens.move()))
f8deb5b9 Poul Sander 2022-01-15 13:55 449
    return parsed
f8deb5b9 Poul Sander 2022-01-15 13:55 450
f8deb5b9 Poul Sander 2022-01-15 13:55 451
f8deb5b9 Poul Sander 2022-01-15 13:55 452
def parse_defaults(doc):
f8deb5b9 Poul Sander 2022-01-15 13:55 453
    defaults = []
f8deb5b9 Poul Sander 2022-01-15 13:55 454
    for s in parse_section('options:', doc):
f8deb5b9 Poul Sander 2022-01-15 13:55 455
        # FIXME corner case "bla: options: --foo"
f8deb5b9 Poul Sander 2022-01-15 13:55 456
        _, _, s = s.partition(':')  # get rid of "options:"
f8deb5b9 Poul Sander 2022-01-15 13:55 457
        split = re.split('\n[ \t]*(-\S+?)', '\n' + s)[1:]
f8deb5b9 Poul Sander 2022-01-15 13:55 458
        split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])]
f8deb5b9 Poul Sander 2022-01-15 13:55 459
        options = [Option.parse(s) for s in split if s.startswith('-')]
f8deb5b9 Poul Sander 2022-01-15 13:55 460
        defaults += options
f8deb5b9 Poul Sander 2022-01-15 13:55 461
    return defaults
f8deb5b9 Poul Sander 2022-01-15 13:55 462
f8deb5b9 Poul Sander 2022-01-15 13:55 463
f8deb5b9 Poul Sander 2022-01-15 13:55 464
def parse_section(name, source):
f8deb5b9 Poul Sander 2022-01-15 13:55 465
    pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)',
f8deb5b9 Poul Sander 2022-01-15 13:55 466
                         re.IGNORECASE | re.MULTILINE)
f8deb5b9 Poul Sander 2022-01-15 13:55 467
    return [s.strip() for s in pattern.findall(source)]
f8deb5b9 Poul Sander 2022-01-15 13:55 468
f8deb5b9 Poul Sander 2022-01-15 13:55 469
f8deb5b9 Poul Sander 2022-01-15 13:55 470
def formal_usage(section):
f8deb5b9 Poul Sander 2022-01-15 13:55 471
    _, _, section = section.partition(':')  # drop "usage:"
f8deb5b9 Poul Sander 2022-01-15 13:55 472
    pu = section.split()
f8deb5b9 Poul Sander 2022-01-15 13:55 473
    return '( ' + ' '.join(') | (' if s == pu[0] else s for s in pu[1:]) + ' )'
f8deb5b9 Poul Sander 2022-01-15 13:55 474
f8deb5b9 Poul Sander 2022-01-15 13:55 475
f8deb5b9 Poul Sander 2022-01-15 13:55 476
def extras(help, version, options, doc):
f8deb5b9 Poul Sander 2022-01-15 13:55 477
    if help and any((o.name in ('-h', '--help')) and o.value for o in options):
f8deb5b9 Poul Sander 2022-01-15 13:55 478
        print(doc.strip("\n"))
f8deb5b9 Poul Sander 2022-01-15 13:55 479
        sys.exit()
f8deb5b9 Poul Sander 2022-01-15 13:55 480
    if version and any(o.name == '--version' and o.value for o in options):
f8deb5b9 Poul Sander 2022-01-15 13:55 481
        print(version)
f8deb5b9 Poul Sander 2022-01-15 13:55 482
        sys.exit()
f8deb5b9 Poul Sander 2022-01-15 13:55 483
f8deb5b9 Poul Sander 2022-01-15 13:55 484
f8deb5b9 Poul Sander 2022-01-15 13:55 485
class Dict(dict):
f8deb5b9 Poul Sander 2022-01-15 13:55 486
    def __repr__(self):
f8deb5b9 Poul Sander 2022-01-15 13:55 487
        return '{%s}' % ',\n '.join('%r: %r' % i for i in sorted(self.items()))
f8deb5b9 Poul Sander 2022-01-15 13:55 488
f8deb5b9 Poul Sander 2022-01-15 13:55 489
f8deb5b9 Poul Sander 2022-01-15 13:55 490
def docopt(doc, argv=None, help=True, version=None, options_first=False):
f8deb5b9 Poul Sander 2022-01-15 13:55 491
    """Parse `argv` based on command-line interface described in `doc`.
f8deb5b9 Poul Sander 2022-01-15 13:55 492
f8deb5b9 Poul Sander 2022-01-15 13:55 493
    `docopt` creates your command-line interface based on its
f8deb5b9 Poul Sander 2022-01-15 13:55 494
    description that you pass as `doc`. Such description can contain
f8deb5b9 Poul Sander 2022-01-15 13:55 495
    --options, <positional-argument>, commands, which could be
f8deb5b9 Poul Sander 2022-01-15 13:55 496
    [optional], (required), (mutually | exclusive) or repeated...
f8deb5b9 Poul Sander 2022-01-15 13:55 497
f8deb5b9 Poul Sander 2022-01-15 13:55 498
    Parameters
f8deb5b9 Poul Sander 2022-01-15 13:55 499
    ----------
f8deb5b9 Poul Sander 2022-01-15 13:55 500
    doc : str
f8deb5b9 Poul Sander 2022-01-15 13:55 501
        Description of your command-line interface.
f8deb5b9 Poul Sander 2022-01-15 13:55 502
    argv : list of str, optional
f8deb5b9 Poul Sander 2022-01-15 13:55 503
        Argument vector to be parsed. sys.argv[1:] is used if not
f8deb5b9 Poul Sander 2022-01-15 13:55 504
        provided.
f8deb5b9 Poul Sander 2022-01-15 13:55 505
    help : bool (default: True)
f8deb5b9 Poul Sander 2022-01-15 13:55 506
        Set to False to disable automatic help on -h or --help
f8deb5b9 Poul Sander 2022-01-15 13:55 507
        options.
f8deb5b9 Poul Sander 2022-01-15 13:55 508
    version : any object
f8deb5b9 Poul Sander 2022-01-15 13:55 509
        If passed, the object will be printed if --version is in
f8deb5b9 Poul Sander 2022-01-15 13:55 510
        `argv`.
f8deb5b9 Poul Sander 2022-01-15 13:55 511
    options_first : bool (default: False)
f8deb5b9 Poul Sander 2022-01-15 13:55 512
        Set to True to require options precede positional arguments,
f8deb5b9 Poul Sander 2022-01-15 13:55 513
        i.e. to forbid options and positional arguments intermix.
f8deb5b9 Poul Sander 2022-01-15 13:55 514
f8deb5b9 Poul Sander 2022-01-15 13:55 515
    Returns
f8deb5b9 Poul Sander 2022-01-15 13:55 516
    -------
f8deb5b9 Poul Sander 2022-01-15 13:55 517
    args : dict
f8deb5b9 Poul Sander 2022-01-15 13:55 518
        A dictionary, where keys are names of command-line elements
f8deb5b9 Poul Sander 2022-01-15 13:55 519
        such as e.g. "--verbose" and "<path>", and values are the
f8deb5b9 Poul Sander 2022-01-15 13:55 520
        parsed values of those elements.
f8deb5b9 Poul Sander 2022-01-15 13:55 521
f8deb5b9 Poul Sander 2022-01-15 13:55 522
    Example
f8deb5b9 Poul Sander 2022-01-15 13:55 523
    -------
f8deb5b9 Poul Sander 2022-01-15 13:55 524
    >>> from docopt import docopt
f8deb5b9 Poul Sander 2022-01-15 13:55 525
    >>> doc = '''
f8deb5b9 Poul Sander 2022-01-15 13:55 526
    ... Usage:
f8deb5b9 Poul Sander 2022-01-15 13:55 527
    ...     my_program tcp <host> <port> [--timeout=<seconds>]
f8deb5b9 Poul Sander 2022-01-15 13:55 528
    ...     my_program serial <port> [--baud=<n>] [--timeout=<seconds>]
f8deb5b9 Poul Sander 2022-01-15 13:55 529
    ...     my_program (-h | --help | --version)
f8deb5b9 Poul Sander 2022-01-15 13:55 530
    ...
f8deb5b9 Poul Sander 2022-01-15 13:55 531
    ... Options:
f8deb5b9 Poul Sander 2022-01-15 13:55 532
    ...     -h, --help  Show this screen and exit.
f8deb5b9 Poul Sander 2022-01-15 13:55 533
    ...     --baud=<n>  Baudrate [default: 9600]
f8deb5b9 Poul Sander 2022-01-15 13:55 534
    ... '''
f8deb5b9 Poul Sander 2022-01-15 13:55 535
    >>> argv = ['tcp', '127.0.0.1', '80', '--timeout', '30']
f8deb5b9 Poul Sander 2022-01-15 13:55 536
    >>> docopt(doc, argv)
f8deb5b9 Poul Sander 2022-01-15 13:55 537
    {'--baud': '9600',
f8deb5b9 Poul Sander 2022-01-15 13:55 538
     '--help': False,
f8deb5b9 Poul Sander 2022-01-15 13:55 539
     '--timeout': '30',
f8deb5b9 Poul Sander 2022-01-15 13:55 540
     '--version': False,
f8deb5b9 Poul Sander 2022-01-15 13:55 541
     '<host>': '127.0.0.1',
f8deb5b9 Poul Sander 2022-01-15 13:55 542
     '<port>': '80',
f8deb5b9 Poul Sander 2022-01-15 13:55 543
     'serial': False,
f8deb5b9 Poul Sander 2022-01-15 13:55 544
     'tcp': True}
f8deb5b9 Poul Sander 2022-01-15 13:55 545
f8deb5b9 Poul Sander 2022-01-15 13:55 546
    See also
f8deb5b9 Poul Sander 2022-01-15 13:55 547
    --------
f8deb5b9 Poul Sander 2022-01-15 13:55 548
    * For video introduction see http://docopt.org
f8deb5b9 Poul Sander 2022-01-15 13:55 549
    * Full documentation is available in README.rst as well as online
f8deb5b9 Poul Sander 2022-01-15 13:55 550
      at https://github.com/docopt/docopt#readme
f8deb5b9 Poul Sander 2022-01-15 13:55 551
f8deb5b9 Poul Sander 2022-01-15 13:55 552
    """
f8deb5b9 Poul Sander 2022-01-15 13:55 553
    argv = sys.argv[1:] if argv is None else argv
f8deb5b9 Poul Sander 2022-01-15 13:55 554
f8deb5b9 Poul Sander 2022-01-15 13:55 555
    usage_sections = parse_section('usage:', doc)
f8deb5b9 Poul Sander 2022-01-15 13:55 556
    if len(usage_sections) == 0:
f8deb5b9 Poul Sander 2022-01-15 13:55 557
        raise DocoptLanguageError('"usage:" (case-insensitive) not found.')
f8deb5b9 Poul Sander 2022-01-15 13:55 558
    if len(usage_sections) > 1:
f8deb5b9 Poul Sander 2022-01-15 13:55 559
        raise DocoptLanguageError('More than one "usage:" (case-insensitive).')
f8deb5b9 Poul Sander 2022-01-15 13:55 560
    DocoptExit.usage = usage_sections[0]
f8deb5b9 Poul Sander 2022-01-15 13:55 561
f8deb5b9 Poul Sander 2022-01-15 13:55 562
    options = parse_defaults(doc)
f8deb5b9 Poul Sander 2022-01-15 13:55 563
    pattern = parse_pattern(formal_usage(DocoptExit.usage), options)
f8deb5b9 Poul Sander 2022-01-15 13:55 564
    # [default] syntax for argument is disabled
f8deb5b9 Poul Sander 2022-01-15 13:55 565
    #for a in pattern.flat(Argument):
f8deb5b9 Poul Sander 2022-01-15 13:55 566
    #    same_name = [d for d in arguments if d.name == a.name]
f8deb5b9 Poul Sander 2022-01-15 13:55 567
    #    if same_name:
f8deb5b9 Poul Sander 2022-01-15 13:55 568
    #        a.value = same_name[0].value
f8deb5b9 Poul Sander 2022-01-15 13:55 569
    argv = parse_argv(Tokens(argv), list(options), options_first)
f8deb5b9 Poul Sander 2022-01-15 13:55 570
    pattern_options = set(pattern.flat(Option))
f8deb5b9 Poul Sander 2022-01-15 13:55 571
    for options_shortcut in pattern.flat(OptionsShortcut):
f8deb5b9 Poul Sander 2022-01-15 13:55 572
        doc_options = parse_defaults(doc)
f8deb5b9 Poul Sander 2022-01-15 13:55 573
        options_shortcut.children = list(set(doc_options) - pattern_options)
f8deb5b9 Poul Sander 2022-01-15 13:55 574
        #if any_options:
f8deb5b9 Poul Sander 2022-01-15 13:55 575
        #    options_shortcut.children += [Option(o.short, o.long, o.argcount)
f8deb5b9 Poul Sander 2022-01-15 13:55 576
        #                    for o in argv if type(o) is Option]
f8deb5b9 Poul Sander 2022-01-15 13:55 577
    extras(help, version, argv, doc)
f8deb5b9 Poul Sander 2022-01-15 13:55 578
    matched, left, collected = pattern.fix().match(argv)
f8deb5b9 Poul Sander 2022-01-15 13:55 579
    if matched and left == []:  # better error message if left?
f8deb5b9 Poul Sander 2022-01-15 13:55 580
        return Dict((a.name, a.value) for a in (pattern.flat() + collected))
f8deb5b9 Poul Sander 2022-01-15 13:55 581
    raise DocoptExit()
1970-01-01 00:00 582