[pypy-commit] pypy better-enforceargs: do some typechecking on @enforceargs functions

antocuni noreply at buildbot.pypy.org
Tue Jul 17 18:10:56 CEST 2012


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: better-enforceargs
Changeset: r56106:0214d225faf9
Date: 2012-07-17 17:52 +0200
http://bitbucket.org/pypy/pypy/changeset/0214d225faf9/

Log:	do some typechecking on @enforceargs functions

diff --git a/pypy/rlib/objectmodel.py b/pypy/rlib/objectmodel.py
--- a/pypy/rlib/objectmodel.py
+++ b/pypy/rlib/objectmodel.py
@@ -3,6 +3,7 @@
 RPython-compliant way.
 """
 
+import py
 import sys
 import types
 import math
@@ -106,15 +107,43 @@
 
 specialize = _Specialize()
 
-def enforceargs(*args):
+def enforceargs(*types):
     """ Decorate a function with forcing of RPython-level types on arguments.
     None means no enforcing.
 
     XXX shouldn't we also add asserts in function body?
     """
+    import inspect
     def decorator(f):
-        f._annenforceargs_ = args
-        return f
+        def typecheck(*args):
+            for t, arg in zip(types, args):
+                if t is not None and not isinstance(arg, t):
+                    raise TypeError
+        #
+        # we cannot simply wrap the function using *args, **kwds, because it's
+        # not RPython. Instead, we generate a function with exactly the same
+        # argument list
+        argspec = inspect.getargspec(f)
+        assert len(argspec.args) == len(types), (
+            'not enough types provided: expected %d, got %d' %
+            (len(types), len(argspec.args)))
+        assert not argspec.varargs, '*args not supported by enforceargs'
+        assert not argspec.keywords, '**kwargs not supported by enforceargs'
+        #
+        arglist = ', '.join(argspec.args)
+        src = py.code.Source("""
+            def {name}({arglist}):
+                typecheck({arglist})
+                return {name}_original({arglist})
+        """.format(name=f.func_name, arglist=arglist))
+        #
+        mydict = {f.func_name + '_original': f,
+                  'typecheck': typecheck}
+        exec src.compile() in mydict
+        result = mydict[f.func_name]
+        # XXX defaults
+        result._annenforceargs_ = types
+        return result
     return decorator
 
 # ____________________________________________________________
diff --git a/pypy/rlib/test/test_objectmodel.py b/pypy/rlib/test/test_objectmodel.py
--- a/pypy/rlib/test/test_objectmodel.py
+++ b/pypy/rlib/test/test_objectmodel.py
@@ -420,9 +420,14 @@
 def test_enforceargs_decorator():
     @enforceargs(int, str, None)
     def f(a, b, c):
-        pass
+        return a, b, c
+    assert f._annenforceargs_ == (int, str, None)
+    assert f.func_name == 'f'
+    assert f(1, 'hello', 42) == (1, 'hello', 42)
+    py.test.raises(TypeError, "f(1, 2, 3)")
+    py.test.raises(TypeError, "f('hello', 'world', 3)")
 
-    assert f._annenforceargs_ == (int, str, None)
+    
 
 def getgraph(f, argtypes):
     from pypy.translator.translator import TranslationContext, graphof


More information about the pypy-commit mailing list