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
|