[pypy-commit] pypy cffi-static-callback: update to the 'extern "Python"' style

arigo noreply at buildbot.pypy.org
Wed Nov 18 05:04:55 EST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: cffi-static-callback
Changeset: r80751:c9f979361cde
Date: 2015-11-18 11:05 +0100
http://bitbucket.org/pypy/pypy/changeset/c9f979361cde/

Log:	update to the 'extern "Python"' style

diff --git a/pypy/module/_cffi_backend/call_python.py b/pypy/module/_cffi_backend/call_python.py
--- a/pypy/module/_cffi_backend/call_python.py
+++ b/pypy/module/_cffi_backend/call_python.py
@@ -16,18 +16,18 @@
 
 
 STDERR = 2
-CALLPY_FN = lltype.FuncType([parse_c_type.PCALLPY, rffi.CCHARP],
-                            lltype.Void)
+EXTERNPY_FN = lltype.FuncType([parse_c_type.PEXTERNPY, rffi.CCHARP],
+                              lltype.Void)
 
 
-def _cffi_call_python(ll_callpy, ll_args):
-    """Invoked by the helpers generated from CFFI_CALL_PYTHON in the cdef.
+def _cffi_call_python(ll_externpy, ll_args):
+    """Invoked by the helpers generated from extern "Python" in the cdef.
 
-       'callpy' is a static structure that describes which of the
-       CFFI_CALL_PYTHON is called.  It has got fields 'name' and
+       'externpy' is a static structure that describes which of the
+       extern "Python" functions is called.  It has got fields 'name' and
        'type_index' describing the function, and more reserved fields
        that are initially zero.  These reserved fields are set up by
-       ffi.call_python(), which invokes init_call_python() below.
+       ffi.def_extern(), which invokes externpy_deco() below.
 
        'args' is a pointer to an array of 8-byte entries.  Each entry
        contains an argument.  If an argument is less than 8 bytes, only
@@ -35,8 +35,9 @@
        argument is 'long double' or a struct/union, then it is passed
        by reference.
 
