Strong type hints with Traits

pip install typen==0.0.3


Strong Type Hints with Traits

Build Status

This package supplies simple decorators to enforce Python type hints on function parameters and return types.

from typen import enforce_type_hints

def halve_integer(a: int) -> float:
    return a / 2

halve_integer(5)  # 2.5

halve_integer(5.0)  # ParameterTypeError
def give_int(a) -> int:
    return a

give_int(1)  # 1

give_int("a")  # ReturnTypeError

Trait Type Hints

Trait types can also be used to define complex patterns in type hints

from traits.api import Array, Either, Enum, Instance, Int, Str, Tuple

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

Strict Enforcement

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

def add_numbers(a, b: float) -> float:
    return a + b

add_numbers(1, 2)  # UnspecifiedParameterTypeError
def add_numbers(a: float, b: float):
    return a + b

add_numbers(1, 2)  # UnspecifiedReturnTypeError

Packed args and kwargs

Type hints on packed parameters apply to all values passed through that packing.

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

Method Decoration

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:
    def __init__(self, a: int, b: int):

    def a_class_method(cls, a: int, c: int) -> int:

    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:

def add_numbers(a: float, b: float) -> float:
    return a + b

type(add_numbers(1, 2))  # int

Recovering from ReturnTypeError

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

def give_int(a) -> int:
    return a

except ReturnTypeError as err:
    print(err.return_value)  # a