[pypy-commit] pypy py3k: add support for more complex types in enforceargs, like [int] or {str:int}; also move the imports as late as possible, to prevent circular imports

antocuni noreply at buildbot.pypy.org
Tue Aug 21 11:51:04 CEST 2012


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: py3k
Changeset: r56772:f5c7bfc60abd
Date: 2012-08-21 11:48 +0200
http://bitbucket.org/pypy/pypy/changeset/f5c7bfc60abd/

Log:	add support for more complex types in enforceargs, like [int] or
	{str:int}; also move the imports as late as possible, to prevent
	circular imports

diff --git a/pypy/rlib/objectmodel.py b/pypy/rlib/objectmodel.py
--- a/pypy/rlib/objectmodel.py
+++ b/pypy/rlib/objectmodel.py
@@ -125,19 +125,39 @@
             return f
         return decorator
     #
-    from pypy.annotation.signature import annotationoftype
-    from pypy.annotation.model import SomeObject
-    def decorator(f):
+    def decorator(f): 
         def get_annotation(t):
+            from pypy.annotation.signature import annotation
+            from pypy.annotation.model import SomeObject
             if isinstance(t, SomeObject):
                 return t
-            return annotationoftype(t)
+            return annotation(t)
+        def get_type_descr_of_argument(arg):
+            # we don't want to check *all* the items in list/dict: we assume
+            # they are already homogeneous, so we only check the first
+            # item. The case of empty list/dict is handled inside typecheck()
+            if isinstance(arg, list):
+                item = arg[0]
+                return [get_type_descr_of_argument(item)]
+            elif isinstance(arg, dict):
+                key, value = next(arg.iteritems())
+                return {get_type_descr_of_argument(key): get_type_descr_of_argument(value)}
+            else:
+                return type(arg)
         def typecheck(*args):
+            from pypy.annotation.model import SomeList, SomeDict
             for i, (expected_type, arg) in enumerate(zip(types, args)):
                 if expected_type is None:
                     continue
                 s_expected = get_annotation(expected_type)
-                s_argtype = get_annotation(type(arg))
+                # special case: if we expect a list or dict and the argument
+                # is an empty list/dict, the typecheck always pass
+                if isinstance(s_expected, SomeList) and arg == []:
+                    continue
+                if isinstance(s_expected, SomeDict) and arg == {}:
+                    continue
+                #
+                s_argtype = get_annotation(get_type_descr_of_argument(arg))
                 if not s_expected.contains(s_argtype):
                     msg = "%s argument number %d must be of type %s" % (
                         f.func_name, i+1, expected_type)
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
@@ -444,6 +444,19 @@
     # in RPython there is an implicit int->float promotion
     assert f(42) == 42
 
+def test_enforceargs_complex_types():
+    @enforceargs([int], {str: int})
+    def f(a, b):
+        return a, b
+    x = [0, 1, 2]
+    y = {'a': 1, 'b': 2}
+    assert f(x, y) == (x, y)
+    assert f([], {}) == ([], {})
+    assert f(None, None) == (None, None)
+    py.test.raises(TypeError, "f(['hello'], y)")
+    py.test.raises(TypeError, "f(x, {'a': 'hello'})")
+    py.test.raises(TypeError, "f(x, {0: 42})")
+
 def test_enforceargs_no_typecheck():
     @enforceargs(int, str, None, typecheck=False)
     def f(a, b, c):


More information about the pypy-commit mailing list