-       'args' is also used as the place to write the result to.  In all
-       cases, 'args' is at least 8 bytes in size.
+       'args' is also used as the place to write the result to
+       (directly, even if more than 8 bytes).  In all cases, 'args' is
+       at least 8 bytes in size.
     """
     from pypy.module._cffi_backend.ccallback import reveal_callback
 
@@ -48,25 +49,25 @@
 
     cerrno._errno_after(rffi.RFFI_ERR_ALL | rffi.RFFI_ALT_ERRNO)
 
-    if not ll_callpy.c_reserved1:
+    if not ll_externpy.c_reserved1:
         # Not initialized!  We don't have a space at all, so just
         # write the error to the file descriptor stderr.  (xxx cpython's
         # cffi writes it to sys.stderr)
         try:
-            funcname = rffi.charp2str(ll_callpy.c_name)
-            msg = ("CFFI_CALL_PYTHON: function %s() called, but no code was "
-                   "attached to it yet with ffi.call_python('%s').  "
-                   "Returning 0.\n" % (funcname, funcname))
+            funcname = rffi.charp2str(ll_externpy.c_name)
+            msg = ("extern \"Python\": function %s() called, but no code was "
+                   "attached to it yet with @ffi.def_extern().  "
+                   "Returning 0.\n" % (funcname,))
             os.write(STDERR, msg)
         except:
             pass
-        for i in range(intmask(ll_callpy.c_size_of_result)):
+        for i in range(intmask(ll_externpy.c_size_of_result)):
             ll_args[i] = '\x00'
     else:
-        callpython = reveal_callback(ll_callpy.c_reserved1)
+        externpython = reveal_callback(ll_externpy.c_reserved1)
         # the same buffer is used both for passing arguments and
         # the result value
-        callpython.invoke(ll_args, ll_args)
+        externpython.invoke(ll_args, ll_args)
 
     cerrno._errno_before(rffi.RFFI_ERR_ALL | rffi.RFFI_ALT_ERRNO)
 
@@ -77,7 +78,7 @@
 
 
 def get_ll_cffi_call_python():
-    return llhelper(lltype.Ptr(CALLPY_FN), _cffi_call_python)
+    return llhelper(lltype.Ptr(EXTERNPY_FN), _cffi_call_python)
 
 
 class KeepaliveCache:
@@ -86,9 +87,9 @@
 
 
 @jit.dont_look_inside
-def callpy_deco(space, w_ffi, w_python_callable, w_name, w_error, w_onerror):
+def externpy_deco(space, w_ffi, w_python_callable, w_name, w_error, w_onerror):
     from pypy.module._cffi_backend.ffi_obj import W_FFIObject
-    from pypy.module._cffi_backend.ccallback import W_CallPython
+    from pypy.module._cffi_backend.ccallback import W_ExternPython
 
     ffi = space.interp_w(W_FFIObject, w_ffi)
 
@@ -99,27 +100,28 @@
     ctx = ffi.ctxobj.ctx
     index = parse_c_type.search_in_globals(ctx, name)
     if index < 0:
-        raise callpy_not_found(ffi, name)
+        raise externpy_not_found(ffi, name)
 
     g = ctx.c_globals[index]
-    if getop(g.c_type_op) != cffi_opcode.OP_CALL_PYTHON:
-        raise callpy_not_found(ffi, name)
+    if getop(g.c_type_op) != cffi_opcode.OP_EXTERN_PYTHON:
+        raise externpy_not_found(ffi, name)
 
     w_ct = realize_c_type.realize_c_type(ffi, ctx.c_types, getarg(g.c_type_op))
 
-    # make a W_CallPython instance, which is nonmovable; then cast it
+    # make a W_ExternPython instance, which is nonmovable; then cast it
     # to a raw pointer and assign it to the field 'reserved1' of the
-    # callpy object from C.  We must make sure to keep it alive forever,
-    # or at least until ffi.call_python() is used again to change the
-    # binding.  Note that the W_CallPython is never exposed to the user.
-    callpy = rffi.cast(parse_c_type.PCALLPY, g.c_address)
-    callpython = instantiate(W_CallPython, nonmovable=True)
-    W_CallPython.__init__(callpython, space, rffi.cast(rffi.CCHARP, callpy),
+    # externpy object from C.  We must make sure to keep it alive forever,
+    # or at least until ffi.def_extern() is used again to change the
+    # binding.  Note that the W_ExternPython is never exposed to the user.
+    externpy = rffi.cast(parse_c_type.PEXTERNPY, g.c_address)
+    externpython = instantiate(W_ExternPython, nonmovable=True)
+    cdata = rffi.cast(rffi.CCHARP, externpy)
+    W_ExternPython.__init__(externpython, space, cdata,
                           w_ct, w_python_callable, w_error, w_onerror)
 
-    key = rffi.cast(lltype.Signed, callpy)
-    space.fromcache(KeepaliveCache).cache_dict[key] = callpython
-    callpy.c_reserved1 = callpython.hide_object()
+    key = rffi.cast(lltype.Signed, externpy)
+    space.fromcache(KeepaliveCache).cache_dict[key] = externpython
+    externpy.c_reserved1 = externpython.hide_object()
 
     # return a cdata of type function-pointer, equal to the one
     # obtained by reading 'lib.bar' (see lib_obj.py)
@@ -127,11 +129,11 @@
     return w_ct.convert_to_object(rffi.cast(rffi.CCHARP, ptr))
 
 
-def callpy_not_found(ffi, name):
+def externpy_not_found(ffi, name):
     raise oefmt(ffi.w_FFIError,
-                "ffi.call_python('%s'): name not found as a "
-                "CFFI_CALL_PYTHON line from the cdef", name)
+                "ffi.def_extern('%s'): no 'extern \"Python\"' "
+                "function with this name", name)
 
 @specialize.memo()
 def get_generic_decorator(space):
-    return space.wrap(interp2app(callpy_deco))
+    return space.wrap(interp2app(externpy_deco))
diff --git a/pypy/module/_cffi_backend/ccallback.py b/pypy/module/_cffi_backend/ccallback.py
--- a/pypy/module/_cffi_backend/ccallback.py
+++ b/pypy/module/_cffi_backend/ccallback.py
@@ -32,7 +32,7 @@
 def reveal_callback(raw_ptr):
     addr = rffi.cast(llmemory.Address, raw_ptr)
     gcref = rgc.reveal_gcref(addr)
-    return rgc.try_cast_gcref_to_instance(W_CallPython, gcref)
+    return rgc.try_cast_gcref_to_instance(W_ExternPython, gcref)
 
 
 class Closure(object):
@@ -45,7 +45,7 @@
         clibffi.closureHeap.free(rffi.cast(clibffi.FFI_CLOSUREP, self.ptr))
 
 
-class W_CallPython(W_CData):
+class W_ExternPython(W_CData):
     """Base class for W_CDataCallback, also used from call_python.py.
     """
     decode_args_from_libffi = False
@@ -126,13 +126,13 @@
             space.threadlocals.leave_thread(space)
 
     def py_invoke(self, ll_res, ll_args):
-        # For W_CallPython only; overridden in W_CDataCallback.  Note
+        # For W_ExternPython only; overridden in W_CDataCallback.  Note
         # that the details of the two jitdrivers differ.  For
-        # W_CallPython, it depends on the identity of 'self', which
-        # means every @ffi.call_python() gets its own machine code,
+        # W_ExternPython, it depends on the identity of 'self', which
+        # means every @ffi.def_extern() gets its own machine code,
         # which sounds reasonable here.  Moreover, 'll_res' is ignored
         # as it is always equal to 'll_args'.
-        jitdriver2.jit_merge_point(callpython=self, ll_args=ll_args)
+        jitdriver2.jit_merge_point(externpython=self, ll_args=ll_args)
         self.do_invoke(ll_args, ll_args)
 
     def do_invoke(self, ll_res, ll_args):
@@ -196,15 +196,15 @@
                                        "another exception occurred:\n\n")
 
 
-class W_CDataCallback(W_CallPython):
+class W_CDataCallback(W_ExternPython):
     _immutable_fields_ = ['key_pycode']
     decode_args_from_libffi = True
 
     def __init__(self, space, ctype, w_callable, w_error, w_onerror):
         raw_closure = rffi.cast(rffi.CCHARP, clibffi.closureHeap.alloc())
         self._closure = Closure(raw_closure)
-        W_CallPython.__init__(self, space, raw_closure, ctype,
-                              w_callable, w_error, w_onerror)
+        W_ExternPython.__init__(self, space, raw_closure, ctype,
+                                w_callable, w_error, w_onerror)
         self.key_pycode = space._try_fetch_pycode(w_callable)
         #
         cif_descr = self.getfunctype().cif_descr
@@ -277,7 +277,7 @@
 STDERR = 2
 
 
-# jitdrivers, for both W_CDataCallback and W_CallPython
+# jitdrivers, for both W_CDataCallback and W_ExternPython
 
 def get_printable_location1(key_pycode):
     if key_pycode is None:
@@ -289,13 +289,13 @@
                            reds=['ll_res', 'll_args', 'callback'],
                            get_printable_location=get_printable_location1)
 
-def get_printable_location2(callpython):
-    with callpython as ptr:
-        callpy = rffi.cast(parse_c_type.PCALLPY, ptr)
-        return 'cffi_call_python ' + rffi.charp2str(callpy.c_name)
+def get_printable_location2(externpython):
+    with externpython as ptr:
+        externpy = rffi.cast(parse_c_type.PEXTERNPY, ptr)
+        return 'cffi_call_python ' + rffi.charp2str(externpy.c_name)
 
 jitdriver2 = jit.JitDriver(name='cffi_call_python',
-                           greens=['callpython'],
+                           greens=['externpython'],
                            reds=['ll_args'],
                            get_printable_location=get_printable_location2)
 
diff --git a/pypy/module/_cffi_backend/cffi_opcode.py b/pypy/module/_cffi_backend/cffi_opcode.py
--- a/pypy/module/_cffi_backend/cffi_opcode.py
+++ b/pypy/module/_cffi_backend/cffi_opcode.py
@@ -54,7 +54,7 @@
 OP_DLOPEN_FUNC     = 35
 OP_DLOPEN_CONST    = 37
 OP_GLOBAL_VAR_F    = 39
-OP_CALL_PYTHON     = 41
+OP_EXTERN_PYTHON   = 41
 
 PRIM_VOID          = 0
 PRIM_BOOL          = 1
diff --git a/pypy/module/_cffi_backend/ffi_obj.py b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -282,11 +282,11 @@
     @unwrap_spec(w_name=WrappedDefault(None),
                  w_error=WrappedDefault(None),
                  w_onerror=WrappedDefault(None))
-    def descr_call_python(self, w_name, w_error, w_onerror):
+    def descr_def_extern(self, w_name, w_error, w_onerror):
         """\
 A decorator.  Attaches the decorated Python function to the C code
