def test_slice_stride(self): a = b'World_ _i_s_ _hell!' a_refcount = sys.getrefcount(a) b = containers.ArrayView(a) b_refcount = sys.getrefcount(b) self.assertEqual(sys.getrefcount(a), a_refcount + 1) # When slicing to a strided array view, b's refcount should not change # but a's refcount should increase. Check consistency with slices on # bytes, slicing bytes will make a copy so it doesn't affect the # refcount. c1 = a[4:-4:2] c2 = b[4:-4:2] self.assertIsInstance(c2, containers.StridedArrayView1D) self.assertEqual(len(c1), 6) self.assertEqual(len(c2), 6) self.assertEqual(bytes(c1), b'd is h') self.assertEqual(bytes(c2), b'd is h') self.assertEqual(c2.size, (6, )) self.assertEqual(c2.stride, (2, )) self.assertEqual(sys.getrefcount(b), b_refcount) self.assertEqual(sys.getrefcount(a), a_refcount + 2) # Deleting a slice should reduce a's refcount again, keep b's unchanged del c2 self.assertEqual(sys.getrefcount(b), b_refcount) self.assertEqual(sys.getrefcount(a), a_refcount + 1)
def test_init_buffer_memoryview_obj(self): a = b'hello' v = memoryview(a) b = containers.ArrayView(v) # memoryview's buffer protocol returns itself, not the underlying # bytes, as it manages the Py_buffer instance. So this is expected. self.assertIs(b.owner, v)
def test_init_buffer(self): a = b'hello' a_refcount = sys.getrefcount(a) b = containers.ArrayView(a) self.assertIs(b.owner, a) self.assertEqual(len(b), 5) self.assertEqual(bytes(b), b'hello') self.assertEqual(b[2], 'l') self.assertEqual(sys.getrefcount(a), a_refcount + 1) # Not mutable with self.assertRaisesRegex(TypeError, "object does not support item assignment"): b[4] = '!' # b should keep a reference to a, so deleting the local reference # shouldn't affect it del a self.assertTrue(sys.getrefcount(b.owner), a_refcount) self.assertEqual(b[2], 'l') # Now, if we delete b, a should not be referenced by anything anymore a = b.owner del b self.assertTrue(sys.getrefcount(a), a_refcount)
def test_init_buffer_empty(self): a = b'' a_refcount = sys.getrefcount(a) b = containers.ArrayView(a) self.assertIs(b.owner, None) self.assertEqual(len(b), 0) self.assertEqual(sys.getrefcount(a), a_refcount)
def test_init_buffer_unexpected_stride(self): a = memoryview(b'hello')[::2] self.assertEqual(bytes(a), b'hlo') # Error emitted by memoryview, not us with self.assertRaisesRegex( BufferError, "memoryview: underlying buffer is not C-contiguous"): b = containers.ArrayView(a)
def test_init(self): a = containers.ArrayView() b = containers.MutableArrayView() self.assertIs(a.owner, None) self.assertIs(b.owner, None) self.assertEqual(len(a), 0) self.assertEqual(len(b), 0) self.assertEqual(bytes(a), b'') self.assertEqual(bytes(b), b'')
def test_slice_stride_empty(self): data = b'hello' data_refcount = sys.getrefcount(data) # slice.start = slice.stop a = containers.ArrayView(data)[7:8:2] self.assertEqual(len(a), 0) # Empty view, original data not referenced at all self.assertIs(a.owner, None) self.assertEqual(sys.getrefcount(data), data_refcount)
def test_slice_stride_negative(self): a = b'World_ _i_s_ _hell!' b = containers.ArrayView(a) # Check consistency with slices on bytes c1 = a[-5:3:-2] # like [4:-4:2] above, but reverted c2 = b[-5:3:-2] self.assertEqual(len(c1), 6) self.assertEqual(len(c2), 6) self.assertEqual(bytes(c1), b'h si d') # like b'd is h' but reverted self.assertEqual(bytes(c2), b'h si d') self.assertEqual(c2.size, (6, )) self.assertEqual(c2.stride, (-2, ))
def test_slice(self): a = b'World is hell!' a_refcount = sys.getrefcount(a) b = containers.ArrayView(a) b_refcount = sys.getrefcount(b) self.assertEqual(sys.getrefcount(a), a_refcount + 1) # When slicing, b's refcount should not change but a's refcount should # increase c = b[4:-4] self.assertIsInstance(c, containers.ArrayView) self.assertEqual(bytes(c), b'd is h') self.assertEqual(sys.getrefcount(b), b_refcount) self.assertEqual(sys.getrefcount(a), a_refcount + 2) # Deleting a slice should reduce a's refcount again, keep b's unchanged del c self.assertEqual(sys.getrefcount(b), b_refcount) self.assertEqual(sys.getrefcount(a), a_refcount + 1)
def test_convert_memoryview(self): a = b'World is hell!' a_refcount = sys.getrefcount(a) b = containers.ArrayView(a) b_refcount = sys.getrefcount(b) self.assertEqual(sys.getrefcount(a), a_refcount + 1) c = memoryview(b) # Unlike slicing, ArrayView's buffer protocol returns a reference to # itself -- it needs to be kept around because the Py_buffer refers to # its internals for size. Also returning a reference to the underlying # buffer would mean the underlying buffer's releasebuffer function gets # called instead of ours which is *not* wanted. self.assertIs(c.obj, b) self.assertEqual(sys.getrefcount(b), b_refcount + 1) self.assertEqual(sys.getrefcount(a), a_refcount + 1) with self.assertRaisesRegex(TypeError, "cannot modify read-only memory"): c[-1] = ord('?')
def test_init_array(self): a = array.array('f', [1.0, 4.5, 7.9]) b = containers.ArrayView(a) self.assertIs(b.owner, a) self.assertEqual(len(b), 3 * 4)
def test_slice_stride_reverse(self): # slice.stop = -1 a = containers.ArrayView(b'hello')[::-1] self.assertEqual(len(a), 5) self.assertEqual(bytes(a), b'olleh')
def test_slice_invalid(self): with self.assertRaisesRegex(ValueError, "slice step cannot be zero"): containers.ArrayView()[::0]
def test_slice_empty(self): # slice.start = slice.stop a = containers.ArrayView(b'hello')[7:8] self.assertEqual(len(a), 0)