Welcome, guest | Sign In | My Account | Store | Cart
#!/usr/bin/env python3
"A decorator to use Python3 annotations to type-check inputs and outputs"
import functools

def typecheck(f):
   
@functools.wraps(f)
   
def decorated(*args, **kws):
       
for i, name in enumerate(f.__code__.co_varnames):
            argtype
= f.__annotations__.get(name)
           
# Only check if annotation exists and it is as a type
           
if isinstance(argtype, type):
               
# First len(args) are positional, after that keywords
               
if i < len(args):
                   
assert isinstance(args[i], argtype)
               
elif name in kws:
                   
assert isinstance(kws[name], argtype)
        result
= f(*args, **kws)
        returntype
= f.__annotations__.get('return')
       
if isinstance(returntype, type):
           
assert isinstance(result, returntype)
       
return result
   
return decorated

#################################
# Simple self-test and examples
#################################

def check(func, TESTS):
   
for test in TESTS:
       
try:
            args
= test[:-1]
            expectation
= test[-1]
           
print("TEST: %s%r -> float ... " % (func.__name__, args), end="")
            result
= func(*args)
           
if expectation == "SUCCEED":
               
print("Succeed as expected")
           
else:
               
print("Succeed incorrectly")
       
except AssertionError:
           
if expectation == "FAIL":
               
print("Failed as expected")
           
else:
               
print("Failed incorrectly")

if __name__ == "__main__":
   
@typecheck
   
def happy1(a:int, b:list, c:tuple=(1,2,3)) -> float:
       
return 3.14

   
@typecheck
   
def happy_with_nontype(a:int, b:list, x:"non-type", c:tuple=(1,2,3)) -> float:
       
return 3.14

   
@typecheck
   
def happy_wo_annotation(a:int, b, c:tuple=(1,2,3)) -> float:
       
return 3.14

   
@typecheck
   
def unhappy1(a:int, b:str) -> float:
       
return 314  # This can never succeed in return type

    check
(happy1, [
       
(17, ['a','b'], "SUCCEED"),
       
(17, ['a','b'], (4,5,6), "SUCCEED"),
       
(17.0, ['a','b'], (4,5,6), "FAIL"),
       
(17, ('a','b'), "FAIL")])

    check
(happy_with_nontype, [
       
(17, ['a','b'], "whatever", "SUCCEED"),
       
(17, ['a','b'], 0xDEADBEAF, (4,5,6), "SUCCEED"),
       
(17.0, ['a','b'], "whatever", (4,5,6), "FAIL"),
       
(17, ('a','b'), "whatever", "FAIL")])

    check
(happy_wo_annotation, [
       
(17, 'b', "SUCCEED"),
       
(17, ['a','b'], (4,5,6), "SUCCEED"),
       
(17.0, 'b', (4,5,6), "FAIL"),
       
(17, 'b', [4,5,6], "FAIL")])

    check
(unhappy1, [
       
(17, "x", 'FAIL'),
       
(1.7, "x", 'FAIL')])

Diff to Previous Revision

--- revision 4 2013-05-23 22:45:09
+++ revision 5 2013-05-23 22:46:19
@@ -33,7 +33,7 @@
             
print("TEST: %s%r -> float ... " % (func.__name__, args), end="")
             result
= func(*args)
             
if expectation == "SUCCEED":
-                print("Succeeded as expected")
+                print("Succeed as expected")
             
else:
                 
print("Succeed incorrectly")
         
except AssertionError:

History