Last active
October 8, 2018 18:30
-
-
Save pedro823/0674cd9c6223c5ae2c2b3138b8da10a8 to your computer and use it in GitHub Desktop.
Type Checker decorator in python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from functools import wraps | |
# Definitions | |
def __precheck_types(func_annotations, *args, **kwargs): | |
annotations = dict((argument, value_type) | |
for argument, value_type in func_annotations.items() | |
if argument != 'return') | |
for arg, value in kwargs.items(): | |
if arg not in annotations: | |
continue | |
if not isinstance(value, annotations[arg]): | |
raise TypeError(f'value \'{value}\' is not of ' | |
+ f'type {annotations[arg]}') | |
del annotations[arg] | |
for expected_type, value in zip(annotations.values(), args): | |
if not isinstance(value, expected_type): | |
raise TypeError(f'value \'{value}\' is not of ' | |
+ f'type {expected_type}') | |
def type_checked(bound: bool = False): | |
def decorator(func): | |
has_return_type = 'return' in func.__annotations__ | |
return_type = func.__annotations__.get('return', None.__class__) | |
if bound: | |
@wraps(func) | |
def func_wrapper(self, *args, **kwargs): | |
__precheck_types(func.__annotations__, *args, **kwargs) | |
result = func(self, *args, **kwargs) | |
if has_return_type: | |
if not isinstance(result, return_type): | |
raise TypeError('return type is not of ' | |
+ f'type {return_type}') | |
return result | |
else: | |
@wraps(func) | |
def func_wrapper(*args, **kwargs): | |
__precheck_types(func.__annotations__, *args, **kwargs) | |
result = func(*args, **kwargs) | |
if has_return_type: | |
if not isinstance(result, return_type): | |
raise TypeError('return type is not of ' | |
+ f'type {return_type}') | |
return result | |
return func_wrapper | |
return decorator | |
# Usage | |
if __name__ == '__main__': | |
@type_checked() | |
def f(a: str, b: int): | |
return a * b | |
class A: | |
@type_checked(bound=True) | |
def g(self, a: str): | |
return 4 * a | |
@type_checked(bound=False) | |
def h(a: str, *args) -> tuple: | |
s = a | |
for arg in args: | |
s += arg | |
return s, 2 | |
print(h('a', 'b', 'c', 'd')) | |
print(f('a', 2)) | |
print(f(b=2, a='f')) | |
print(f('c', b=2)) | |
try: | |
print(f(2, 3)) | |
except Exception as e: | |
print(str(e)) | |
x = A() | |
print(x.g('23')) | |
try: | |
print(x.g(23)) | |
except Exception as e: | |
print(str(e)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment