# Python simple type checking decorator

I'd like to know your opinions on this minimal type-checking decorator (with @ annotations) to make type checking of a method while debugging like :

def typecheck(*tuples, **kwtuples):
def decorator(func):
def function_wrapper(*args, **kwargs):

#print('tuples : ' , tuples)
#print('kwtuples : ' , kwtuples)
#print('args : ' , args)
#print('kwargs : ' , kwargs)

for index, tup in enumerate(tuples):
arg = args[index]
if not isinstance(arg, tup):
raise ValueError('in ' + str(func.__name__) + ' : wrong argument on position ,' + str(index) + ' :'  + str(arg) + ' must be of type :' + str(tup) + 'but is' + str(type(arg)) )

for key, tup in kwtuples.items():
arg = kwargs[key]
if not isinstance(args[index], tup):
raise ValueError('in ' + str(func.__name__) + ' : wrong argument ' + str(key) + ' :'  + str(arg) + ' must be of type :' + str(tup) + 'but is' + str(type(arg)) )

#print("arguments: ", *args, **kwargs)
func(*args, **kwargs)
return function_wrapper
return decorator

@typecheck(str,(str, float))
def foo2(x,y):
print("Hi, foo has been called with ",str(x) ,y)

foo2('2',3.4)



The benefit of it is :

• Custom and detailed description about the wrong argument
• Later, you can extend this for custom validation like some item is in some range or structural inspection of an argument.
• Easy to write, apply and delete after testing (so it won't consume cpu time)
• Have you considered somehow taking advantage of type hints, introduced in Python 3.5? (And if not, why?) – 200_success May 27 '19 at 18:39
• In addition to @200_success other than using typing and mypy/pyre/pyright/pytype for static type enforcement, you can also use something like pytypes for run-time type enforcement. – Peilonrayz May 27 '19 at 18:43
• those stuff do not custom value checking or structural checking, dont they? also i want it to be easy removed after testing – user8426627 May 27 '19 at 19:08

To amplify @200_success’s comment:

Python 3.5 introduces type hints. Eg)

>>> def foo2(x:str, y:float) -> None:
...    print(f"Hi, foo has been called with {x} {y}")
...
>>> foo2("2", 3.4)
Hi, foo has been called with 2 3.4
>>>


These type hints (the x:str, and y:float) are not used by the standard Python interpreter, but they are recorded during the parsing of the code, so they are available to the program.

>>> foo2.__annotations__
{'return': None, 'x': <class 'str'>, 'y': <class 'float'>}
>>>


Directly accessing the foo2.__annotations__ is not that interesting, but accessing func.__annotations__ in the @typecheck decorator means you don’t have to provide the argument types to the decorator; the decorator can inspect the argument types directly. Thus you could decorate the function with simply @typecheck, instead of @typecheck(str, float).

For a variant str or float type argument, you could use the typing module to define your own STR_OR_FLOAT = TypeVar("STR_OR_FLOAT", str, float) type (or a better name if you have one), which you could decorate the argument with.

As a bonus, using type hints to provide argument type checking information — even for your do-it-yourself via a function decorator type check system — also gives you the following benefits:

• IDE type hints, as you are writing code
• Can be understood by standard documentation generators
• Only consumes CPU time during parsing of the code; does not affect execution speed