Author: Ronan Lamy <ronan.l...@gmail.com> Branch: py3k Changeset: r86782:d9e0a4feb77e Date: 2016-08-31 16:12 +0100 http://bitbucket.org/pypy/pypy/changeset/d9e0a4feb77e/
Log: Backport memoryview changes from 3.5 diff --git a/pypy/module/array/interp_array.py b/pypy/module/array/interp_array.py --- a/pypy/module/array/interp_array.py +++ b/pypy/module/array/interp_array.py @@ -636,6 +636,18 @@ def getlength(self): return self.array.len * self.array.itemsize + def getformat(self): + return self.array.typecode + + def getitemsize(self): + return self.array.itemsize + + def getndim(self): + return 1 + + def getstrides(self): + return [self.getitemsize()] + def getitem(self, index): array = self.array data = array._charbuf_start() diff --git a/pypy/objspace/std/memoryobject.py b/pypy/objspace/std/memoryobject.py --- a/pypy/objspace/std/memoryobject.py +++ b/pypy/objspace/std/memoryobject.py @@ -10,27 +10,73 @@ from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.gateway import interp2app from pypy.interpreter.typedef import TypeDef, GetSetProperty, make_weakref_descr +from pypy.module.struct.formatiterator import UnpackFormatIterator, PackFormatIterator from pypy.objspace.std.bytesobject import getbytevalue -from pypy.module.struct.formatiterator import UnpackFormatIterator, PackFormatIterator +from rpython.rlib.unroll import unrolling_iterable + +MEMORYVIEW_MAX_DIM = 64 +MEMORYVIEW_SCALAR = 0x0001 +MEMORYVIEW_C = 0x0002 +MEMORYVIEW_FORTRAN = 0x0004 +MEMORYVIEW_SCALAR = 0x0008 +MEMORYVIEW_PIL = 0x0010 class W_MemoryView(W_Root): """Implement the built-in 'memoryview' type as a wrapper around an interp-level buffer. """ - _immutable_fields_ = ['format', 'itemsize'] - def __init__(self, buf, format='B', itemsize=1): + def __init__(self, buf, format=None, itemsize=1, ndim=-1, + shape=None, strides=None, suboffsets=None): assert isinstance(buf, Buffer) self.buf = buf self._hash = -1 + # private copies of format, shape, itemsize, ... on this class self.format = format self.itemsize = itemsize + self.shape = shape + self.strides = strides + self.suboffsets = suboffsets + self.ndim = ndim + self.flags = 0 + self.length = -1 + self._init_flags() + + # several fields are "overwritten" by the memory view (shape, strides, ...) + # thus use only those getter fields instead of directly accessing the fields + def getndim(self): + if self.ndim == -1: + return self.buf.getndim() + return self.ndim + + def getshape(self): + if self.shape is None: + return self.buf.getshape() + return self.shape + + def getstrides(self): + if self.strides is None: + return self.buf.getstrides() + return self.strides + + def getitemsize(self): + return self.itemsize + + # memoryview needs to modify the field 'format', to prevent the modification + # of the buffer, we save the new format here! + def getformat(self): + if self.format is None: + return self.buf.getformat() + return self.format + + def setformat(self, value): + self.format = value def buffer_w_ex(self, space, flags): self._check_released(space) space.check_buf_flags(flags, self.buf.readonly) - return self.buf, self.format, self.itemsize + return self.buf, self.getformat(), self.itemsize @staticmethod def descr_new_memoryview(space, w_subtype, w_object): @@ -63,10 +109,45 @@ descr_ne = _make_descr__cmp('ne') def as_str(self): + return ''.join(self.copy_buffer()) + + def copy_buffer(self): buf = self.buf - return buf.as_str() + n_bytes = buf.getlength() + data = [] + self._copy_rec(0, data, 0) + return data + + def _copy_rec(self, idim, data, off): + shapes = self.getshape() + shape = shapes[idim] + strides = self.getstrides() + + if self.getndim()-1 == idim: + self._copy_base(data,off) + return + + # TODO add a test that has at least 2 dims + for i in range(shape): + self._copy_rec(idim+1,data,off) + off += strides[idim] + + def _copy_base(self, data, off): + shapes = self.getshape() + step = shapes[0] + strides = self.getstrides() + itemsize = self.getitemsize() + for i in range(step): + bytes = self.buf.getslice(off, off+itemsize, 1, itemsize) + data.append(bytes) + off += strides[0] + # do notcopy data if the sub buffer is out of bounds + if off >= self.buf.getlength(): + break def getlength(self): + if self.length != -1: + return self.length // self.itemsize return self.buf.getlength() // self.itemsize def descr_tobytes(self, space): @@ -75,43 +156,184 @@ def descr_tolist(self, space): self._check_released(space) + + buf = self.buf + dim = self.getndim() + fmt = self.getformat() + if dim == 0: + raise NotImplementedError + elif dim == 1: + itemsize = self.getitemsize() + return self._tolist(space, buf, buf.getlength() // itemsize, fmt) + else: + return self._tolist_rec(space, buf, 0, 0, fmt) + + def _tolist(self, space, buf, count, fmt): # TODO: this probably isn't very fast - fmtiter = UnpackFormatIterator(space, self.buf) - fmtiter.interpret(self.format * self.getlength()) + fmtiter = UnpackFormatIterator(space, buf) + fmtiter.interpret(fmt * count) return space.newlist(fmtiter.result_w) + def _tolist_rec(self, space, buf, start, idim, fmt): + strides = self.getstrides() + shape = self.getshape() + # + dim = idim+1 + stride = strides[idim] + itemsize = self.getitemsize() + dimshape = shape[idim] + # + if dim >= self.getndim(): + bytecount = (stride * dimshape) + count = bytecount // itemsize + return self._tolist(space, buf, count, fmt) + items = [None] * dimshape + + for i in range(dimshape): + item = self._tolist_rec(space, SubBuffer(buf, start, stride), start, idim+1, fmt) + items[i] = item + start += stride + + return space.newlist(items) + + + def _start_from_tuple(self, space, w_tuple): + from pypy.objspace.std.tupleobject import W_TupleObject + start = 0 + + view = self.buf + length = space.len_w(w_tuple) + dim = view.getndim() + dim = 0 + assert isinstance(w_tuple, W_TupleObject) + while dim < length: + w_obj = w_tuple.getitem(space, dim) + index = w_obj.int_w(space) + start = self.lookup_dimension(space, start, dim, index) + dim += 1 + return start + + def lookup_dimension(self, space, start, dim, index): + view = self.buf + shape = view.getshape() + strides = view.getstrides() + nitems = shape[dim] + if index < 0: + index += nitems + if index < 0 or index >= nitems: + raise oefmt(space.w_IndexError, + "index out of bounds on dimension %d", dim+1) + start += strides[dim] * index + # TODO suboffsets? + return start + + def _getitem_tuple_indexed(self, space, w_index): + view = self.buf + + fmt = view.getformat() # TODO adjust format? + + length = space.len_w(w_index) + ndim = view.getndim() + if length < ndim: + raise OperationError(space.w_NotImplementedError, \ + space.wrap("sub-views are not implemented")) + + if length > ndim: + raise oefmt(space.w_TypeError, \ + "cannot index %d-dimension view with %d-element tuple", + length, ndim) + + start = self._start_from_tuple(space, w_index) + + buf = SubBuffer(self.buf, start, view.getitemsize()) + fmtiter = UnpackFormatIterator(space, buf) + fmtiter.interpret(fmt) + return fmtiter.result_w[0] + + def descr_getitem(self, space, w_index): self._check_released(space) + + if space.isinstance_w(w_index, space.w_tuple): + return self._getitem_tuple_indexed(space, w_index) + start, stop, step, size = space.decode_index4(w_index, self.getlength()) # ^^^ for a non-slice index, this returns (index, 0, 0, 1) - itemsize = self.itemsize + itemsize = self.getitemsize() + start, stop, size = self._apply_itemsize(space, start, size, itemsize) if step == 0: # index only if itemsize == 1: ch = self.buf.getitem(start) return space.newint(ord(ch)) else: # TODO: this probably isn't very fast - buf = SubBuffer(self.buf, start * itemsize, itemsize) + buf = SubBuffer(self.buf, start, itemsize) fmtiter = UnpackFormatIterator(space, buf) fmtiter.interpret(self.format) return fmtiter.result_w[0] elif step == 1: - buf = SubBuffer(self.buf, start * itemsize, size * itemsize) - return W_MemoryView(buf, self.format, itemsize) + mv = W_MemoryView.copy(self) + mv.slice(start, stop, step, size) + mv._init_flags() + return mv else: - # XXX needs to return a W_MemoryView with a NonContiguousSubBuffer - # maybe? Need to check the cpyext requirements for that - raise oefmt(space.w_NotImplementedError, - "XXX extended slicing") + mv = W_MemoryView.copy(self) + mv.slice(start, stop, step, size) + mv.length = mv.bytecount_from_shape() + mv._init_flags() + return mv + + def slice(self, start, stop, step, size): + # modifies the buffer, shape and stride to allow step to be > 1 + # NOTE that start, stop & size are already byte offsets/count + # TODO subbuffer + strides = self.getstrides()[:] + shape = self.getshape()[:] + itemsize = self.getitemsize() + dim = 0 + self.buf = SubBuffer(self.buf, strides[dim] * (start//itemsize), size*step) + shape[dim] = size + strides[dim] = strides[dim] * step + self.strides = strides + self.shape = shape + + def bytecount_from_shape(self): + dim = self.getndim() + shape = self.getshape() + length = 1 + for i in range(dim): + length *= shape[i] + return length * self.getitemsize() + + @staticmethod + def copy(view, buf=None): + # TODO suboffsets + if buf == None: + buf = view.buf + return W_MemoryView(buf, view.getformat(), view.getitemsize(), + view.getndim(), view.getshape()[:], view.getstrides()[:]) + + def _apply_itemsize(self, space, start, size, itemsize): + if itemsize > 1: + start *= itemsize + size *= itemsize + + stop = start + size + # start & stop are now byte offset, thus use self.buf.getlength() + if stop > self.buf.getlength(): + raise oefmt(space.w_IndexError, 'index out of range') + + return start, stop, size def descr_setitem(self, space, w_index, w_obj): self._check_released(space) if self.buf.readonly: raise oefmt(space.w_TypeError, "cannot modify read-only memory") if space.isinstance_w(w_index, space.w_tuple): - raise oefmt(space.w_NotImplementedError, "XXX tuple setitem") + raise oefmt(space.w_NotImplementedError, "") start, stop, step, size = space.decode_index4(w_index, self.getlength()) - itemsize = self.itemsize + itemsize = self.getitemsize() + start, stop, size = self._apply_itemsize(space, start, size, itemsize) if step == 0: # index only if itemsize == 1: ch = getbytevalue(space, w_obj) @@ -125,16 +347,41 @@ raise oefmt(space.w_TypeError, "memoryview: invalid type for format '%s'", self.format) - self.buf.setslice(start * itemsize, fmtiter.result.build()) + self.buf.setslice(start, fmtiter.result.build()) elif step == 1: value = space.buffer_w(w_obj, space.BUF_CONTIG_RO) - if value.getlength() != size * self.itemsize: + if value.getlength() != size: raise oefmt(space.w_ValueError, "cannot modify size of memoryview object") - self.buf.setslice(start * itemsize, value.as_str()) + self.buf.setslice(start, value.as_str()) else: - raise oefmt(space.w_NotImplementedError, - "XXX extended slicing") + if self.getndim() != 1: + raise oefmt(space.w_NotImplementedError, + "memoryview slice assignments are currently " + "restricted to ndim = 1") + # this is the case of a one dimensional copy! + # NOTE we could maybe make use of copy_base, but currently we do not + itemsize = self.getitemsize() + data = [] + src = space.buffer_w(w_obj, space.BUF_CONTIG_RO) + dst_strides = self.getstrides() + dim = 0 + dst = SubBuffer(self.buf, start, size) + src_stride0 = dst_strides[dim] + + off = 0 + src_shape0 = size // itemsize + src_stride0 = src.getstrides()[0] + if isinstance(w_obj, W_MemoryView): + src_stride0 = w_obj.getstrides()[0] + for i in range(src_shape0): + data.append(src.getslice(off,off+itemsize,1,itemsize)) + off += src_stride0 + off = 0 + dst_stride0 = self.getstrides()[0] * step + for dataslice in data: + dst.setslice(off, dataslice) + off += dst_stride0 def descr_len(self, space): self._check_released(space) @@ -142,11 +389,11 @@ def w_get_format(self, space): self._check_released(space) - return space.wrap(self.format) + return space.wrap(self.getformat()) def w_get_itemsize(self, space): self._check_released(space) - return space.newint(self.itemsize) + return space.wrap(self.itemsize) def w_get_ndim(self, space): self._check_released(space) @@ -158,11 +405,11 @@ def w_get_shape(self, space): self._check_released(space) - return space.newtuple([space.newint(self.getlength())]) + return space.newtuple([space.wrap(x) for x in self.getshape()]) def w_get_strides(self, space): self._check_released(space) - return space.newtuple([space.newint(self.itemsize)]) + return space.newtuple([space.wrap(x) for x in self.getstrides()]) def w_get_suboffsets(self, space): self._check_released(space) @@ -240,14 +487,178 @@ size = rffi.sizeof(rffi.VOIDP) return size + def _zero_in_shape(self): + # this method could be moved to the class Buffer + buf = self.buf + shape = buf.getshape() + for i in range(buf.getndim()): + if shape[i] == 0: + return True + return False + def descr_cast(self, space, w_format, w_shape=None): self._check_released(space) - if not space.is_none(w_shape): - raise oefmt(space.w_NotImplementedError, - "XXX cast() with a shape") + + if not space.isinstance_w(w_format, space.w_unicode): + raise OperationError(space.w_TypeError, \ + space.wrap("memoryview: format argument must be a string")) + fmt = space.str_w(w_format) - newitemsize = self.get_native_fmtchar(fmt) - return W_MemoryView(self.buf, fmt, newitemsize) + buf = self.buf + ndim = 1 + + if not memory_view_c_contiguous(space, self.flags): + raise OperationError(space.w_TypeError, \ + space.wrap("memoryview: casts are restricted" \ + " to C-contiguous views")) + + if (w_shape or buf.getndim() != 1) and self._zero_in_shape(): + raise OperationError(space.w_TypeError, \ + space.wrap("memoryview: cannot casts view with" \ + " zeros in shape or strides")) + + itemsize = self.get_native_fmtchar(fmt) + if w_shape: + if not (space.isinstance_w(w_shape, space.w_list) or space.isinstance_w(w_shape, space.w_tuple)): + raise oefmt(space.w_TypeError, "expected list or tuple got %T", w_shape) + ndim = space.len_w(w_shape) + if ndim > MEMORYVIEW_MAX_DIM: + raise oefmt(space.w_ValueError, \ + "memoryview: number of dimensions must not exceed %d", + ndim) + # yes access ndim as field + if self.ndim > 1 and buf.getndim() != 1: + raise OperationError(space.w_TypeError, \ + space.wrap("memoryview: cast must be 1D -> ND or ND -> 1D")) + + mv = W_MemoryView(buf, self.format, self.itemsize) + origfmt = mv.getformat() + mv._cast_to_1D(space, origfmt, fmt, itemsize) + if w_shape: + fview = space.fixedview(w_shape) + shape = [space.int_w(w_obj) for w_obj in fview] + mv._cast_to_ND(space, shape, ndim) + return mv + + def _init_flags(self): + buf = self.buf + ndim = buf.getndim() + flags = 0 + if ndim == 0: + flags |= MEMORYVIEW_SCALAR | MEMORYVIEW_C | MEMORYVIEW_FORTRAN + if ndim == 1: + shape = buf.getshape() + strides = buf.getstrides() + if len(shape) > 0 and shape[0] == 1 and \ + len(strides) > 0 and strides[0] == buf.getitemsize(): + flags |= MEMORYVIEW_C | MEMORYVIEW_SCALAR + # TODO for now? + flags |= MEMORYVIEW_C + # TODO if buf.is_contiguous('C'): + # TODO flags |= MEMORYVIEW_C + # TODO elif buf.is_contiguous('F'): + # TODO flags |= MEMORYVIEW_FORTRAN + + # TODO missing suboffsets + + self.flags = flags + + def _cast_to_1D(self, space, origfmt, fmt, itemsize): + buf = self.buf + if itemsize < 0: + raise oefmt(space.w_ValueError, "memoryview: destination" \ + " format must be a native single character format prefixed" \ + " with an optional '@'") + + if self.get_native_fmtchar(origfmt) < 0 or \ + (not is_byte_format(fmt) and not is_byte_format(origfmt)): + raise oefmt(space.w_TypeError, + "memoryview: cannot cast between" \ + " two non-byte formats") + + if buf.getlength() % itemsize != 0: + raise oefmt(space.w_TypeError, + "memoryview: length is not a multiple of itemsize") + + newfmt = self.get_native_fmtstr(fmt) + if not newfmt: + raise oefmt(space.w_RuntimeError, + "memoryview: internal error") + self.format = newfmt + self.itemsize = itemsize + self.ndim = 1 + self.shape = [buf.getlength() // itemsize] + self.strides = [itemsize] + # XX suboffsets + + self._init_flags() + + def get_native_fmtstr(self, fmt): + lenfmt = len(fmt) + nat = False + if lenfmt == 0: + return None + elif lenfmt == 1: + format = fmt[0] # fine! + elif lenfmt == 2: + if fmt[0] == '@': + nat = True + format = fmt[1] + else: + return None + else: + return None + + chars = ['c','b','B','h','H','i','I','l','L','q', + 'Q','n','N','f','d','?','P'] + for c in unrolling_iterable(chars): + if c == format: + if nat: return '@'+c + else: return c + + return None + + def _cast_to_ND(self, space, shape, ndim): + buf = self.buf + + self.ndim = ndim + length = self.itemsize + if ndim == 0: + self.shape = [] + self.strides = [] + else: + self.shape = shape + for i in range(ndim): + length *= shape[i] + self._init_strides_from_shape() + + if length != self.buf.getlength(): + raise OperationError(space.w_TypeError, + space.wrap("memoryview: product(shape) * itemsize != buffer size")) + + self._init_flags() + + def _init_strides_from_shape(self): + shape = self.getshape() + s = [0] * len(shape) + self.strides = s + ndim = self.getndim() + s[ndim-1] = self.itemsize + i = ndim-2 + while i >= 0: + s[i] = s[i+1] * shape[i+1] + i -= 1 + + def descr_hex(self, space): + from pypy.objspace.std.bytearrayobject import _array_to_hexstring + self._check_released(space) + return _array_to_hexstring(space, self.buf) + +def is_byte_format(char): + return char == 'b' or char == 'B' or char == 'c' + +def memory_view_c_contiguous(space, flags): + return flags & (space.BUF_CONTIG_RO|MEMORYVIEW_C) != 0 W_MemoryView.typedef = TypeDef( "memoryview", @@ -266,6 +677,7 @@ __exit__ = interp2app(W_MemoryView.descr_exit), __weakref__ = make_weakref_descr(W_MemoryView), cast = interp2app(W_MemoryView.descr_cast), + #hex = interp2app(W_MemoryView.descr_hex), # 3.5 tobytes = interp2app(W_MemoryView.descr_tobytes), tolist = interp2app(W_MemoryView.descr_tolist), release = interp2app(W_MemoryView.descr_release), diff --git a/pypy/objspace/std/test/test_memoryobject.py b/pypy/objspace/std/test/test_memoryobject.py --- a/pypy/objspace/std/test/test_memoryobject.py +++ b/pypy/objspace/std/test/test_memoryobject.py @@ -1,3 +1,11 @@ +import py +import struct +from pypy.interpreter.baseobjspace import W_Root +from pypy.interpreter.gateway import interp2app +from pypy.interpreter.typedef import TypeDef +from rpython.rlib.buffer import Buffer +from pypy.conftest import option + class AppTestMemoryView: spaceconfig = dict(usemodules=['array']) @@ -16,6 +24,7 @@ w = v[1:234] assert isinstance(w, memoryview) assert len(w) == 2 + exc = raises(TypeError, "memoryview('foobar')") def test_rw(self): data = bytearray(b'abcefg') @@ -28,7 +37,7 @@ v[0:3] = v[2:5] assert data == bytearray(eval("b'23f3fg'")) exc = raises(ValueError, "v[2:3] = b'spam'") - assert str(exc.value) == "cannot modify size of memoryview object" + #assert str(exc.value) == "cannot modify size of memoryview object" def test_extended_slice(self): data = bytearray(b'abcefg') @@ -37,7 +46,7 @@ assert len(w) == 1 assert list(w) == [97] v[::2] = b'ABC' - assert data == bytearray(b'AbBeCg') + assert data == bytearray(eval("b'AbBeCg'")) def test_memoryview_attrs(self): v = memoryview(b"a"*100) @@ -213,4 +222,159 @@ data = bytearray(b'abcdefghij') m3 = memoryview(data).cast('h') m3[1:5:2] = memoryview(b"xyXY").cast('h') - assert data == bytearray(b'abxyefXYij') + assert data == bytearray(eval("b'abxyefXYij'")) + +class MockBuffer(Buffer): + def __init__(self, space, w_arr, w_dim, w_fmt, \ + w_itemsize, w_strides, w_shape): + self.space = space + self.w_arr = w_arr + self.arr = [] + self.ndim = space.int_w(w_dim) + self.format = space.str_w(w_fmt) + self.itemsize = space.int_w(w_itemsize) + self.strides = [] + for w_i in w_strides.getitems_unroll(): + self.strides.append(space.int_w(w_i)) + self.shape = [] + for w_i in w_shape.getitems_unroll(): + self.shape.append(space.int_w(w_i)) + self.readonly = True + self.shape.append(space.len_w(w_arr)) + self.data = [] + itemsize = 1 + worklist = [(1,w_arr)] + while worklist: + dim, w_work = worklist.pop() + if space.isinstance_w(w_work, space.w_list): + for j, w_obj in enumerate(w_work.getitems_unroll()): + worklist.insert(0, (dim+1, w_obj)) + continue + byte = struct.pack(self.format, space.int_w(w_work)) + for c in byte: + self.data.append(c) + self.data = ''.join(self.data) + + def getformat(self): + return self.format + + def getitem(self, index): + return self.data[index:index+1] + + def getlength(self): + return len(self.data) + + def getitemsize(self): + return self.itemsize + + def getndim(self): + return self.ndim + + def getstrides(self): + return self.strides + + def getshape(self): + return self.shape + + def is_contiguous(self, format): + return format == 'C' + +class W_MockArray(W_Root): + def __init__(self, w_list, w_dim, w_fmt, w_size, w_strides, w_shape): + self.w_list = w_list + self.w_dim = w_dim + self.w_fmt = w_fmt + self.w_size = w_size + self.w_strides = w_strides + self.w_shape = w_shape + + @staticmethod + def descr_new(space, w_type, w_list, w_dim, w_fmt, \ + w_size, w_strides, w_shape): + return W_MockArray(w_list, w_dim, w_fmt, w_size, w_strides, w_shape) + + def buffer_w(self, space, flags): + return MockBuffer(space, self.w_list, self.w_dim, self.w_fmt, \ + self.w_size, self.w_strides, self.w_shape) + + def buffer_w_ex(self, space, flags): + return self.buffer_w(space, flags), space.str_w(self.w_fmt), space.int_w(self.w_size) + +W_MockArray.typedef = TypeDef("MockArray", + __new__ = interp2app(W_MockArray.descr_new), +) + +class AppTestMemoryViewMockBuffer(object): + spaceconfig = dict(usemodules=[]) + def setup_class(cls): + if option.runappdirect: + py.test.skip("Impossible to run on appdirect") + cls.w_MockArray = cls.space.gettypefor(W_MockArray) + + def test_tuple_indexing(self): + content = self.MockArray([[0,1,2,3], [4,5,6,7], [8,9,10,11]], + dim=2, fmt='B', size=1, + strides=[4,1], shape=[3,4]) + view = memoryview(content) + assert view[0,0] == 0 + assert view[2,0] == 8 + assert view[2,3] == 11 + assert view[-1,-1] == 11 + assert view[-3,-4] == 0 + + raises(IndexError, "view.__getitem__((2**63-1,0))") + raises(TypeError, "view.__getitem__((0, 0, 0))") + + def test_tuple_indexing_int(self): + content = self.MockArray([ [[1],[2],[3]], [[4],[5],[6]] ], + dim=3, fmt='i', size=4, + strides=[12,4,4], shape=[2,3,1]) + view = memoryview(content) + assert view[0,0,0] == 1 + assert view[-1,2,0] == 6 + + def test_cast_non_byte(self): + empty = self.MockArray([], dim=1, fmt='i', size=4, strides=[1], shape=[1]) + view = memoryview(empty) + raises(TypeError, "view.cast('l')") + try: + view.cast('l') + assert False, "i -> l not possible. buffer must be byte format" + except TypeError: + pass + + def test_cast_empty(self): + empty = self.MockArray([], dim=1, fmt='b', size=1, strides=[1], shape=[1]) + view = memoryview(empty) + cview = view.cast('i') + assert cview.tobytes() == b'' + assert cview.tolist() == [] + assert view.format == 'b' + assert cview.format == 'i' + # + assert cview.cast('b').cast('q').cast('b').tolist() == [] + # + assert cview.format == 'i' + raises(TypeError, "cview.cast('i')") + + def test_cast_with_shape(self): + empty = self.MockArray([1,0,2,0,3,0], + dim=1, fmt='h', size=2, + strides=[8], shape=[6]) + view = memoryview(empty) + byteview = view.cast('b') + assert byteview.tolist() == [1,0,0,0,2,0,0,0,3,0,0,0] + i32view = byteview.cast('i', shape=[1,3]) + assert i32view.format == 'i' + assert i32view.itemsize == 4 + assert i32view.tolist() == [[1,2,3]] + i32view = byteview.cast('i', shape=(1,3)) + assert i32view.tolist() == [[1,2,3]] + + def test_cast_bytes(self): + bytes = b"\x02\x00\x03\x00\x04\x00" \ + b"\x05\x00\x06\x00\x07\x00" + view = memoryview(bytes) + v = view.cast('h', shape=(3,2)) + assert v.tolist() == [[2,3],[4,5],[6,7]] + raises(TypeError, "view.cast('h', shape=(3,3))") _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit