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

Reply via email to