#!/usr/bin/env python3 # Play around with function annotations to implement a crude type checker. import inspect def typechecked(func): wrapped_func = func def typecheck(*args, **kvargs): parameters = inspect.signature(wrapped_func).parameters wants = [(par, parameters[par].annotation) for par in parameters] for want, arg in zip(wants, args): name, class_ = want if not isinstance(arg, class_): raise TypeError("arg {} is not " "an instance of {}".format(name, class_)) return_ = wrapped_func(*args, **kvargs) class_ = wrapped_func.__annotations__["return"] if not isinstance(return_, class_): raise TypeError("return value is not " "an instance of {}".format(class_)) return return_ typecheck.__doc__ = wrapped_func.__doc__ return typecheck @typechecked def greet(name: str, age: int, fnord: str=None) -> str: """Greet someone. >>> greet("Mike", 12) 'Hello Mike, you are 12 years old' >>> greet("Florian", "eins") Traceback (most recent call last): File "func_annotations.py", line 14, in typecheck "is not an instance of {}".format(name, class_)) TypeError: arg age is not an instance of """ return 'Hello {0}, you are {1} years old'.format(name, age) @typechecked def test(foo: int) -> str: """ Just a test. >>> test(1) Traceback (most recent call last): File "func_annotations.py", line 21, in typecheck "is not an instance of {}".format(class_)) TypeError: return value is not an instance of """ return 1 import doctest doctest.testmod()