This package supplies simple decorators to enforce Python type hints on function parameters and return types.
from typen import enforce_type_hints
@enforce_type_hints
def halve_integer(a: int) -> float:
return a / 2
halve_integer(5) # 2.5
halve_integer(5.0) # ParameterTypeError
@enforce_type_hints
def give_int(a) -> int:
return a
give_int(1) # 1
give_int("a") # ReturnTypeError
Trait types can also be used to define complex patterns in type hints
from traits.api import Array, Either, Enum, Instance, Int, Str, Tuple
@enforce_type_hints
def complicated_function(
a: Either(Str, Int), # Either a string or an int
b: Enum(2, 5, "foo"), # One of a specific set of values
c: Instance(MyClass), # Class instances
d: Array(size=(None, 2)), # Numpy array validation
) -> Tuple(Str, Either(Str, Int)): # Complicated return specification
...
Type hints can also be required with the @strict_type_hints
decorator. Both of the following examples will raise an exception when the function is first called. Without strict enforcement, parameters and return values without type hints can have any value.
from typen import strict_type_hints
@strict_type_hints
def add_numbers(a, b: float) -> float:
return a + b
add_numbers(1, 2) # UnspecifiedParameterTypeError
@strict_type_hints
def add_numbers(a: float, b: float):
return a + b
add_numbers(1, 2) # UnspecifiedReturnTypeError
Type hints on packed parameters apply to all values passed through that packing.
@enforce_type_hints
def foos_vs_bars(*foos: int, **bars: str) -> bool:
return sum(foos) >= len(bars)
foos_vs_bars(1, 2, 3, a="a", b="b", c="c") # True
foos_vs_bars(2, 3, 5, d=4) # ParameterTypeError
foos_vs_bars(2, "three", 5, e="e") # ParameterTypeError
Methods can be decorated as well. self
-references are exempt from strict type hint requirements, as is the return type of __init__
. @enforce_type_hints
and @strict_type_hints
should decorate the product of the @classmethod
or @staticmethod
decorators.
class ExClass:
@strict_type_hints
def __init__(self, a: int, b: int):
...
@strict_type_hints
@classmethod
def a_class_method(cls, a: int, c: int) -> int:
...
@strict_type_hints
@staticmethod
def a_static_method(a: int, c: int) -> int:
...
Values are enforced to types based on Trait type coercion. Casting behaviour is not added to the function:
@enforce_type_hints
def add_numbers(a: float, b: float) -> float:
return a + b
type(add_numbers(1, 2)) # int
Because the function has to be executed to enforce the return value, the invalid value is stored on the exception. This makes it possible to recover from a ReturnTypeError
programatically.
from typen.exceptions import ReturnTypeError
@enforce_type_hints
def give_int(a) -> int:
return a
try:
give_int("a")
except ReturnTypeError as err:
print(err.return_value) # a