-generated for the CFFI_CALL_PYTHON function of the same name.  Calling
-the C function will then invoke the Python function.
+generated for the 'extern "Python" function of the same name.
+Calling the C function will then invoke the Python function.
 
 Optional arguments: 'name' is the name of the C function, if
 different from the Python function; and 'error' and 'onerror'
@@ -657,9 +657,9 @@
         addressof   = interp2app(W_FFIObject.descr_addressof),
         alignof     = interp2app(W_FFIObject.descr_alignof),
         buffer      = interp2app(W_FFIObject.descr_buffer),
-        call_python = interp2app(W_FFIObject.descr_call_python),
         callback    = interp2app(W_FFIObject.descr_callback),
         cast        = interp2app(W_FFIObject.descr_cast),
+        def_extern  = interp2app(W_FFIObject.descr_def_extern),
         dlclose     = interp2app(W_FFIObject.descr_dlclose),
         dlopen      = interp2app(W_FFIObject.descr_dlopen),
         from_buffer = interp2app(W_FFIObject.descr_from_buffer),
diff --git a/pypy/module/_cffi_backend/lib_obj.py b/pypy/module/_cffi_backend/lib_obj.py
--- a/pypy/module/_cffi_backend/lib_obj.py
+++ b/pypy/module/_cffi_backend/lib_obj.py
@@ -168,9 +168,9 @@
                 w_result = W_CData(self.space, ptr, w_ctfnptr)
                 #
                 #
