Author: Armin Rigo <[email protected]>
Branch: cffi-static-callback
Changeset: r80679:5fc8adadad4c
Date: 2015-11-15 08:58 +0100
http://bitbucket.org/pypy/pypy/changeset/5fc8adadad4c/
Log: in-progress
diff --git a/pypy/module/_cffi_backend/cffi1_module.py
b/pypy/module/_cffi_backend/cffi1_module.py
--- a/pypy/module/_cffi_backend/cffi1_module.py
+++ b/pypy/module/_cffi_backend/cffi1_module.py
@@ -10,16 +10,19 @@
VERSION_MIN = 0x2601
VERSION_MAX = 0x26FF
-VERSION_EXPORT = 0x0A02
+VERSION_EXPORT = 0x0A03
initfunctype = lltype.Ptr(lltype.FuncType([rffi.VOIDPP], lltype.Void))
def load_cffi1_module(space, name, path, initptr):
# This is called from pypy.module.cpyext.api.load_extension_module()
+ from pypy.module._cffi_backend.call_python import get_cffi_call_python
+
initfunc = rffi.cast(initfunctype, initptr)
- with lltype.scoped_alloc(rffi.VOIDPP.TO, 2) as p:
+ with lltype.scoped_alloc(rffi.VOIDPP.TO, 16, zero=True) as p:
p[0] = rffi.cast(rffi.VOIDP, VERSION_EXPORT)
+ p[1] = rffi.cast(rffi.VOIDP, get_cffi_call_python())
initfunc(p)
version = rffi.cast(lltype.Signed, p[0])
if not (VERSION_MIN <= version <= VERSION_MAX):
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,6 +54,7 @@
OP_DLOPEN_FUNC = 35
OP_DLOPEN_CONST = 37
OP_GLOBAL_VAR_F = 39
+OP_CALL_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
@@ -10,7 +10,7 @@
from pypy.module._cffi_backend import parse_c_type, realize_c_type
from pypy.module._cffi_backend import newtype, cerrno, ccallback, ctypearray
from pypy.module._cffi_backend import ctypestruct, ctypeptr, handle
-from pypy.module._cffi_backend import cbuffer, func, wrapper
+from pypy.module._cffi_backend import cbuffer, func, wrapper, call_python
from pypy.module._cffi_backend import cffi_opcode, allocator
from pypy.module._cffi_backend.ctypeobj import W_CType
from pypy.module._cffi_backend.cdataobj import W_CData
@@ -633,6 +633,7 @@
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),
dlclose = interp2app(W_FFIObject.descr_dlclose),
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
@@ -167,6 +167,14 @@
w_ctfnptr = w_ct.unwrap_as_fnptr(self.ffi)
w_result = W_CData(self.space, ptr, w_ctfnptr)
#
+ #
+ elif op == cffi_opcode.OP_CALL_PYTHON:
+ # for reading 'lib.bar' where bar is declared
+ # with CFFI_CALL_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')
+ w_result = w_ct.convert_to_object(rffi.cast(rffi.CCHARP, ptr))
else:
raise oefmt(space.w_NotImplementedError,
"in lib_build_attr: op=%d", op)
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,6 +71,12 @@
('error_location', rffi.SIZE_T),
('error_message', rffi.CCHARP))
+CALLPY_S = rffi.CStruct('_cffi_callpy_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),
('ctx', PCTX),
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
@@ -1376,3 +1376,216 @@
'test_share_FILE_b',
"FILE *barize(void) { return NULL; }")
lib1.do_stuff(lib2.barize())
+
+ def w_StdErrCapture(self, fd=False):
+ if fd:
+ # note: this is for a case where CPython prints to sys.stderr
+ # too, but not PyPy
+ import os
+ class MiniStringIO(object):
+ def __init__(self):
+ self._rd, self._wr = os.pipe()
+ self._result = None
+ def getvalue(self):
+ if self._result is None:
+ os.close(self._wr)
+ self._result = os.read(self._rd, 4096)
+ os.close(self._rd)
+ # xxx hack away these lines
+ while self._result.startswith('[platform:execute]'):
+ self._result = ''.join(
+ self._result.splitlines(True)[1:])
+ return self._result
+ class StdErrCapture(object):
+ def __enter__(self):
+ f = MiniStringIO()
+ self.old_fd2 = os.dup(2)
+ os.dup2(f._wr, 2)
+ return f
+ def __exit__(self, *args):
+ os.dup2(self.old_fd2, 2)
+ os.close(self.old_fd2)
+ return StdErrCapture()
+ else:
+ import sys
+ class MiniStringIO(object):
+ def __init__(self):
+ self._lst = []
+ self.write = self._lst.append
+ def getvalue(self):
+ return ''.join(self._lst)
+ class StdErrCapture(object):
+ def __enter__(self):
+ self.old_stderr = sys.stderr
+ sys.stderr = f = MiniStringIO()
+ return f
+ def __exit__(self, *args):
+ sys.stderr = self.old_stderr
+ return StdErrCapture()
+
+ def test_call_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', "")
+ 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")
+
+ @ffi.call_python("bar")
+ def my_bar(x, y):
+ seen.append(("Bar", x, y))
+ return x * y
+ assert my_bar == lib.bar
+ seen = []
+ res = lib.bar(6, 7)
+ assert seen == [("Bar", 6, 7)]
+ assert res == 42
+
+ @ffi.call_python()
+ def baz(x, y):
+ seen.append(("Baz", x, y))
+ seen = []
+ res = baz(50L, 8L)
+ assert res is None
+ assert seen == [("Baz", 50, 8)]
+ assert type(seen[0][1]) is type(seen[0][2]) is int
+ assert baz == lib.baz
+
+ @ffi.call_python()
+ def bok():
+ seen.append("Bok")
+ return 42
+ seen = []
+ assert lib.bok() == bok() == 42
+ assert seen == ["Bok", "Bok"]
+
+ @ffi.call_python()
+ def boz():
+ seen.append("Boz")
+ seen = []
+ assert lib.boz() is boz() is None
+ assert seen == ["Boz", "Boz"]
+
+ def test_call_python_bogus_name():
+ ffi = FFI()
+ ffi.cdef("int abc;")
+ lib = verify(ffi, 'test_call_python_bogus_name', "int abc;")
+ def fn():
+ pass
+ py.test.raises(ffi.error, ffi.call_python("unknown_name"), fn)
+ py.test.raises(ffi.error, ffi.call_python("abc"), fn)
+ assert lib.abc == 0
+ e = py.test.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 = py.test.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")
+ #
+ py.test.raises(TypeError, ffi.call_python(42), fn)
+ py.test.raises((TypeError, AttributeError), ffi.call_python(), "foo")
+ class X:
+ pass
+ x = X()
+ x.__name__ = x
+ py.test.raises(TypeError, ffi.call_python(), x)
+
+ def test_call_python_bogus_result_type():
+ ffi = FFI()
+ ffi.cdef("CFFI_CALL_PYTHON void bar(int);")
+ lib = verify(ffi, 'test_call_python_bogus_result_type', "")
+ #
+ def bar(n):
+ return n * 10
+ bar1 = ffi.call_python()(bar)
+ with 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")
+
+ def test_call_python_redefine():
+ ffi = FFI()
+ ffi.cdef("CFFI_CALL_PYTHON int bar(int);")
+ lib = verify(ffi, 'test_call_python_redefine', "")
+ #
+ @ffi.call_python()
+ def bar(n):
+ return n * 10
+ assert lib.bar(42) == 420
+ #
+ @ffi.call_python()
+ def bar(n):
+ return -n
+ assert lib.bar(42) == -42
+
+ def test_call_python_struct():
+ ffi = FFI()
+ ffi.cdef("""
+ 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);
+ """)
+ lib = verify(ffi, 'test_call_python_struct',
+ "struct foo_s { int a, b, c; };")
+ #
+ @ffi.call_python()
+ 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()
+ def baz(x, y):
+ return [x + y, x - y, x * y]
+ res = lib.baz(1000, 42)
+ assert res.a == 1042
+ assert res.b == 958
+ assert res.c == 42000
+ #
+ @ffi.call_python()
+ 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():
+ ffi = FFI()
+ ffi.cdef("""
+ CFFI_CALL_PYTHON int bar(int, long double, int);
+ CFFI_CALL_PYTHON long double baz(int, int);
+ CFFI_CALL_PYTHON long double bok(void);
+ """)
+ lib = verify(ffi, 'test_call_python_long_double', "")
+ #
+ @ffi.call_python()
+ def bar(x, l, z):
+ seen.append((x, l, z))
+ return 6
+ seen = []
+ lib.bar(10, 3.5, 20)
+ expected = ffi.cast("long double", 3.5)
+ assert repr(seen) == repr([(10, expected, 20)])
+ #
+ @ffi.call_python()
+ 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()
+ def bok():
+ return expected
+ res = lib.bok()
+ assert repr(res) == repr(expected)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit