Initial commit (Clean history)
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
from typing import Any, Iterable, Optional, Tuple
|
||||
|
||||
# TODO: dynamic type for kind
|
||||
# T = TypeVar('T')
|
||||
|
||||
|
||||
class Argument:
|
||||
"""
|
||||
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: Optional[str] = None,
|
||||
names: Iterable[str] = (),
|
||||
kind: Any = str,
|
||||
default: Optional[Any] = None,
|
||||
help: Optional[str] = None,
|
||||
positional: bool = False,
|
||||
optional: bool = False,
|
||||
incrementable: bool = False,
|
||||
attr_name: Optional[str] = None,
|
||||
) -> None:
|
||||
if name and names:
|
||||
raise TypeError(
|
||||
"Cannot give both 'name' and 'names' arguments! Pick one."
|
||||
)
|
||||
if not (name or names):
|
||||
raise TypeError("An Argument must have at least one name.")
|
||||
if names:
|
||||
self.names = tuple(names)
|
||||
elif name and not names:
|
||||
self.names = (name,)
|
||||
self.kind = kind
|
||||
initial_value: Optional[Any] = 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) -> str:
|
||||
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) -> Optional[str]:
|
||||
"""
|
||||
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) -> Tuple[str, ...]:
|
||||
return self.names[1:]
|
||||
|
||||
@property
|
||||
def takes_value(self) -> bool:
|
||||
if self.kind is bool:
|
||||
return False
|
||||
if self.incrementable:
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def value(self) -> Any:
|
||||
# TODO: should probably be optional instead
|
||||
return self._value if self._value is not None else self.default
|
||||
|
||||
@value.setter
|
||||
def value(self, arg: str) -> None:
|
||||
self.set_value(arg, cast=True)
|
||||
|
||||
def set_value(self, value: Any, cast: bool = True) -> None:
|
||||
"""
|
||||
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) -> bool:
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user