-            elif op == cffi_opcode.OP_CALL_PYTHON:
+            elif op == cffi_opcode.OP_EXTERN_PYTHON:
                 # for reading 'lib.bar' where bar is declared
-                # with CFFI_CALL_PYTHON
+                # as an extern "Python"
                 w_ct = realize_c_type.realize_c_type(
                     self.ffi, self.ctx.c_types, getarg(g.c_type_op))
                 ptr = lltype.direct_fieldptr(g, 'c_size_or_direct_fn')
diff --git a/pypy/module/_cffi_backend/parse_c_type.py b/pypy/module/_cffi_backend/parse_c_type.py
--- a/pypy/module/_cffi_backend/parse_c_type.py
+++ b/pypy/module/_cffi_backend/parse_c_type.py
@@ -71,11 +71,11 @@
                         ('error_location', rffi.SIZE_T),
                         ('error_message', rffi.CCHARP))
 
-PCALLPY = rffi.CStructPtr('_cffi_callpy_s',
-                          ('name', rffi.CCHARP),
-                          ('size_of_result', rffi.SIZE_T),
-                          ('reserved1', rffi.VOIDP),
-                          ('reserved2', rffi.VOIDP))
+PEXTERNPY = rffi.CStructPtr('_cffi_externpy_s',
+                            ('name', rffi.CCHARP),
+                            ('size_of_result', rffi.SIZE_T),
+                            ('reserved1', rffi.VOIDP),
+                            ('reserved2', rffi.VOIDP))
 
 GETCONST_S = rffi.CStruct('_cffi_getconst_s',
                           ('value', rffi.ULONGLONG),
diff --git a/pypy/module/_cffi_backend/src/parse_c_type.h b/pypy/module/_cffi_backend/src/parse_c_type.h
--- a/pypy/module/_cffi_backend/src/parse_c_type.h
+++ b/pypy/module/_cffi_backend/src/parse_c_type.h
@@ -27,7 +27,7 @@
 #define _CFFI_OP_DLOPEN_FUNC    35
 #define _CFFI_OP_DLOPEN_CONST   37
 #define _CFFI_OP_GLOBAL_VAR_F   39
-#define _CFFI_OP_CALL_PYTHON    41
+#define _CFFI_OP_EXTERN_PYTHON  41
 
 #define _CFFI_PRIM_VOID          0
 #define _CFFI_PRIM_BOOL          1
@@ -161,7 +161,7 @@
     const char *error_message;
 };
 
