Skip to content

Instantly share code, notes, and snippets.

@u8sand
Last active August 13, 2017 21:06
Show Gist options
  • Save u8sand/f13c45a40371ea8338ab98c5fbf3bc55 to your computer and use it in GitHub Desktop.
Save u8sand/f13c45a40371ea8338ab98c5fbf3bc55 to your computer and use it in GitHub Desktop.
Enforce new python3.6 type decorators at runtime with this decorator.
#!/bin/env python
def enforce(func):
''' Decorator to enforce type decorators at runtime via
type assertion. Usage:
@enforce
def foo(a : str, b : int, c = True : bool) -> str:
return None
'''
def enforce_wrapper(*args, **kwargs):
for k, v in dict(zip(func.__code__.co_varnames, args),
**dict(zip(func.__code__.co_varnames[len(args):func.__code__.co_argcount],
func.__defaults__) if func.__defaults__ else {},
**kwargs)).items():
assert type(v) == func.__annotations__.get(k, type(v)), \
"Argument `%s : %s` received type `%s`." % (
k, func.__annotations__.get(k), type(v))
ret = func(*args, **kwargs)
assert type(ret) == func.__annotations__.get('return', type(ret)), \
"`%s(...) -> %s` returned type `%s`." % (
func.__name__, func.__annotations__.get('return'), type(ret)
)
return ret
return enforce_wrapper
from enforce import enforce
from unittest import TestCase
@enforce
def test_1() -> bool:
return None
@enforce
def test_2(a : str, b, c : bool = 0) -> str:
return ''
class TestEnforce(TestCase):
def test_return_error(self):
with self.assertRaises(AssertionError):
test_1()
def test_no_error(self):
test_2('a', 'b', False)
def test_bad_arg(self):
with self.assertRaises(AssertionError):
test_2('a', 0, 0)
def test_default_type_error(self):
with self.assertRaises(AssertionError):
test_2('a', 'b')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment