Author: Wim Lavrijsen <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit