Standard library types#
The following Python standard library types can be used as type hints for Feud commands.
Tip
Types listed on this page from the collections
, datetime
decimal
,
enum
, pathlib
and uuid
standard library modules are easily accessible
from the feud.typing
module.
It is recommended to import the feud.typing
module with an alias such as t
for convenient short-hand use, e.g.
from feud import typing as t
t.Path # pathlib.Path
t.datetime # datetime.datetime
t.IntEnum # enum.IntEnum
t.NamedTuple # typing.NamedTuple
t.Union # typing.Union
String type#
The string type permits the user to enter free text as an input value.
str
should be used to specify free text,
but it is also the default when no type hint is provided.
See also
pydantic.types.constr
can be used to impose restrictions such as minimum/maximum string length.
Example
- Arguments:
ARG1
: Any text (no type hint).ARG2
: Any text.
- Options:
--opt
: Any text.
# str.py
import feud
def command(arg1, arg2: str, *, opt: str = "value"):
print(f"{arg1=!r} ({type(arg1)})")
print(f"{arg2=!r} ({type(arg2)})")
print(f"{opt=!r} ({type(opt)})")
if __name__ == "__main__":
feud.run(command)
$ python str.py --help
$ python str.py "Hello World!" abc --opt test
arg1='Hello World' (<class 'str'>)
arg2='abc' (<class 'str'>)
opt='test' (<class 'str'>)
As free text is not validated, any input is accepted.
Literal string type (choices)#
Literal strings can be used to limit the user to a number of string choices.
typing.Literal
should be used to specify literal string inputs.
See also
Enumeration types can also be used to represent choices.
Example
- Arguments:
ARG
: Either the stringa
orb
.
- Options:
--opt
: Either the stringc
,d
ore
. Defaults toe
.
# literal.py
import feud
from feud import typing as t
def command(arg: t.Literal["a", "b"], *, opt: t.Literal["c", "d", "e"] = "e"):
print(f"{arg=!r} ({type(arg)})")
print(f"{opt=!r} ({type(opt)})")
if __name__ == "__main__":
feud.run(command)
$ python literal.py --help
$ python literal.py b --opt d
arg='b' (<class 'str'>)
opt='d' (<class 'str'>)
$ python literal.py c
Boolean type#
The boolean type can be used to indicate a true/false input.
bool
should be used to specify boolean inputs.
Common truth values that are accepted include any of the following (case insensitive):
True:
true
,t
,yes
,y
,1
False:
false
,f
,no
,n
,0
Tip
When used as an option, the presence of a boolean flag is enough to set
the value to True
— that is, to enable the flag the user should simply
specify --opt
instead of --opt true
(which will not work), for
example.
By default, a negated version of the flag (e.g. --no-opt
) is also generated
to set the value to False
. The generation of this negated flag can be
disabled by changing the Feud configuration parameters.
Example
- Arguments:
ARG
: Boolean value.
- Options:
--opt
: Boolean value.
# bool.py
import feud
def command(arg: bool, *, opt: bool = True):
print(f"{arg=!r} ({type(arg)})")
print(f"{opt=!r} ({type(opt)})")
if __name__ == "__main__":
feud.run(command)
$ python bool.py --help
$ python bool.py false --opt
arg=False (<class 'bool'>)
opt=True (<class 'bool'>)
$ python bool.py true --no-opt
arg=True (<class 'bool'>)
opt=False (<class 'bool'>)
$ python bool.py maybe
Number types#
Number types can be used to indicate integers, floats or decimal numbers.
int
should be used to specify integer inputs.float
should be used to specify fixed precision-floating point inputs.decimal.Decimal
should be used to specify arbitrary-precision floating point inputs.
See also
pydantic.types.conint
can be used to restrict integers.pydantic.types.confloat
can be used to restrict floats.pydantic.types.condecimal
can be used to restrict decimals.
Example
- Arguments:
ARG
: An integer value.
- Options:
--opt1
: An integer value.--opt2
: A fixed-precision floating point number.--opt3
: An arbirary precision floating point number.
# number.py
import feud
from feud import typing as t
def command(arg: int, *, opt1: int, opt2: float, opt3: t.Decimal):
print(f"{arg=!r} ({type(arg)})")
print(f"{opt1=!r} ({type(opt1)})")
print(f"{opt2=!r} ({type(opt2)})")
print(f"{opt3=!r} ({type(opt3)})")
if __name__ == "__main__":
feud.run(command)
$ python number.py --help
$ python number.py 0 --opt1 1 --opt2 -2.2 --opt3 3.33
arg=0 (<class 'int'>)
opt1=1 (<class 'int'>)
opt2=-2.2 (<class 'float'>)
opt3=Decimal('3.33') (<class 'decimal.Decimal'>)
$ python number.py abc --opt1 1.1 --opt2 true --opt3 invalid
Datetime types#
Datetime types can be used to indicate date or time related inputs.
datetime.datetime
can be used to indicate inputs with a date and time component.datetime.date
can be used to indicate date inputs.datetime.time
can be used to indicate time inputs.datetime.timedelta
can be used to indicate time delta inputs.
See also
pydantic.types.condate
can be used to impose restrictions such as minimum/maximum dates.pydantic.types.PastDate
/pydantic.types.PastDatetime
can be used to restrict date/datetime inputs to the past.pydantic.types.FutureDate
/pydantic.types.FutureDatetime
can be used to restrict date/datetime inputs to the future.
Example
- Arguments:
ARG
: A datetime value.
- Options:
--opt1
: A date value.--opt2
: A time value.--opt3
: A time delta value.
# date_time.py
import feud
from feud import typing as t
def command(arg: t.datetime, *, opt1: t.date, opt2: t.time, opt3: t.timedelta):
print(f"{arg=!r} ({type(arg)})")
print(f"{opt1=!r} ({type(opt1)})")
print(f"{opt2=!r} ({type(opt2)})")
print(f"{opt3=!r} ({type(opt3)})")
if __name__ == "__main__":
feud.run(command)
$ python date_time.py --help
$ python date_time.py "2012-12-21 00:01:00" \
--opt1 2012-12-21 \
--opt2 00:01:00 \
--opt3 "704 days, 19:21:44.938965"
arg=datetime.datetime(2012, 12, 21, 0, 1) (<class 'datetime.datetime'>)
opt1=datetime.date(2012, 12, 21) (<class 'datetime.date'>)
opt2=datetime.time(0, 1) (<class 'datetime.time'>)
opt3=datetime.timedelta(days=704, seconds=69704, microseconds=938965) (<class 'datetime.timedelta'>)
$ python date_time.py abc --opt1 a --opt2 b --opt3 c
Path type#
The path type can be used to indicate a file or directory path input.
pathlib.Path
should be used to specify path inputs.
Important
pathlib.Path
does not validate whether or not the path already exists.
See also
pydantic.types.NewPath
can be used to indicate a path that must not already exist.pydantic.types.FilePath
can be used to indicate a path to a file that must already exist.pydantic.types.DirectoryPath
can be used to indicate a path to a directory that must already exist.
Example
- Arguments:
ARG
: Path to a file or directory (may not exist).
- Options:
--opt
: Path to a file or directory (may not exist). Defaults to/usr/local/bin
.
# path.py
import feud
from feud import typing as t
def command(arg: t.Path, *, opt: t.Path = t.Path("/usr/local/bin")):
print(f"{arg=!r} ({type(arg)})")
print(f"{opt=!r} ({type(opt)})")
if __name__ == "__main__":
feud.run(command)
$ python literal.py --help
$ python path.py /opt/homebrew --opt ~/dev/feud/README.md
arg=PosixPath('/opt/homebrew') (<class 'pathlib.PosixPath'>)
opt=PosixPath('/Users/eonu/dev/feud/README.md') (<class 'pathlib.PosixPath'>)
As pathlib.Path
allows any string, any input is accepted.
Sequence types#
Variable length#
Variable length sequence types can be used to accept multiple input values.
list
/typing.List
should be used to specify a list of inputs.set
/typing.Set
should be used to specify a set of inputs.frozenset
/typing.FrozenSet
should be used to specify a frozen set of inputs.collections.deque
/typing.Deque
should be used to specify a deque of inputs.
Tip
Additional type restrictions can be placed on the items within the sequence, e.g.:
See also
pydantic.types.conlist
can be used to impose restrictions such as minimum/maximum list length.pydantic.types.conset
can be used to impose restrictions such as minimum/maximum set length.pydantic.types.confrozenset
can be used to impose restrictions such as minimum/maximum frozen set length.
Example
- Arguments:
FLOATS
: Any number of float values (at least one).
- Options:
--ints
: Any number of integer values (at least one).
# variable.py
import feud
def command(floats: list[float], *, ints: set[int]):
print(f"{floats=!r} ({type(floats)})")
print(f"{ints=!r} ({type(ints)})")
if __name__ == "__main__":
feud.run(command)
$ python variable.py --help
$ python variable.py 1.1 2.2 3.3 --ints 0 --ints 1 --ints 0 --ints 2
floats=[1.1, 2.2, 3.3] (<class 'list'>)
ints={0, 1, 2} (<class 'set'>)
$ python variable.py string
Fixed length#
Fixed length sequence types can be used to accept a fixed number of input values.
tuple
/typing.Tuple
should be used to specify a tuple of inputs.typing.NamedTuple
should be used to specify a named tuple of inputs.
Tip
When used with ...
as the second type argument, tuple
/typing.Tuple
may also be used to accept a variable length input and convert the items into a tuple,
e.g. tuple
[int
, ...
] accepts a variable number of integers.
Example
- Arguments:
NUMBERS
: Pair of numbers consisting of an integer and float.
- Options:
--location
: Pair of numbers consisting of a latitude & longitude (both floats).
# fixed.py
import feud
from feud import typing as t
class Coordinate(t.NamedTuple):
latitude: t.Latitude
longitude: t.Longitude
def command(numbers: tuple[int, float], *, location: Coordinate):
print(f"{numbers=!r} ({type(numbers)})")
print(f"{location=!r} ({type(location)})")
if __name__ == "__main__":
feud.run(command)
$ python fixed.py --help
$ python fixed.py 1 1.1 --location 65.2 149.0
numbers=(1, 1.1) (<class 'tuple'>)
location=Coordinate(latitude=65.2, longitude=149.0) (<class '__main__.Coordinate'>)
$ python fixed.py 1 1.1 --location 100 200
Enumeration types (choices)#
Enumerate types can be used to limit the user to a number of choices.
enum.Enum
/enum.StrEnum
should be used to limit the user to string choices.enum.IntEnum
should be used to limit the user to integer choices.
Important
enum.Enum
values may only be strings.
Example
- Arguments:
ARG
: Either the number1
or2
.
- Options:
--opt
: Either the stringa
orb
. Defaults toa
.
import feud
from feud import typing as t
class Mode(t.Enum):
A = "a"
B = "b"
class Version(t.IntEnum):
ONE = 1
TWO = 2
def command(arg: Version, *, opt: Mode = Mode.A):
print(f"{arg=!r} ({type(arg)})")
print(f"{opt=!r} ({type(opt)})")
if __name__ == "__main__":
feud.run(command)
$ python literal.py --help
$ python enumeration.py 1 --opt b
arg=<Version.ONE: 1> (<enum 'Version'>)
opt=<Mode.B: 'b'> (<enum 'Mode'>)
$ python enumeration.py 3 --opt c
UUID type#
The UUID type can be used to indicate a UUID input.
uuid.UUID
should be used to specify UUID inputs.
Example
- Arguments:
ARG
: A UUID value.
- Options:
--opt
: A UUID value. Defaults to a random UUID if none is provided.
# uuids.py
from uuid import uuid4
import feud
from feud import typing as t
def command(arg: t.UUID, *, opt: t.UUID = uuid4()):
print(f"{arg=!r} ({type(arg)})")
print(f"{opt=!r} ({type(opt)})")
if __name__ == "__main__":
feud.run(command)
$ python uuids.py --help
$ python uuids.py 2b293576-fe8c-4482-898c-547adf5a4a25
arg=UUID('2b293576-fe8c-4482-898c-547adf5a4a25') (<class 'uuid.UUID'>)
opt=UUID('8186f015-8ca6-4793-9513-121288f972fd') (<class 'uuid.UUID'>)
$ python uuids.py 123
Unions#
Unions can be used to allow for an input to match two or more supported Feud types.
typing.Union
or the |
operator can be used to specify union types.
Example
- Arguments:
ARG
: A UUID value.
- Options:
--opt
: A UUID value. Defaults to a random UUID if none is provided.
# union.py
import feud
from feud import typing as t
def command(arg: t.Union[int, float], *, opt: int | float):
print(f"{arg=!r} ({type(arg)})")
print(f"{opt=!r} ({type(opt)})")
if __name__ == "__main__":
feud.run(command)
$ python union.py --help
$ python union.py 1 --opt 1.1
arg=1 (<class 'int'>)
opt=1.1 (<class 'float'>)
$ python union.py a --opt b