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
Free text example help screen
Valid input
$ python str.py "Hello World!" abc --opt test
arg1='Hello World' (<class 'str'>)
arg2='abc' (<class 'str'>)
opt='test' (<class 'str'>)
Invalid input

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 string a or b.

  • Options:
    • --opt: Either the string c, d or e. Defaults to e.

# 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
Literal example help screen
Valid input
$ python literal.py b --opt d
arg='b' (<class 'str'>)
opt='d' (<class 'str'>)
Invalid input
$ python literal.py c
Literal example error

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
Boolean example help screen
Valid input (1)
$ python bool.py false --opt
arg=False (<class 'bool'>)
opt=True (<class 'bool'>)
Valid input (2)
$ python bool.py true --no-opt
arg=True (<class 'bool'>)
opt=False (<class 'bool'>)
Invalid input
$ python bool.py maybe
Boolean example error

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

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
Number example help screen
Valid input
$ 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'>)
Invalid input
$ python number.py abc --opt1 1.1 --opt2 true --opt3 invalid
Number example error

Datetime types#

Datetime types can be used to indicate date or time related inputs.

See also

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
Datetime example help screen
Valid input
$ 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'>)
Invalid input
$ python date_time.py abc --opt1 a --opt2 b --opt3 c
Datetime example error

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

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
Path example help screen
Valid input
$ 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'>)
Invalid input

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.

Tip

Additional type restrictions can be placed on the items within the sequence, e.g.:

  • list[float] indicates a list of float inputs.

  • set[int] indicates a set of integer inputs.

See also

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
Variable length sequence example help screen
Valid input
$ 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'>)
Invalid input
$ python variable.py string
Variable length sequence example error

Fixed length#

Fixed length sequence types can be used to accept a fixed number of input values.

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
Fixed length sequence example help screen
Valid input
$ 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'>)
Invalid input
$ python fixed.py 1 1.1 --location 100 200
Fixed length sequence example error

Enumeration types (choices)#

Enumerate types can be used to limit the user to a number of choices.

Important

enum.Enum values may only be strings.

Example
  • Arguments:
    • ARG: Either the number 1 or 2.

  • Options:
    • --opt: Either the string a or b. Defaults to a.

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
Enumeration example help screen
Valid input
$ python enumeration.py 1 --opt b
arg=<Version.ONE: 1> (<enum 'Version'>)
opt=<Mode.B: 'b'> (<enum 'Mode'>)
Invalid input
$ python enumeration.py 3 --opt c
Enumeration example error

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
UUID example help screen
Valid input
$ 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'>)
Invalid input
$ python uuids.py 123
UUID example error

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
Union example help screen
Valid input
$ python union.py 1 --opt 1.1
arg=1 (<class 'int'>)
opt=1.1 (<class 'float'>)
Invalid input
$ python union.py a --opt b
Union example error