Author: Wim Lavrijsen <[email protected]>
Branch: reflex-support
Changeset: r55403:665ede1ab7ce
Date: 2012-06-04 16:34 -0700
http://bitbucket.org/pypy/pypy/changeset/665ede1ab7ce/

Log:    allow PyObject* function arguments and returns (with their tests)

diff --git a/pypy/module/cppyy/converter.py b/pypy/module/cppyy/converter.py
--- a/pypy/module/cppyy/converter.py
+++ b/pypy/module/cppyy/converter.py
@@ -81,7 +81,7 @@
     def finalize_call(self, space, w_obj, call_local):
         pass
 
-    def free_argument(self, arg, call_local):
+    def free_argument(self, space, arg, call_local):
         pass
 
 
@@ -498,7 +498,7 @@
         charpptr = rffi.cast(rffi.CCHARPP, address)
         return space.wrap(rffi.charp2str(charpptr[0]))
 
-    def free_argument(self, arg, call_local):
+    def free_argument(self, space, arg, call_local):
         lltype.free(rffi.cast(rffi.CCHARPP, arg)[0], flavor='raw')
 
 
@@ -733,7 +733,7 @@
             pass
         return InstanceConverter.to_memory(self, space, w_obj, w_value, offset)
 
-    def free_argument(self, arg, call_local):
+    def free_argument(self, space, arg, call_local):
         capi.c_free_stdstring(rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, 
arg)[0]))
 
 class StdStringRefConverter(InstancePtrConverter):
@@ -745,6 +745,35 @@
         InstancePtrConverter.__init__(self, space, cppclass)
 
 
+class PyObjectConverter(TypeConverter):
+    _immutable_ = True
+
+    def convert_argument(self, space, w_obj, address, call_local):
+        if hasattr(space, "fake"):
+            raise NotImplementedError
+        space.getbuiltinmodule("cpyext")
+        from pypy.module.cpyext.pyobject import make_ref
+        ref = make_ref(space, w_obj)
+        x = rffi.cast(rffi.VOIDPP, address)
+        x[0] = rffi.cast(rffi.VOIDP, ref);
+        ba = rffi.cast(rffi.CCHARP, address)
+        ba[capi.c_function_arg_typeoffset()] = 'a'
+
+    def convert_argument_libffi(self, space, w_obj, argchain, call_local):
+        if hasattr(space, "fake"):
+            raise NotImplementedError
+        space.getbuiltinmodule("cpyext")
+        from pypy.module.cpyext.pyobject import make_ref
+        ref = make_ref(space, w_obj)
+        argchain.arg(rffi.cast(rffi.VOIDP, ref))
+
+    def free_argument(self, space, arg, call_local):
+        if hasattr(space, "fake"):
+            raise NotImplementedError
+        from pypy.module.cpyext.pyobject import Py_DecRef, PyObject
+        Py_DecRef(space, rffi.cast(PyObject, rffi.cast(rffi.VOIDPP, arg)[0]))
+
+
 _converters = {}         # builtin and custom types
 _a_converters = {}       # array and ptr versions of above
 def get_converter(space, name, default):
@@ -855,6 +884,9 @@
 _converters["std::basic_string<char>&"]          = StdStringRefConverter
 _converters["string&"]                           = 
_converters["std::basic_string<char>&"]
 
+_converters["PyObject*"]                         = PyObjectConverter
+_converters["_object*"]                          = _converters["PyObject*"]
+
 # it should be possible to generate these:
 _a_converters["short int*"]               = ShortPtrConverter
 _a_converters["short*"]                   = _a_converters["short int*"]
diff --git a/pypy/module/cppyy/executor.py b/pypy/module/cppyy/executor.py
--- a/pypy/module/cppyy/executor.py
+++ b/pypy/module/cppyy/executor.py
@@ -336,6 +336,31 @@
         raise FastCallNotPossible
 
 
+class PyObjectExecutor(PtrTypeExecutor):
+    _immutable_ = True
+
+    def wrap_result(self, space, lresult):
+        space.getbuiltinmodule("cpyext")
+        from pypy.module.cpyext.pyobject import PyObject, from_ref, make_ref, 
Py_DecRef
+        result = rffi.cast(PyObject, lresult)
+        w_obj = from_ref(space, result)
+        if result:
+            Py_DecRef(space, result)
+        return w_obj
+
+    def execute(self, space, cppmethod, cppthis, num_args, args):
+        if hasattr(space, "fake"):
+            raise NotImplementedError
+        lresult = capi.c_call_l(cppmethod, cppthis, num_args, args)
+        return self.wrap_result(space, lresult)
+
+    def execute_libffi(self, space, libffifunc, argchain):
+        if hasattr(space, "fake"):
+            raise NotImplementedError
+        lresult = libffifunc.call(argchain, rffi.LONG)
+        return self.wrap_result(space, lresult)
+
+
 _executors = {}
 def get_executor(space, name):
     # Matching of 'name' to an executor factory goes through up to four levels:
