Author: Wim Lavrijsen <wlavrij...@lbl.gov>
Branch: cppyy-packaging
Changeset: r94860:1214d76af168
Date: 2018-07-13 11:44 -0700
http://bitbucket.org/pypy/pypy/changeset/1214d76af168/

Log:    support low-level views of arrays that allow user-side updates to
        shape

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
@@ -1,15 +1,12 @@
 import sys
 
 from pypy.interpreter.error import OperationError, oefmt
-
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.rlib.rarithmetic import r_singlefloat, r_longfloat
 from rpython.rlib import rfloat, rawrefcount
-
 from pypy.module._rawffi.interp_rawffi import letter2tp
 from pypy.module._rawffi.array import W_ArrayInstance
-
-from pypy.module._cppyy import helper, capi, ffitypes
+from pypy.module._cppyy import helper, capi, ffitypes, lowlevelviews
 
 # Converter objects are used to translate between RPython and C++. They are
 # defined by the type name for which they provide conversion. Uses are for
@@ -149,7 +146,8 @@
         # read access, so no copy needed
         address_value = self._get_raw_address(space, w_obj, offset)
         address = rffi.cast(rffi.ULONG, address_value)
-        return W_ArrayInstance(space, letter2tp(space, self.typecode), 
self.size, address)
+        return lowlevelviews.W_LowLevelView(
+            space, letter2tp(space, self.typecode), self.size, address)
 
     def to_memory(self, space, w_obj, w_value, offset):
         # copy the full array (uses byte copy for now)
@@ -190,7 +188,8 @@
         # read access, so no copy needed
         address_value = self._get_raw_address(space, w_obj, offset)
         address = rffi.cast(rffi.ULONGP, address_value)
-        return W_ArrayInstance(space, letter2tp(space, self.typecode), 
self.size, address[0])
+        return lowlevelviews.W_LowLevelView(
+            space, letter2tp(space, self.typecode), self.size, address[0])
 
     def to_memory(self, space, w_obj, w_value, offset):
         # copy only the pointer value
@@ -438,7 +437,7 @@
             from pypy.module._cppyy import interp_cppyy
             return interp_cppyy.get_nullptr(space)
         shape = letter2tp(space, 'P')
-        return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval)
+        return lowlevelviews.W_LowLevelView(space, shape, 
sys.maxint/shape.size, ptrval)
 
     def to_memory(self, space, w_obj, w_value, offset):
         address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, 
offset))
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
@@ -1,14 +1,10 @@
 import sys
 
 from pypy.interpreter.error import oefmt
-
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.rlib import jit_libffi
-
 from pypy.module._rawffi.interp_rawffi import letter2tp
-from pypy.module._rawffi.array import W_Array, W_ArrayInstance
-
-from pypy.module._cppyy import helper, capi, ffitypes
+from pypy.module._cppyy import helper, capi, ffitypes, lowlevelviews
 
 # Executor objects are used to dispatch C++ methods. They are defined by their
 # return type only: arguments are converted by Converter objects, and Executors
@@ -60,7 +56,7 @@
             from pypy.module._cppyy import interp_cppyy
             return interp_cppyy.get_nullptr(space)
         shape = letter2tp(space, self.typecode)
-        return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval)
+        return lowlevelviews.W_LowLevelView(space, shape, 
sys.maxint/shape.size, ptrval)
 
 
 class VoidExecutor(Executor):
diff --git a/pypy/module/_cppyy/lowlevelviews.py 
b/pypy/module/_cppyy/lowlevelviews.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cppyy/lowlevelviews.py
@@ -0,0 +1,54 @@
+# Naked C++ pointers carry no useful size or layout information, but often
+# such information is externnally available. Low level views are arrays with
+# a few more methods allowing such information to be set. Afterwards, it is
+# simple to pass these views on to e.g. numpy (w/o the need to copy).
+
+from pypy.interpreter.error import oefmt
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.typedef import TypeDef, GetSetProperty, 
interp_attrproperty_w
+from pypy.module._rawffi.array import W_ArrayInstance
+
+
+class W_LowLevelView(W_ArrayInstance):
+    def __init__(self, space, shape, length, address):
+        assert address   # if not address, base class will allocate memory
+        W_ArrayInstance.__init__(self, space, shape, length, address)
+
+    @unwrap_spec(args_w='args_w')
+    def reshape(self, space, args_w):
+        # llviews are only created from non-zero addresses, so we only need
+        # to adjust length and shape
+
+        nargs = len(args_w)
+        if nargs == 0:
+            raise oefmt(space.w_TypeError, "reshape expects a tuple argument")
+
+        newshape_w = args_w
+        if nargs != 1 or not space.isinstance_w(args_w[0], space.w_tuple) or \
+               not space.len_w(args_w[0]) == 1:
+            raise oefmt(space.w_TypeError,
+                "tuple object of length 1 expected, received %T", args_w[0])
+
+        w_shape = args_w[0]
+
+        # shape in W_ArrayInstance-speak is somewhat different from what
+        # e.g. numpy thinks of it: self.shape contains the info (itemcode,
+        # size, etc.) of a single entry; length is user-facing shape
+        self.length = space.int_w(space.getitem(w_shape, space.newint(0)))
+
+
+W_LowLevelView.typedef = TypeDef(
+    'LowLevelView',
+    __repr__    = interp2app(W_LowLevelView.descr_repr),
+    __setitem__ = interp2app(W_LowLevelView.descr_setitem),
+    __getitem__ = interp2app(W_LowLevelView.descr_getitem),
+    __len__     = interp2app(W_LowLevelView.getlength),
+    buffer      = GetSetProperty(W_LowLevelView.getbuffer),
+    shape       = interp_attrproperty_w('shape', W_LowLevelView),
+    free        = interp2app(W_LowLevelView.free),
+    byptr       = interp2app(W_LowLevelView.byptr),
+    itemaddress = interp2app(W_LowLevelView.descr_itemaddress),
+    reshape     = interp2app(W_LowLevelView.reshape),
+)
+W_ArrayInstance.typedef.acceptable_as_base_class = False
+
diff --git a/pypy/module/_cppyy/test/test_datatypes.py 
b/pypy/module/_cppyy/test/test_datatypes.py
--- a/pypy/module/_cppyy/test/test_datatypes.py
+++ b/pypy/module/_cppyy/test/test_datatypes.py
@@ -181,7 +181,7 @@
         names = ['uchar', 'short', 'ushort', 'int', 'uint', 'long', 'ulong']
         import array
         a = range(self.N)
-        atypes = ['B', 'h', 'H', 'i', 'I', 'l', 'L' ]
+        atypes = ['B', 'h', 'H', 'i', 'I', 'l', 'L']
         for j in range(len(names)):
             b = array.array(atypes[j], a)
             setattr(c, 'm_'+names[j]+'_array', b)     # buffer copies
@@ -189,6 +189,7 @@
                 assert eval('c.m_%s_array[i]' % names[j]) == b[i]
 
             setattr(c, 'm_'+names[j]+'_array2', b)    # pointer copies
+            assert 3 < self.N
             b[3] = 28
             for i in range(self.N):
                 assert eval('c.m_%s_array2[i]' % names[j]) == b[i]
@@ -210,7 +211,7 @@
 
         a = range(self.N)
         # test arrays in mixed order, to give overload resolution a workout
-        for t in ['d', 'i', 'f', 'H', 'I', 'h', 'L', 'l' ]:
+        for t in ['d', 'i', 'f', 'H', 'I', 'h', 'L', 'l']:
             b = array.array(t, a)
 
             # typed passing
@@ -689,9 +690,6 @@
                      'get_long_array',   'get_long_array2',
                      'get_ulong_array',  'get_ulong_array2']:
             arr = getattr(c, func)()
-            arr = arr.shape.fromaddress(arr.itemaddress(0), self.N)
-
-            """TODO: interface change ...
             arr.reshape((self.N,))
             assert len(arr) == self.N
 
@@ -703,7 +701,7 @@
 
             l = list(arr)
             for i in range(self.N):
-                assert arr[i] == l[i]"""
+                assert arr[i] == l[i]
 
     def test20_voidp(self):
         """Test usage of void* data"""
@@ -756,6 +754,15 @@
     def test21_function_pointers(self):
         """Function pointer passing"""
 
+        import os
+
+        # TODO: currently crashes if fast path disabled
+        try:
+            if os.environ['CPPYY_DISABLE_FASTPATH']:
+                return
+        except KeyError:
+            pass
+
         import _cppyy as cppyy
 
         f1 = cppyy.gbl.sum_of_int
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to