def typecheck(func):
if not hasattr(func,'__annotations__'): return method
import inspect
argspec = inspect.getfullargspec(func)
def check( t, T ):
if type(T) == type: return isinstance(t,T) #types
else: return T(t) #predicates
def wrapper(*args):
if len(argspec.args) != len(args):
raise TypeError( "%s() takes exactly %s positional argument (%s given)"
%(func.__name__,len(argspec.args),len(args)) )
for argname,t in zip(argspec.args, args):
if argname in func.__annotations__:
T = func.__annotations__[argname]
if not check( t, T ):
raise TypeError( "%s( %s:%s ) but received %s=%s"
% (func.__name__, argname,T, argname,repr(t)) )
r = func(*args)
if 'return' in func.__annotations__:
T = func.__annotations__['return']
if not check( r, T ):
raise TypeError( "%s() -> %s but returned %s"%(func.__name__,T,repr(r)) )
return r
return wrapper
def Record(sig,*types):
cls_name, _, cls_vars = sig.partition(' ')
import collections
cls = collections.namedtuple(cls_name,cls_vars)
cls_vars = cls_vars.split(' ')
assert len(types)==len(cls_vars)
cls.__new__.__annotations__ = dict(zip(cls_vars,types))
cls.__new__ = typecheck(cls.__new__)
return cls
Diff to Previous Revision
--- revision 3 2010-06-01 08:57:21
+++ revision 4 2010-07-10 01:43:38
@@ -1,35 +1,35 @@
-from operator import itemgetter
-
-class Record(tuple):
- def __new__(cls,*types):
- names,types = (types[0].split(),types[1:]) if len(types)>0 and isinstance(types[0],str) else ([],types)
-
- class R(cls):
- __slots__ = ()
- _types = types
- def __new__(cls,*args):
- if not all( isinstance(t,T) for (t,T) in zip(args,R._types) ):
- raise TypeError("expected {T} got {t}".format(T=R._types,t=tuple(a.__class__ for a in args)) )
- return tuple.__new__(cls,args)
-
- for i in range(len(names)):
- setattr(R,names[i],property(itemgetter(i)))
-
- return R
+def typecheck(func):
+ if not hasattr(func,'__annotations__'): return method
+ import inspect
+ argspec = inspect.getfullargspec(func)
+ def check( t, T ):
+ if type(T) == type: return isinstance(t,T) #types
+ else: return T(t) #predicates
+ def wrapper(*args):
+ if len(argspec.args) != len(args):
+ raise TypeError( "%s() takes exactly %s positional argument (%s given)"
+ %(func.__name__,len(argspec.args),len(args)) )
+ for argname,t in zip(argspec.args, args):
+ if argname in func.__annotations__:
+ T = func.__annotations__[argname]
+ if not check( t, T ):
+ raise TypeError( "%s( %s:%s ) but received %s=%s"
+ % (func.__name__, argname,T, argname,repr(t)) )
+ r = func(*args)
+ if 'return' in func.__annotations__:
+ T = func.__annotations__['return']
+ if not check( r, T ):
+ raise TypeError( "%s() -> %s but returned %s"%(func.__name__,T,repr(r)) )
+ return r
+ return wrapper
-
-#example usage: a binary tree
-class Tree(Record): pass
-Leaf = Tree(object)
-Branch = Tree(Tree,Tree)
-
-
-left,right = btree = Branch( Leaf(1), Leaf(2) )
-
-
-#example usage: a namedtuple style record
-MyRec = Record("a b",int,str)
-m = MyRec(1,'a')
-print m.a
-print m.b
+def Record(sig,*types):
+ cls_name, _, cls_vars = sig.partition(' ')
+ import collections
+ cls = collections.namedtuple(cls_name,cls_vars)
+ cls_vars = cls_vars.split(' ')
+ assert len(types)==len(cls_vars)
+ cls.__new__.__annotations__ = dict(zip(cls_vars,types))
+ cls.__new__ = typecheck(cls.__new__)
+ return cls