@@ -436,3 +461,6 @@
 # special cases (note: CINT backend requires the simple name 'string')
 _executors["std::basic_string<char>"]        = StdStringExecutor
 _executors["string"]                         = 
_executors["std::basic_string<char>"]
+
+_executors["PyObject*"]           = PyObjectExecutor
+_executors["_object*"]            = _executors["PyObject*"]
diff --git a/pypy/module/cppyy/interp_cppyy.py 
b/pypy/module/cppyy/interp_cppyy.py
--- a/pypy/module/cppyy/interp_cppyy.py
+++ b/pypy/module/cppyy/interp_cppyy.py
@@ -228,7 +228,7 @@
                     conv = self.converters[j]
                     arg_j = lltype.direct_ptradd(rffi.cast(rffi.CCHARP, args), 
j*stride)
                     loc_j = self._address_from_local_buffer(call_local, j)
-                    conv.free_argument(rffi.cast(capi.C_OBJECT, arg_j), loc_j)
+                    conv.free_argument(self.space, rffi.cast(capi.C_OBJECT, 
arg_j), loc_j)
                 capi.c_deallocate_function_args(args)
                 raise
         return args
@@ -241,7 +241,7 @@
             arg_i = lltype.direct_ptradd(rffi.cast(rffi.CCHARP, args), 
i*stride)
             loc_i = self._address_from_local_buffer(call_local, i)
             conv.finalize_call(self.space, args_w[i], loc_i)
-            conv.free_argument(rffi.cast(capi.C_OBJECT, arg_i), loc_i)
+            conv.free_argument(self.space, rffi.cast(capi.C_OBJECT, arg_i), 
loc_i)
         capi.c_deallocate_function_args(args)
 
     def signature(self):
diff --git a/pypy/module/cppyy/test/Makefile b/pypy/module/cppyy/test/Makefile
--- a/pypy/module/cppyy/test/Makefile
+++ b/pypy/module/cppyy/test/Makefile
@@ -1,5 +1,5 @@
 dicts = example01Dict.so datatypesDict.so advancedcppDict.so 
advancedcpp2Dict.so \
-overloadsDict.so stltypesDict.so operatorsDict.so fragileDict.so \
+overloadsDict.so stltypesDict.so operatorsDict.so fragileDict.so 
crossingDict.so \
 std_streamsDict.so
 all : $(dicts)
 
diff --git a/pypy/module/cppyy/test/crossing.cxx 
b/pypy/module/cppyy/test/crossing.cxx
new file mode 100644
--- /dev/null
+++ b/pypy/module/cppyy/test/crossing.cxx
@@ -0,0 +1,16 @@
+#include "crossing.h"
+#include <stdlib.h>
+
+extern "C" long bar_unwrap(PyObject*);
+extern "C" PyObject* bar_wrap(long);
+
+
+long crossing::A::unwrap(PyObject* pyobj)
+{
+    return bar_unwrap(pyobj);
+}
+
+PyObject* crossing::A::wrap(long l)
+{
+    return bar_wrap(l);
+}
diff --git a/pypy/module/cppyy/test/crossing.h 
b/pypy/module/cppyy/test/crossing.h
new file mode 100644
--- /dev/null
+++ b/pypy/module/cppyy/test/crossing.h
@@ -0,0 +1,12 @@
+struct _object;
+typedef _object PyObject;
+
+namespace crossing {
+
+class A {
+public:
+    long unwrap(PyObject* pyobj);
+    PyObject* wrap(long l);
+};
+
+} // namespace crossing
diff --git a/pypy/module/cppyy/test/crossing.xml 
b/pypy/module/cppyy/test/crossing.xml
new file mode 100644
--- /dev/null
+++ b/pypy/module/cppyy/test/crossing.xml
@@ -0,0 +1,7 @@
+<lcgdict>
+
+  <namespace name="crossing" />
+
+  <class pattern="crossing::[A-Z]" />
+
+</lcgdict>
diff --git a/pypy/module/cppyy/test/test_crossing.py 
b/pypy/module/cppyy/test/test_crossing.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cppyy/test/test_crossing.py
@@ -0,0 +1,104 @@
+import py, os, sys
+from pypy.conftest import gettestobjspace
+from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
+
+currpath = py.path.local(__file__).dirpath()
+test_dct = str(currpath.join("crossingDict.so"))
+
+def setup_module(mod):
+    if sys.platform == 'win32':
+        py.test.skip("win32 not supported so far")
+    err = os.system("cd '%s' && make crossingDict.so" % currpath)
+    if err:
+        raise OSError("'make' failed (see stderr)")
+
+
+class AppTestCrossing(AppTestCpythonExtensionBase):
+    def setup_class(cls):
+        # following from AppTestCpythonExtensionBase, with cppyy added
+        cls.space = gettestobjspace(usemodules=['cpyext', 'cppyy', 'thread', 
'_rawffi', '_ffi', 'array'])
+        cls.space.getbuiltinmodule("cpyext")
+        from pypy.module.imp.importing import importhook
+        importhook(cls.space, "os") # warm up reference counts
+        from pypy.module.cpyext.pyobject import RefcountState
+        state = cls.space.fromcache(RefcountState)
+        state.non_heaptypes_w[:] = []
+
+        # cppyy specific additions (not that the test_dct is loaded late
+        # to allow the generated extension module be loaded first)
+        cls.w_test_dct  = cls.space.wrap(test_dct)
+        cls.w_datatypes = cls.space.appexec([], """():
+            import cppyy, cpyext""")
+
+    def setup_method(self, func):
+        AppTestCpythonExtensionBase.setup_method(self, func)
+
+        if hasattr(self, 'cmodule'):
+            return
+
+        import os, ctypes
+
+        init = """
+        if (Py_IsInitialized())
+            Py_InitModule("bar", methods);
+        """
+        body = """
+        long bar_unwrap(PyObject* arg)
+        {
+            return PyLong_AsLong(arg);
+        }
+        PyObject* bar_wrap(long l)
+        {
+            return PyLong_FromLong(l);
+        }
+        static PyMethodDef methods[] = {
+            { NULL }
+        };
+        """
+
+        modname = self.import_module(name='bar', init=init, body=body, 
load_it=False)
+        from pypy.module.imp.importing import get_so_extension
+        soext = get_so_extension(self.space)
+        fullmodname = os.path.join(modname, 'bar' + soext)
+        self.cmodule = ctypes.CDLL(fullmodname, ctypes.RTLD_GLOBAL)
+
+    def test00_base_class(self):
+        """Test from cpyext; only here to see whether the imported class 
works"""
+
+        import sys
+        init = """
+        if (Py_IsInitialized())
+            Py_InitModule("foo", NULL);
+        """
+        self.import_module(name='foo', init=init)
+        assert 'foo' in sys.modules
+
+    def test01_crossing_dict(self):
+        """Test availability of all needed classes in the dict"""
+
+        import cppyy
+        cppyy.load_reflection_info(self.test_dct)
+
+        assert cppyy.gbl.crossing == cppyy.gbl.crossing
+        crossing = cppyy.gbl.crossing
+
+        assert crossing.A == crossing.A
+
+    def test02_send_pyobject(self):
+        """Test sending a true pyobject to C++"""
+
+        import cppyy
+        crossing = cppyy.gbl.crossing
+
+        a = crossing.A()
+        assert a.unwrap(13) == 13
+
+    def test03_send_and_receive_pyobject(self):
+        """Test receiving a true pyobject from C++"""
+
+        import cppyy
+        crossing = cppyy.gbl.crossing
+
+        a = crossing.A()
+
+        assert a.wrap(41) == 41
diff --git a/pypy/module/cppyy/test/test_zjit.py 
b/pypy/module/cppyy/test/test_zjit.py
--- a/pypy/module/cppyy/test/test_zjit.py
+++ b/pypy/module/cppyy/test/test_zjit.py
@@ -7,6 +7,9 @@
 
 from pypy.module.cppyy import interp_cppyy, capi
 
+# load cpyext early, or its global vars are counted as leaks in the test
+# (note that the module is not otherwise used in the test itself)
+import pypy.module.cpyext
 
 currpath = py.path.local(__file__).dirpath()
 test_dct = str(currpath.join("example01Dict.so"))
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to