-struct _cffi_callpy_s {
+struct _cffi_externpy_s {
     const char *name;
     size_t size_of_result;
     void *reserved1, *reserved2;
diff --git a/pypy/module/_cffi_backend/test/test_recompiler.py b/pypy/module/_cffi_backend/test/test_recompiler.py
--- a/pypy/module/_cffi_backend/test/test_recompiler.py
+++ b/pypy/module/_cffi_backend/test/test_recompiler.py
@@ -1423,22 +1423,24 @@
                     sys.stderr = self.old_stderr
             return StdErrCapture()
 
-    def test_call_python_1(self):
+    def test_extern_python_1(self):
         ffi, lib = self.prepare("""
-            CFFI_CALL_PYTHON int bar(int, int);
-            CFFI_CALL_PYTHON void baz(int, int);
-            CFFI_CALL_PYTHON int bok(void);
-            CFFI_CALL_PYTHON void boz(void);
-        """, 'test_call_python_1', "")
+            extern "Python" {
+                int bar(int, int);
+                void baz(int, int);
+                int bok(void);
+                void boz(void);
+            }
+        """, 'test_extern_python_1', "")
         assert ffi.typeof(lib.bar) == ffi.typeof("int(*)(int, int)")
         with self.StdErrCapture(fd=True) as f:
             res = lib.bar(4, 5)
         assert res == 0
         assert f.getvalue() == (
-            "CFFI_CALL_PYTHON: function bar() called, but no code was attached "
-            "to it yet with ffi.call_python('bar').  Returning 0.\n")
+            "extern \"Python\": function bar() called, but no code was attached "
+            "to it yet with @ffi.def_extern().  Returning 0.\n")
 
-        @ffi.call_python("bar")
+        @ffi.def_extern("bar")
         def my_bar(x, y):
             seen.append(("Bar", x, y))
             return x * y
@@ -1448,7 +1450,7 @@
         assert seen == [("Bar", 6, 7)]
         assert res == 42
 
-        @ffi.call_python()
+        @ffi.def_extern()
         def baz(x, y):
             seen.append(("Baz", x, y))
         seen = []
@@ -1458,91 +1460,90 @@
         assert type(seen[0][1]) is type(seen[0][2]) is int
         assert baz == lib.baz
 
-        @ffi.call_python()
-        def bok():
+        @ffi.def_extern(name="bok")
+        def bokk():
             seen.append("Bok")
             return 42
         seen = []
-        assert lib.bok() == bok() == 42
+        assert lib.bok() == bokk() == 42
         assert seen == ["Bok", "Bok"]
 
-        @ffi.call_python()
+        @ffi.def_extern()
         def boz():
             seen.append("Boz")
         seen = []
         assert lib.boz() is boz() is None
         assert seen == ["Boz", "Boz"]
 
-    def test_call_python_bogus_name(self):
+    def test_extern_python_bogus_name(self):
         ffi, lib = self.prepare("int abc;",
-                                'test_call_python_bogus_name',
+                                'test_extern_python_bogus_name',
                                 "int abc;")
         def fn():
             pass
-        raises(ffi.error, ffi.call_python("unknown_name"), fn)
-        raises(ffi.error, ffi.call_python("abc"), fn)
+        raises(ffi.error, ffi.def_extern("unknown_name"), fn)
+        raises(ffi.error, ffi.def_extern("abc"), fn)
         assert lib.abc == 0
-        e = raises(ffi.error, ffi.call_python("abc"), fn)
-        assert str(e.value) == ("ffi.call_python('abc'): name not found as a "
-                                "CFFI_CALL_PYTHON line from the cdef")
-        e = raises(ffi.error, ffi.call_python(), fn)
-        assert str(e.value) == ("ffi.call_python('fn'): name not found as a "
-                                "CFFI_CALL_PYTHON line from the cdef")
+        e = raises(ffi.error, ffi.def_extern("abc"), fn)
+        assert str(e.value) == ("ffi.def_extern('abc'): no 'extern \"Python\"' "
+                                "function with this name")
+        e = raises(ffi.error, ffi.def_extern(), fn)
+        assert str(e.value) == ("ffi.def_extern('fn'): no 'extern \"Python\"' "
+                                "function with this name")
         #
-        raises(TypeError, ffi.call_python(42), fn)
-        raises((TypeError, AttributeError), ffi.call_python(), "foo")
+        raises(TypeError, ffi.def_extern(42), fn)
+        raises((TypeError, AttributeError), ffi.def_extern(), "foo")
         class X:
             pass
         x = X()
         x.__name__ = x
-        raises(TypeError, ffi.call_python(), x)
+        raises(TypeError, ffi.def_extern(), x)
 
-    def test_call_python_bogus_result_type(self):
-        ffi, lib = self.prepare("CFFI_CALL_PYTHON void bar(int);",
-                                'test_call_python_bogus_result_type',
+    def test_extern_python_bogus_result_type(self):
+        ffi, lib = self.prepare("""extern "Python" void bar(int);""",
+                                'test_extern_python_bogus_result_type',
                                 "")
         def bar(n):
             return n * 10
-        bar1 = ffi.call_python()(bar)
+        bar1 = ffi.def_extern()(bar)
         with self.StdErrCapture() as f:
             res = bar1(321)
         assert res is None
         assert f.getvalue() == (
             "From cffi callback %r:\n" % (bar,) +
             "Trying to convert the result back to C:\n"
-            "TypeError: callback with the return type 'void' must return None\n"
-            )
+            "TypeError: callback with the return type 'void' must return None\n")
 
-    def test_call_python_redefine(self):
-        ffi, lib = self.prepare("CFFI_CALL_PYTHON int bar(int);",
-                                'test_call_python_redefine',
+    def test_extern_python_redefine(self):
+        ffi, lib = self.prepare("""extern "Python" int bar(int);""",
+                                'test_extern_python_redefine',
                                 "")
-        @ffi.call_python()
+        @ffi.def_extern()
         def bar(n):
             return n * 10
         assert lib.bar(42) == 420
         #
-        @ffi.call_python()
+        @ffi.def_extern()
         def bar(n):
             return -n
         assert lib.bar(42) == -42
 
-    def test_call_python_struct(self):
+    def test_extern_python_struct(self):
         ffi, lib = self.prepare("""
             struct foo_s { int a, b, c; };
-            CFFI_CALL_PYTHON int bar(int, struct foo_s, int);
-            CFFI_CALL_PYTHON struct foo_s baz(int, int);
-            CFFI_CALL_PYTHON struct foo_s bok(void);
-        """, 'test_call_python_struct',
-            "struct foo_s { int a, b, c; };")
+            extern "Python" int bar(int, struct foo_s, int);
+            extern "Python" { struct foo_s baz(int, int);
+                              struct foo_s bok(void); }
+        """, 'test_extern_python_struct',
+             "struct foo_s { int a, b, c; };")
         #
-        @ffi.call_python()
+        @ffi.def_extern()
         def bar(x, s, z):
             return x + s.a + s.b + s.c + z
         res = lib.bar(1000, [1001, 1002, 1004], 1008)
         assert res == 5015
         #
-        @ffi.call_python()
+        @ffi.def_extern()
         def baz(x, y):
             return [x + y, x - y, x * y]
         res = lib.baz(1000, 42)
@@ -1550,20 +1551,20 @@
         assert res.b == 958
         assert res.c == 42000
         #
-        @ffi.call_python()
+        @ffi.def_extern()
         def bok():
             return [10, 20, 30]
         res = lib.bok()
         assert [res.a, res.b, res.c] == [10, 20, 30]
 
-    def test_call_python_long_double(self):
+    def test_extern_python_long_double(self):
         ffi, lib = self.prepare("""
-            CFFI_CALL_PYTHON int bar(int, long double, int);
-            CFFI_CALL_PYTHON long double baz(int, int);
-            CFFI_CALL_PYTHON long double bok(void);
-        """, 'test_call_python_long_double', "")
+            extern "Python" int bar(int, long double, int);
+            extern "Python" long double baz(int, int);
+            extern "Python" long double bok(void);
+        """, 'test_extern_python_long_double', "")
         #
-        @ffi.call_python()
+        @ffi.def_extern()
         def bar(x, l, z):
             seen.append((x, l, z))
             return 6
@@ -1572,15 +1573,53 @@
         expected = ffi.cast("long double", 3.5)
         assert repr(seen) == repr([(10, expected, 20)])
         #
-        @ffi.call_python()
+        @ffi.def_extern()
         def baz(x, z):
             assert x == 10 and z == 20
             return expected
         res = lib.baz(10, 20)
         assert repr(res) == repr(expected)
         #
-        @ffi.call_python()
+        @ffi.def_extern()
         def bok():
             return expected
         res = lib.bok()
         assert repr(res) == repr(expected)
+
+    def test_extern_python_signature(self):
+        ffi, lib = self.prepare("", 'test_extern_python_signature', "")
+        raises(TypeError, ffi.def_extern(425), None)
+        raises(TypeError, ffi.def_extern, 'a', 'b', 'c', 'd')
+
+    def test_extern_python_errors(self):
+        ffi, lib = self.prepare("""
+            extern "Python" int bar(int);
+        """, 'test_extern_python_errors', "")
+
+        seen = []
+        def oops(*args):
+            seen.append(args)
+
+        @ffi.def_extern(onerror=oops)
+        def bar(x):
+            return x + ""
+        assert bar(10) == 0
+
+        @ffi.def_extern(name="bar", onerror=oops, error=-66)
+        def bar2(x):
+            return x + ""
+        assert bar(10) == -66
+
+        assert len(seen) == 2
+        exc, val, tb = seen[0]
+        assert exc is TypeError
+        assert isinstance(val, TypeError)
+        assert tb.tb_frame.f_code.co_name == "bar"
+        exc, val, tb = seen[1]
+        assert exc is TypeError
+        assert isinstance(val, TypeError)
+        assert tb.tb_frame.f_code.co_name == "bar2"
+        #
+        # a case where 'onerror' is not callable
+        raises(TypeError, ffi.def_extern(name='bar', onerror=42),
+                       lambda x: x)


More information about the pypy-commit mailing list