I'm trying to write my one python decorator for type checking. It works well, but struggles with nested type hints. For example take
@type_check
def fun(x: int) -> List[List[List[str]]]:
return [[[str(x)]]]
fun(x=42)
I have a decorator, which eveluates the function and checks, if the actual return value has the expected type:
import inspect
# inside the decorator
actual_result = func(*args, **kwargs) # [[['42']]]
expected_result_type = spec.annotations['return'] # typing.List[typing.List[typing.List[str]]]
And now is challenge to check is [[['42']]]
has type typing.List[typing.List[typing.List[str]]]
. How can I do that?
All I found out, is that [[['42']]]
has simply type <class 'list'>
which ignores the information about nesting completly. And I can check this with
if hasattr(expected_result_type, '__origin__'):
expected_result_type = expected_result_type.__origin__
if expected_result_type is not None:
assert isinstance(result, expected_result_type)
else: # None is kind of a special case
assert result is expected_result_type
This works on the "outer layer", but ignores nested type hints. Is there any way to check all layers?
The simple answer would be:
Simply recurse into all arguments like:
def type_check(value, annotation):
if isinstance(annotation, type):
return isinstance(value, annotation)
elif annotation == typing.T or annotation == typing.Any:
return True
elif isinstance(annotation, typing._GenericAlias):
if annotation.__origin__ == list:
if not isinstance(value, list):
return False
inner_annotation = annotation.__args__[0]
return all(type_check(val, inner_annotation) for val in value)
Example
>>> type_check(5, int)
True
>>> type_check([1, 2, 3], typing.List[int])
True
>>> type_check([1, 2, 3], typing.List[float])
False
Which you can simply use in you decorator. It might be improved to handle Dict, Tuple and other common types.
However it's just a toy example, if you want to do robust dynamic type checking, you'll have to handle Generator
, Callable
, Iterator
etc... which is not only quite complex but also in some case completely impossible (Iterators for example can't be checked unless iterated over).
Have a look at the enforce project which is exactly that: a python dynamic type checking using decorators. The typing
module source code is also really interesting to read, but really pushes the limits of what dynamic introspection can do.
Hope it helps;)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.