[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