diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2009-01-03 16:59:18 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2009-01-03 16:59:18 (GMT) |
commit | c3b39245a7695cf39ba5524f59deeff52b00e5f9 (patch) | |
tree | 9736aa9a6d7c03b55eeb7d362a53f91d6000dda3 /Lib | |
parent | 8bcddcabd770dd424b97d7c667ef8e5337436215 (diff) | |
download | cpython-c3b39245a7695cf39ba5524f59deeff52b00e5f9.zip cpython-c3b39245a7695cf39ba5524f59deeff52b00e5f9.tar.gz cpython-c3b39245a7695cf39ba5524f59deeff52b00e5f9.tar.bz2 |
Issue #4580: slicing of memoryviews when itemsize != 1 is wrong.
Also fix len() to return number of items rather than length in bytes.
I'm sorry it was not possible for me to work on this without reindenting
a bit some stuff around. The indentation in memoryobject.c is a mess,
I'll open a separate bug for it.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/ctypes/test/test_pep3118.py | 14 | ||||
-rw-r--r-- | Lib/test/test_io.py | 2 | ||||
-rw-r--r-- | Lib/test/test_memoryview.py | 340 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 2 |
4 files changed, 219 insertions, 139 deletions
diff --git a/Lib/ctypes/test/test_pep3118.py b/Lib/ctypes/test/test_pep3118.py index bdcf3ec..b839ac2 100644 --- a/Lib/ctypes/test/test_pep3118.py +++ b/Lib/ctypes/test/test_pep3118.py @@ -25,7 +25,10 @@ class Test(unittest.TestCase): v = memoryview(ob) try: self.failUnlessEqual(normalize(v.format), normalize(fmt)) - self.failUnlessEqual(len(v), sizeof(ob)) + if shape is not None: + self.failUnlessEqual(len(v), shape[0]) + else: + self.failUnlessEqual(len(v) * sizeof(itemtp), sizeof(ob)) self.failUnlessEqual(v.itemsize, sizeof(itemtp)) self.failUnlessEqual(v.shape, shape) # ctypes object always have a non-strided memory block @@ -37,7 +40,7 @@ class Test(unittest.TestCase): n = 1 for dim in v.shape: n = n * dim - self.failUnlessEqual(v.itemsize * n, len(v)) + self.failUnlessEqual(n * v.itemsize, len(v.tobytes())) except: # so that we can see the failing type print(tp) @@ -49,7 +52,10 @@ class Test(unittest.TestCase): v = memoryview(ob) try: self.failUnlessEqual(v.format, fmt) - self.failUnlessEqual(len(v), sizeof(ob)) + if shape is not None: + self.failUnlessEqual(len(v), shape[0]) + else: + self.failUnlessEqual(len(v) * sizeof(itemtp), sizeof(ob)) self.failUnlessEqual(v.itemsize, sizeof(itemtp)) self.failUnlessEqual(v.shape, shape) # ctypes object always have a non-strided memory block @@ -61,7 +67,7 @@ class Test(unittest.TestCase): n = 1 for dim in v.shape: n = n * dim - self.failUnlessEqual(v.itemsize * n, len(v)) + self.failUnlessEqual(n, len(v)) except: # so that we can see the failing type print(tp) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 0fc017f..af5b6f4 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -260,7 +260,7 @@ class IOTest(unittest.TestCase): def test_array_writes(self): a = array.array('i', range(10)) - n = len(memoryview(a)) + n = len(a.tostring()) f = io.open(support.TESTFN, "wb", 0) self.assertEqual(f.write(a), n) f.close() diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index 7139065..cc8502b 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -8,24 +8,30 @@ import test.support import sys import gc import weakref +import array -class CommonMemoryTests: - # - # Tests common to direct memoryviews and sliced memoryviews - # +class AbstractMemoryTests: + source_bytes = b"abcdef" - base_object = b"abcdef" + @property + def _source(self): + return self.source_bytes + + @property + def _types(self): + return filter(None, [self.ro_type, self.rw_type]) def check_getitem_with_type(self, tp): - b = tp(self.base_object) + item = self.getitem_type + b = tp(self._source) oldrefcount = sys.getrefcount(b) m = self._view(b) - self.assertEquals(m[0], b"a") + self.assertEquals(m[0], item(b"a")) self.assert_(isinstance(m[0], bytes), type(m[0])) - self.assertEquals(m[5], b"f") - self.assertEquals(m[-1], b"f") - self.assertEquals(m[-6], b"a") + self.assertEquals(m[5], item(b"f")) + self.assertEquals(m[-1], item(b"f")) + self.assertEquals(m[-6], item(b"a")) # Bounds checking self.assertRaises(IndexError, lambda: m[6]) self.assertRaises(IndexError, lambda: m[-7]) @@ -38,14 +44,14 @@ class CommonMemoryTests: m = None self.assertEquals(sys.getrefcount(b), oldrefcount) - def test_getitem_readonly(self): - self.check_getitem_with_type(bytes) - - def test_getitem_writable(self): - self.check_getitem_with_type(bytearray) + def test_getitem(self): + for tp in self._types: + self.check_getitem_with_type(tp) def test_setitem_readonly(self): - b = self.base_object + if not self.ro_type: + return + b = self.ro_type(self._source) oldrefcount = sys.getrefcount(b) m = self._view(b) def setitem(value): @@ -57,27 +63,30 @@ class CommonMemoryTests: self.assertEquals(sys.getrefcount(b), oldrefcount) def test_setitem_writable(self): - b = bytearray(self.base_object) + if not self.rw_type: + return + tp = self.rw_type + b = self.rw_type(self._source) oldrefcount = sys.getrefcount(b) m = self._view(b) - m[0] = b"0" - self._check_contents(b, b"0bcdef") - m[1:3] = b"12" - self._check_contents(b, b"012def") - m[1:1] = b"" - self._check_contents(b, b"012def") - m[:] = b"abcdef" - self._check_contents(b, b"abcdef") + m[0] = tp(b"0") + self._check_contents(tp, b, b"0bcdef") + m[1:3] = tp(b"12") + self._check_contents(tp, b, b"012def") + m[1:1] = tp(b"") + self._check_contents(tp, b, b"012def") + m[:] = tp(b"abcdef") + self._check_contents(tp, b, b"abcdef") # Overlapping copies of a view into itself m[0:3] = m[2:5] - self._check_contents(b, b"cdedef") - m[:] = b"abcdef" + self._check_contents(tp, b, b"cdedef") + m[:] = tp(b"abcdef") m[2:5] = m[0:3] - self._check_contents(b, b"ababcf") + self._check_contents(tp, b, b"ababcf") def setitem(key, value): - m[key] = value + m[key] = tp(value) # Bounds checking self.assertRaises(IndexError, setitem, 6, b"a") self.assertRaises(IndexError, setitem, -7, b"a") @@ -96,159 +105,224 @@ class CommonMemoryTests: m = None self.assertEquals(sys.getrefcount(b), oldrefcount) - def test_len(self): - self.assertEquals(len(self._view(self.base_object)), 6) - def test_tobytes(self): - m = self._view(self.base_object) - b = m.tobytes() - self.assertEquals(b, b"abcdef") - self.assert_(isinstance(b, bytes), type(b)) + for tp in self._types: + m = self._view(tp(self._source)) + b = m.tobytes() + # This calls self.getitem_type() on each separate byte of b"abcdef" + expected = b"".join( + self.getitem_type(bytes([c])) for c in b"abcdef") + self.assertEquals(b, expected) + self.assert_(isinstance(b, bytes), type(b)) def test_tolist(self): - m = self._view(self.base_object) - l = m.tolist() - self.assertEquals(l, list(b"abcdef")) + for tp in self._types: + m = self._view(tp(self._source)) + l = m.tolist() + self.assertEquals(l, list(b"abcdef")) def test_compare(self): # memoryviews can compare for equality with other objects # having the buffer interface. - m = self._view(self.base_object) - for tp in (bytes, bytearray): - self.assertTrue(m == tp(b"abcdef")) - self.assertFalse(m != tp(b"abcdef")) - self.assertFalse(m == tp(b"abcde")) - self.assertTrue(m != tp(b"abcde")) - self.assertFalse(m == tp(b"abcde1")) - self.assertTrue(m != tp(b"abcde1")) - self.assertTrue(m == m) - self.assertTrue(m == m[:]) - self.assertTrue(m[0:6] == m[:]) - self.assertFalse(m[0:5] == m) - - # Comparison with objects which don't support the buffer API - self.assertFalse(m == "abc") - self.assertTrue(m != "abc") - self.assertFalse("abc" == m) - self.assertTrue("abc" != m) - - # Unordered comparisons - for c in (m, b"abcdef"): - self.assertRaises(TypeError, lambda: m < c) - self.assertRaises(TypeError, lambda: c <= m) - self.assertRaises(TypeError, lambda: m >= c) - self.assertRaises(TypeError, lambda: c > m) + for tp in self._types: + m = self._view(tp(self._source)) + for tp_comp in self._types: + self.assertTrue(m == tp_comp(b"abcdef")) + self.assertFalse(m != tp_comp(b"abcdef")) + self.assertFalse(m == tp_comp(b"abcde")) + self.assertTrue(m != tp_comp(b"abcde")) + self.assertFalse(m == tp_comp(b"abcde1")) + self.assertTrue(m != tp_comp(b"abcde1")) + self.assertTrue(m == m) + self.assertTrue(m == m[:]) + self.assertTrue(m[0:6] == m[:]) + self.assertFalse(m[0:5] == m) + + # Comparison with objects which don't support the buffer API + self.assertFalse(m == "abcdef") + self.assertTrue(m != "abcdef") + self.assertFalse("abcdef" == m) + self.assertTrue("abcdef" != m) + + # Unordered comparisons + for c in (m, b"abcdef"): + self.assertRaises(TypeError, lambda: m < c) + self.assertRaises(TypeError, lambda: c <= m) + self.assertRaises(TypeError, lambda: m >= c) + self.assertRaises(TypeError, lambda: c > m) def check_attributes_with_type(self, tp): - b = tp(self.base_object) - m = self._view(b) - self.assertEquals(m.format, 'B') - self.assertEquals(m.itemsize, 1) + m = self._view(tp(self._source)) + self.assertEquals(m.format, self.format) + self.assertEquals(m.itemsize, self.itemsize) self.assertEquals(m.ndim, 1) self.assertEquals(m.shape, (6,)) self.assertEquals(len(m), 6) - self.assertEquals(m.strides, (1,)) + self.assertEquals(m.strides, (self.itemsize,)) self.assertEquals(m.suboffsets, None) return m def test_attributes_readonly(self): - m = self.check_attributes_with_type(bytes) + if not self.ro_type: + return + m = self.check_attributes_with_type(self.ro_type) self.assertEquals(m.readonly, True) def test_attributes_writable(self): - m = self.check_attributes_with_type(bytearray) + if not self.rw_type: + return + m = self.check_attributes_with_type(self.rw_type) self.assertEquals(m.readonly, False) def test_getbuffer(self): # Test PyObject_GetBuffer() on a memoryview object. - b = self.base_object - oldrefcount = sys.getrefcount(b) - m = self._view(b) - oldviewrefcount = sys.getrefcount(m) - s = str(m, "utf-8") - self._check_contents(b, s.encode("utf-8")) - self.assertEquals(sys.getrefcount(m), oldviewrefcount) - m = None - self.assertEquals(sys.getrefcount(b), oldrefcount) + for tp in self._types: + b = tp(self._source) + oldrefcount = sys.getrefcount(b) + m = self._view(b) + oldviewrefcount = sys.getrefcount(m) + s = str(m, "utf-8") + self._check_contents(tp, b, s.encode("utf-8")) + self.assertEquals(sys.getrefcount(m), oldviewrefcount) + m = None + self.assertEquals(sys.getrefcount(b), oldrefcount) def test_gc(self): - class MyBytes(bytes): - pass - class MyObject: - pass + for tp in self._types: + if not isinstance(tp, type): + # If tp is a factory rather than a plain type, skip + continue + + class MySource(tp): + pass + class MyObject: + pass + + # Create a reference cycle through a memoryview object + b = MySource(tp(b'abc')) + m = self._view(b) + o = MyObject() + b.m = m + b.o = o + wr = weakref.ref(o) + b = m = o = None + # The cycle must be broken + gc.collect() + self.assert_(wr() is None, wr()) + + +# Variations on source objects for the buffer: bytes-like objects, then arrays +# with itemsize > 1. +# NOTE: support for multi-dimensional objects is unimplemented. + +class BaseBytesMemoryTests(AbstractMemoryTests): + ro_type = bytes + rw_type = bytearray + getitem_type = bytes + itemsize = 1 + format = 'B' + +class BaseArrayMemoryTests(AbstractMemoryTests): + ro_type = None + rw_type = lambda self, b: array.array('i', list(b)) + getitem_type = lambda self, b: array.array('i', list(b)).tostring() + itemsize = array.array('i').itemsize + format = 'i' - # Create a reference cycle through a memoryview object - b = MyBytes(b'abc') - m = self._view(b) - o = MyObject() - b.m = m - b.o = o - wr = weakref.ref(o) - b = m = o = None - # The cycle must be broken - gc.collect() - self.assert_(wr() is None, wr()) + def test_getbuffer(self): + # XXX Test should be adapted for non-byte buffers + pass + + def test_tolist(self): + # XXX NotImplementedError: tolist() only supports byte views + pass -class MemoryviewTest(unittest.TestCase, CommonMemoryTests): +# Variations on indirection levels: memoryview, slice of memoryview, +# slice of slice of memoryview. +# This is important to test allocation subtleties. +class BaseMemoryviewTests: def _view(self, obj): return memoryview(obj) - def _check_contents(self, obj, contents): - self.assertEquals(obj, contents) - - def test_constructor(self): - ob = b'test' - self.assert_(memoryview(ob)) - self.assert_(memoryview(object=ob)) - self.assertRaises(TypeError, memoryview) - self.assertRaises(TypeError, memoryview, ob, ob) - self.assertRaises(TypeError, memoryview, argument=ob) - self.assertRaises(TypeError, memoryview, ob, argument=True) - - def test_array_assign(self): - # Issue #4569: segfault when mutating a memoryview with itemsize != 1 - from array import array - a = array('i', range(10)) - m = memoryview(a) - new_a = array('i', range(9, -1, -1)) - m[:] = new_a - self.assertEquals(a, new_a) + def _check_contents(self, tp, obj, contents): + self.assertEquals(obj, tp(contents)) - -class MemorySliceTest(unittest.TestCase, CommonMemoryTests): - base_object = b"XabcdefY" +class BaseMemorySliceTests: + source_bytes = b"XabcdefY" def _view(self, obj): m = memoryview(obj) return m[1:7] - def _check_contents(self, obj, contents): - self.assertEquals(obj[1:7], contents) + def _check_contents(self, tp, obj, contents): + self.assertEquals(obj[1:7], tp(contents)) def test_refs(self): - m = memoryview(b"ab") - oldrefcount = sys.getrefcount(m) - m[1:2] - self.assertEquals(sys.getrefcount(m), oldrefcount) - + for tp in self._types: + m = memoryview(tp(self._source)) + oldrefcount = sys.getrefcount(m) + m[1:2] + self.assertEquals(sys.getrefcount(m), oldrefcount) -class MemorySliceSliceTest(unittest.TestCase, CommonMemoryTests): - base_object = b"XabcdefY" +class BaseMemorySliceSliceTests: + source_bytes = b"XabcdefY" def _view(self, obj): m = memoryview(obj) return m[:7][1:] - def _check_contents(self, obj, contents): - self.assertEquals(obj[1:7], contents) + def _check_contents(self, tp, obj, contents): + self.assertEquals(obj[1:7], tp(contents)) -def test_main(): - test.support.run_unittest( - MemoryviewTest, MemorySliceTest, MemorySliceSliceTest) +# Concrete test classes + +class BytesMemoryviewTest(unittest.TestCase, + BaseMemoryviewTests, BaseBytesMemoryTests): + + def test_constructor(self): + for tp in self._types: + ob = tp(self._source) + self.assert_(memoryview(ob)) + self.assert_(memoryview(object=ob)) + self.assertRaises(TypeError, memoryview) + self.assertRaises(TypeError, memoryview, ob, ob) + self.assertRaises(TypeError, memoryview, argument=ob) + self.assertRaises(TypeError, memoryview, ob, argument=True) + +class ArrayMemoryviewTest(unittest.TestCase, + BaseMemoryviewTests, BaseArrayMemoryTests): + def test_array_assign(self): + # Issue #4569: segfault when mutating a memoryview with itemsize != 1 + a = array.array('i', range(10)) + m = memoryview(a) + new_a = array.array('i', range(9, -1, -1)) + m[:] = new_a + self.assertEquals(a, new_a) + + +class BytesMemorySliceTest(unittest.TestCase, + BaseMemorySliceTests, BaseBytesMemoryTests): + pass + +class ArrayMemorySliceTest(unittest.TestCase, + BaseMemorySliceTests, BaseArrayMemoryTests): + pass + +class BytesMemorySliceSliceTest(unittest.TestCase, + BaseMemorySliceSliceTests, BaseBytesMemoryTests): + pass + +class ArrayMemorySliceSliceTest(unittest.TestCase, + BaseMemorySliceSliceTests, BaseArrayMemoryTests): + pass + + +def test_main(): + test.support.run_unittest(__name__) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index a8c19ee..7655fbf 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -559,7 +559,7 @@ class SizeofTest(unittest.TestCase): check(32768*32768-1, size(vh) + 2*self.H) check(32768*32768, size(vh) + 3*self.H) # memory - check(memoryview(b''), size(h + 'P PP2P2i5P')) + check(memoryview(b''), size(h + 'P PP2P2i7P')) # module check(unittest, size(h + '3P')) # None |