Author: Armin Rigo <ar...@tunes.org> 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 pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit