Created
February 14, 2015 21:37
-
-
Save minus7/e0786917c7349e9a74bf to your computer and use it in GitHub Desktop.
Python Type-Checking Decorator
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
# Check if a function's arguments are instances of a (set of) type(s): | |
# >>> @type_checked | |
# >>> def test(asdf: str): | |
# >>> print(type(asdf)) | |
# >>> test(3) | |
# ValueError: Argument asdf should be of type str but is int | |
# Using @type_checked_cast instead of @type_checked tries | |
# casting the argument to the desired type: | |
# >>> @type_checked_cast | |
# >>> def test(asdf: str): | |
# >>> print(type(asdf)) | |
# >>> test(3) | |
# <class 'str'> | |
from collections.abc import Iterable | |
from functools import wraps | |
from inspect import signature | |
def type_checked(func): | |
@wraps(func) | |
def wrapper(*args, **kwargs): | |
sig = signature(func) | |
bound = sig.bind(*args, **kwargs) | |
for k,v in bound.arguments.items(): | |
ann = sig.parameters[k].annotation | |
if isinstance(ann, type): | |
ann = (ann,) | |
if isinstance(ann, Iterable): | |
for anni in ann: | |
if anni is sig.empty: | |
continue | |
if isinstance(anni, type) and not isinstance(v, anni): | |
raise ValueError("Argument {} should be of type {} but is {}" | |
.format(k, ann.__name__, type(v).__name__)) | |
else: | |
# ignore non-type annotations | |
pass | |
else: | |
# ignore non-type, non-Container annotations | |
pass | |
return func(*bound.args, **bound.kwargs) | |
return wrapper | |
def type_checked_cast(func): | |
@wraps(func) | |
def wrapper(*args, **kwargs): | |
sig = signature(func) | |
bound = sig.bind(*args, **kwargs) | |
for k,v in bound.arguments.items(): | |
ann = sig.parameters[k].annotation | |
if isinstance(ann, type): | |
ann = (ann,) | |
if isinstance(ann, Iterable): | |
for anni in ann: | |
if anni is sig.empty: | |
continue | |
if isinstance(anni, type) and not isinstance(v, anni): | |
bound.arguments[k] = anni(v) | |
else: | |
# ignore non-type annotations | |
pass | |
else: | |
# ignore non-type, non-Container annotations | |
pass | |
return func(*bound.args, **bound.kwargs) | |
return wrapper |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment