forked from damiafuentes/DJITelloPy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathenforce_types.py
65 lines (52 loc) · 2.2 KB
/
enforce_types.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
"""
This file is based on a StackOverflow post by @301_Moved_Permanently.
See https://stackoverflow.com/a/50622643
The code was adapted to be able to wrap all methods of a class by simply
adding the decorator to the class itself.
"""
import inspect
import typing
from contextlib import suppress
from functools import wraps
def _is_unparameterized_special_typing(type_hint):
# Check for typing.Any, typing.Union, typing.ClassVar (without parameters)
if hasattr(typing, "_SpecialForm"):
return isinstance(type_hint, typing._SpecialForm)
elif hasattr(type_hint, "__origin__"):
return type_hint.__origin__ is None
else:
return False
def enforce_types(target):
"""Class decorator adding type checks to all member functions
"""
def check_types(spec, *args, **kwargs):
parameters = dict(zip(spec.args, args))
parameters.update(kwargs)
for name, value in parameters.items():
with suppress(KeyError): # Assume un-annotated parameters can be any type
type_hint = spec.annotations[name]
if _is_unparameterized_special_typing(type_hint):
continue
if hasattr(type_hint, "__origin__") and type_hint.__origin__ is not None:
actual_type = type_hint.__origin__
elif hasattr(type_hint, "__args__") and type_hint.__args__ is not None:
actual_type = type_hint.__args__
else:
actual_type = type_hint
if not isinstance(value, actual_type):
raise TypeError("Unexpected type for '{}' (expected {} but found {})"
.format(name, type_hint, type(value)))
def decorate(func):
spec = inspect.getfullargspec(func)
@wraps(func)
def wrapper(*args, **kwargs):
check_types(spec, *args, **kwargs)
return func(*args, **kwargs)
return wrapper
if inspect.isclass(target):
members = inspect.getmembers(target, predicate=inspect.isfunction)
for name, func in members:
setattr(target, name, decorate(func))
return target
else:
return decorate(target)