File: argument.py

package info (click to toggle)
python-invoke 1.4.1%2Bds-0.1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 1,704 kB
  • sloc: python: 11,377; makefile: 18; sh: 12
file content (166 lines) | stat: -rw-r--r-- 5,575 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
class Argument(object):
    """
    A command-line argument/flag.

    :param name:
        Syntactic sugar for ``names=[<name>]``. Giving both ``name`` and
        ``names`` is invalid.
    :param names:
        List of valid identifiers for this argument. For example, a "help"
        argument may be defined with a name list of ``['-h', '--help']``.
    :param kind:
        Type factory & parser hint. E.g. ``int`` will turn the default text
        value parsed, into a Python integer; and ``bool`` will tell the
        parser not to expect an actual value but to treat the argument as a
        toggle/flag.
    :param default:
        Default value made available to the parser if no value is given on the
        command line.
    :param help:
        Help text, intended for use with ``--help``.
    :param positional:
        Whether or not this argument's value may be given positionally. When
        ``False`` (default) arguments must be explicitly named.
    :param optional:
        Whether or not this (non-``bool``) argument requires a value.
    :param incrementable:
        Whether or not this (``int``) argument is to be incremented instead of
        overwritten/assigned to.
    :param attr_name:
        A Python identifier/attribute friendly name, typically filled in with
        the underscored version when ``name``/``names`` contain dashes.

    .. versionadded:: 1.0
    """

    def __init__(
        self,
        name=None,
        names=(),
        kind=str,
        default=None,
        help=None,
        positional=False,
        optional=False,
        incrementable=False,
        attr_name=None,
    ):
        if name and names:
            msg = "Cannot give both 'name' and 'names' arguments! Pick one."
            raise TypeError(msg)
        if not (name or names):
            raise TypeError("An Argument must have at least one name.")
        self.names = tuple(names if names else (name,))
        self.kind = kind
        initial_value = None
        # Special case: list-type args start out as empty list, not None.
        if kind is list:
            initial_value = []
        # Another: incrementable args start out as their default value.
        if incrementable:
            initial_value = default
        self.raw_value = self._value = initial_value
        self.default = default
        self.help = help
        self.positional = positional
        self.optional = optional
        self.incrementable = incrementable
        self.attr_name = attr_name

    def __repr__(self):
        nicks = ""
        if self.nicknames:
            nicks = " ({})".format(", ".join(self.nicknames))
        flags = ""
        if self.positional or self.optional:
            flags = " "
        if self.positional:
            flags += "*"
        if self.optional:
            flags += "?"
        # TODO: store this default value somewhere other than signature of
        # Argument.__init__?
        kind = ""
        if self.kind != str:
            kind = " [{}]".format(self.kind.__name__)
        return "<{}: {}{}{}{}>".format(
            self.__class__.__name__, self.name, nicks, kind, flags
        )

    @property
    def name(self):
        """
        The canonical attribute-friendly name for this argument.

        Will be ``attr_name`` (if given to constructor) or the first name in
        ``names`` otherwise.

        .. versionadded:: 1.0
        """
        return self.attr_name or self.names[0]

    @property
    def nicknames(self):
        return self.names[1:]

    @property
    def takes_value(self):
        if self.kind is bool:
            return False
        if self.incrementable:
            return False
        return True

    @property
    def value(self):
        return self._value if self._value is not None else self.default

    @value.setter
    def value(self, arg):
        self.set_value(arg, cast=True)

    def set_value(self, value, cast=True):
        """
        Actual explicit value-setting API call.

        Sets ``self.raw_value`` to ``value`` directly.

        Sets ``self.value`` to ``self.kind(value)``, unless:

        - ``cast=False``, in which case the raw value is also used.
        - ``self.kind==list``, in which case the value is appended to
          ``self.value`` instead of cast & overwritten.
        - ``self.incrementable==True``, in which case the value is ignored and
          the current (assumed int) value is simply incremented.

        .. versionadded:: 1.0
        """
        self.raw_value = value
        # Default to do-nothing/identity function
        func = lambda x: x
        # If cast, set to self.kind, which should be str/int/etc
        if cast:
            func = self.kind
        # If self.kind is a list, append instead of using cast func.
        if self.kind is list:
            func = lambda x: self._value + [x]
        # If incrementable, just increment.
        if self.incrementable:
            # TODO: explode nicely if self._value was not an int to start with
            func = lambda x: self._value + 1
        self._value = func(value)

    @property
    def got_value(self):
        """
        Returns whether the argument was ever given a (non-default) value.

        For most argument kinds, this simply checks whether the internally
        stored value is non-``None``; for others, such as ``list`` kinds,
        different checks may be used.

        .. versionadded:: 1.3
        """
        if self.kind is list:
            return bool(self._value)
        return self._value is not None