import array import numpy as np from numba import jit from numba.tests.support import TestCase, compile_function, MemoryLeakMixin import unittest @jit(nopython=True) def len_usecase(buf): return len(buf) @jit(nopython=True) def getitem_usecase(buf, i): return buf[i] @jit(nopython=True) def getslice_usecase(buf, i, j): s = buf[i:j] return s[0] + 2 * s[-1] @jit(nopython=True) def setitem_usecase(buf, i, v): buf[i] = v @jit(nopython=True) def iter_usecase(buf): res = 0.0 for i, x in enumerate(buf): res += x res *= i + 1 return res def attrgetter(attr): code = """def func(x): return x.%(attr)s """ % locals() pyfunc = compile_function("func", code, globals()) return jit(nopython=True)(pyfunc) contiguous_usecase = attrgetter("contiguous") c_contiguous_usecase = attrgetter("c_contiguous") f_contiguous_usecase = attrgetter("f_contiguous") itemsize_usecase = attrgetter("itemsize") nbytes_usecase = attrgetter("nbytes") ndim_usecase = attrgetter("ndim") readonly_usecase = attrgetter("readonly") shape_usecase = attrgetter("shape") strides_usecase = attrgetter("strides") class TestBufferProtocol(MemoryLeakMixin, TestCase): """ Test operations on buffer-providing objects. """ def _arrays(self): n = 10 for letter, offset in [ ('b', -3), ('B', 0), ('h', -5000), ('H', 40000), ('i', -100000), ('I', 1000000), ('l', -100000), ('L', 1000000), ('q', -2**60), ('Q', 2**63 + 1), ('f', 1.5), ('d', -1.5), ]: yield array.array(letter, [i + offset for i in range(n)]) def _memoryviews(self): n = 10 yield memoryview(bytearray(b"abcdefghi")) yield memoryview(b"abcdefghi") # Different item types for dtype, start, stop in [ ('int8', -10, 10), ('uint8', 0, 10), ('int16', -5000, 1000), ('uint16', 40000, 50000), ('int32', -100000, 100000), ('uint32', 0, 1000000), ('int64', -2**60, 10), ('uint64', 0, 2**64 - 10), ('float32', 1.5, 3.5), ('float64', 1.5, 3.5), ('complex64', -8j, 12 + 5j), ('complex128', -8j, 12 + 5j), ]: yield memoryview(np.linspace(start, stop, n).astype(dtype)) # Different layouts arr = np.arange(12).reshape((3, 4)) assert arr.flags.c_contiguous and not arr.flags.f_contiguous yield memoryview(arr) arr = arr.T assert arr.flags.f_contiguous and not arr.flags.c_contiguous yield memoryview(arr) arr = arr[::2] assert not arr.flags.f_contiguous and not arr.flags.c_contiguous yield memoryview(arr) def _readonlies(self): yield b"xyz" yield memoryview(b"abcdefghi") arr = np.arange(5) arr.setflags(write=False) yield memoryview(arr) def _check_unary(self, jitfunc, *args): pyfunc = jitfunc.py_func self.assertPreciseEqual(jitfunc(*args), pyfunc(*args)) def check_len(self, obj): self._check_unary(len_usecase, obj) def check_iter(self, obj): self._check_unary(iter_usecase, obj) def check_getitem(self, obj): # Be careful to index all dimensions, since we don't support # partial indexing yet. def yield_indices(obj): try: shape = obj.shape except AttributeError: shape = len(obj), for tup in np.ndindex(shape): # Simple 1d buffer-providing objects usually don't support # tuple indexing. if len(tup) == 1: yield tup[0] else: yield tup for i in yield_indices(obj): try: expected = obj[i] except (NotImplementedError, TypeError): if isinstance(obj, memoryview): # The memoryview object doesn't support all codes yet, # fall back on the underlying object. expected = obj.obj[i] else: raise self.assertPreciseEqual(getitem_usecase(obj, i), expected) def check_setitem(self, obj): for i in range(len(obj)): orig = list(obj) val = obj[i] // 2 + 1 setitem_usecase(obj, i, val) self.assertEqual(obj[i], val) for j, val in enumerate(orig): if j != i: self.assertEqual(obj[j], val) def check_getslice(self, obj): self._check_unary(getslice_usecase, obj, 1, len(obj) - 1) def test_len(self): self.check_len(bytearray(5)) self.check_len(b"xyz") for mem in self._memoryviews(): self.check_len(mem) for arr in self._arrays(): self.check_len(arr) for buf in self._readonlies(): self.check_getitem(buf) def test_getitem(self): self.check_getitem(bytearray(b"abc")) self.check_getitem(b"xyz") for mem in self._memoryviews(): self.check_getitem(mem) for arr in self._arrays(): self.check_getitem(arr) for buf in self._readonlies(): self.check_getitem(buf) def test_getslice(self): with self.assertTypingError(): self.check_getslice(bytearray(b"abcde")) self.check_getslice(b"xyzuvw") self.check_getslice(memoryview(b"xyzuvw")) with self.assertTypingError(): self.check_getslice(array.array('i', range(10))) for buf in self._readonlies(): self.check_getitem(buf) def test_setitem(self): self.check_setitem(bytearray(b"abcdefghi")) for arr in self._arrays(): self.check_setitem(arr) for mem in self._memoryviews(): self.check_getitem(mem) # Read-only buffers for buf in self._readonlies(): with self.assertTypingError(): self.check_setitem(buf) def test_iter(self): self.check_iter(bytearray(b"abc")) self.check_iter(b"xyz") self.check_iter(memoryview(b"xyz")) for arr in self._arrays(): self.check_iter(arr) for buf in self._readonlies(): self.check_getitem(buf) class TestMemoryView(MemoryLeakMixin, TestCase): """ Test memoryview-specific attributes and operations. """ def _arrays(self): arr = np.arange(12) yield arr arr = arr.reshape((3, 4)) yield arr yield arr.T yield arr[::2] arr.setflags(write=False) yield arr arr = np.zeros(()) assert arr.ndim == 0 yield arr def test_ndim(self): for arr in self._arrays(): m = memoryview(arr) self.assertPreciseEqual(ndim_usecase(m), arr.ndim) def test_shape(self): for arr in self._arrays(): m = memoryview(arr) self.assertPreciseEqual(shape_usecase(m), arr.shape) def test_strides(self): for arr in self._arrays(): m = memoryview(arr) self.assertPreciseEqual(strides_usecase(m), arr.strides) def test_itemsize(self): for arr in self._arrays(): m = memoryview(arr) self.assertPreciseEqual(itemsize_usecase(m), arr.itemsize) def test_nbytes(self): for arr in self._arrays(): m = memoryview(arr) self.assertPreciseEqual(nbytes_usecase(m), arr.size * arr.itemsize) def test_readonly(self): for arr in self._arrays(): m = memoryview(arr) self.assertIs(readonly_usecase(m), not arr.flags.writeable) m = memoryview(b"xyz") self.assertIs(readonly_usecase(m), True) m = memoryview(bytearray(b"xyz")) self.assertIs(readonly_usecase(m), False) def test_contiguous(self): m = memoryview(bytearray(b"xyz")) self.assertIs(contiguous_usecase(m), True) self.assertIs(c_contiguous_usecase(m), True) self.assertIs(f_contiguous_usecase(m), True) for arr in self._arrays(): m = memoryview(arr) # Note `arr.flags.contiguous` is wrong (it mimics c_contiguous) self.assertIs(contiguous_usecase(m), arr.flags.f_contiguous or arr.flags.c_contiguous) self.assertIs(c_contiguous_usecase(m), arr.flags.c_contiguous) self.assertIs(f_contiguous_usecase(m), arr.flags.f_contiguous) if __name__ == '__main__': unittest.main()