[pypy-commit] cffi default: Performance: no real need to call PyArg_ParseTuple() here

arigo noreply at buildbot.pypy.org
Tue May 26 13:15:07 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r2104:e756b0b13434
Date: 2015-05-26 13:15 +0200
http://bitbucket.org/cffi/cffi/changeset/e756b0b13434/

Log:	Performance: no real need to call PyArg_ParseTuple() here

diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h
--- a/cffi/_cffi_include.h
+++ b/cffi/_cffi_include.h
@@ -51,6 +51,11 @@
 # endif
 #endif
 
+#ifdef __GNUC__
+# define _CFFI_UNUSED_FN  __attribute__((unused))
+#else
+# define _CFFI_UNUSED_FN  /* nothing */
+#endif
 
 /**********  CPython-specific section  **********/
 #ifndef PYPY_VERSION
@@ -182,6 +187,20 @@
     return NULL;
 }
 
+_CFFI_UNUSED_FN
+static PyObject **_cffi_unpack_args(PyObject *args_tuple, Py_ssize_t expected,
+                                    const char *fnname)
+{
+    if (PyTuple_GET_SIZE(args_tuple) != expected) {
+        PyErr_Format(PyExc_TypeError,
+                     "%.150s() takes exactly %zd arguments (%zd given)",
+                     fnname, expected, PyTuple_GET_SIZE(args_tuple));
+        return NULL;
+    }
+    return &PyTuple_GET_ITEM(args_tuple, 0);   /* pointer to the first item,
+                                                  the others follow */
+}
+
 #endif
 /**********  end CPython-specific section  **********/
 
@@ -201,12 +220,6 @@
     ((got_nonpos) == (expected <= 0) &&                 \
      (got) == (unsigned long long)expected)
 
-#ifdef __GNUC__
-# define _CFFI_UNUSED_FN  __attribute__((unused))
-#else
-# define _CFFI_UNUSED_FN  /* nothing */
-#endif
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -632,10 +632,13 @@
             rng = range(len(tp.args))
             for i in rng:
                 prnt('  PyObject *arg%d;' % i)
+            prnt('  PyObject **aa;')
             prnt()
-            prnt('  if (!PyArg_ParseTuple(args, "%s:%s", %s))' % (
-                'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng])))
+            prnt('  aa = _cffi_unpack_args(args, %d, "%s");' % (len(rng), name))
+            prnt('  if (aa == NULL)')
             prnt('    return NULL;')
+            for i in rng:
+                prnt('  arg%d = aa[%d];' % (i, i))
         prnt()
         #
         for i, type in enumerate(tp.args):
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -776,3 +776,32 @@
     #endif
     """)
     assert lib.CORRECT == 1
+
+def test_unpack_args():
+    ffi = FFI()
+    ffi.cdef("void foo0(void); void foo1(int); void foo2(int, int);")
+    lib = verify(ffi, "test_unpack_args", """
+    void foo0(void) { }
+    void foo1(int x) { }
+    void foo2(int x, int y) { }
+    """)
+    assert 'foo0' in repr(lib.foo0)
+    assert 'foo1' in repr(lib.foo1)
+    assert 'foo2' in repr(lib.foo2)
+    lib.foo0()
+    lib.foo1(42)
+    lib.foo2(43, 44)
+    e1 = py.test.raises(TypeError, lib.foo0, 42)
+    e2 = py.test.raises(TypeError, lib.foo0, 43, 44)
+    e3 = py.test.raises(TypeError, lib.foo1)
+    e4 = py.test.raises(TypeError, lib.foo1, 43, 44)
+    e5 = py.test.raises(TypeError, lib.foo2)
+    e6 = py.test.raises(TypeError, lib.foo2, 42)
+    e7 = py.test.raises(TypeError, lib.foo2, 45, 46, 47)
+    assert str(e1.value) == "foo0() takes no arguments (1 given)"
+    assert str(e2.value) == "foo0() takes no arguments (2 given)"
+    assert str(e3.value) == "foo1() takes exactly one argument (0 given)"
+    assert str(e4.value) == "foo1() takes exactly one argument (2 given)"
+    assert str(e5.value) == "foo2() takes exactly 2 arguments (0 given)"
+    assert str(e6.value) == "foo2() takes exactly 2 arguments (1 given)"
+    assert str(e7.value) == "foo2() takes exactly 2 arguments (3 given)"


More information about the pypy-commit mailing list