Welcome, guest | Sign In | My Account | Store | Cart
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

History