I wrote some type checking decorators for Python. One checks the type that a function returns, the other checks the type that a function accepts.
import functools
def returns(t):
""" Calls a function, and if it returns something of the wrong type,
throws a TypeError
"""
def outer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
output = func(*args, **kwargs)
if not isinstance(output, t):
message = "function {} returned an invalid type: {} is a {}"
raise TypeError(message.format(func.__name__,
output,
type(output)))
else:
return output
return wrapper
return outer
class accepts:
def __init__(self, *artypes, **kwtypes):
""" Pass it a list consisting of types or tuples of types.
in other words, pass it things that will be valid second
arguments to the isinstance(arg, type) method.
"""
self.artypes = artypes
self.kwtypes = kwtypes
def checkTypes(self, args, kwargs):
""" Checks each arg and kwarg for type validity, and if
they fail, then it throws a TypeError. Not very descriptive,
but it's just an example.
"""
type_zip = zip(args, self.artypes)
for arg, type_ in type_zip:
if not isinstance(arg, type_):
raise ValueError()
for key, value in kwargs.items():
if not isinstance(value, self.kwtypes[key]):
raise ValueError()
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
self.checkTypes(args, kwargs)
return func(*args, **kwargs)
return wrapper
@accepts(int)
def hitimes(times=1):
return "hello " * times
@accepts(int, word=str)
def byetimes(times, word="bye"):
return " ".join([word] * times)
print(hitimes(2)) # works
print(byetimes(5)) # works
print(byetimes(3, word="adios")) # works as expected
print(byetimes(3, word=[])) # fails as expected
For returns
, I used nested functions, and for accepts I used the class style of decorator. But both of them get pretty unwieldy with respect to indentation levels. What can I do to make these cleaner?
Note that the wrappers are themselves pretty pointless.
accepts
shouldn’t be a class. \$\endgroup\$