diff -r fa4a44afda12 Lib/test/test_memoryview.py --- a/Lib/test/test_memoryview.py Tue Aug 19 08:38:12 2008 +0200 +++ b/Lib/test/test_memoryview.py Tue Aug 19 14:37:17 2008 +0200 @@ -5,8 +5,78 @@ import unittest import test.support +import sys -class MemoryviewTest(unittest.TestCase): + +class CommonMemoryTests: + # + # Tests common to direct memoryviews and sliced memoryviews + # + + base_object = b"abc" + + def _test_getitem_with_type(self, tp): + b = tp(self.base_object) + oldrefcount = sys.getrefcount(b) + m = self._view(b) + self.assertEquals(m[0], b"a") + self.assert_(isinstance(m[0], bytearray), type(m[0])) + self.assertEquals(m[2], b"c") + self.assertEquals(m[-1], b"c") + self.assertEquals(m[-3], b"a") + self.assertRaises(IndexError, lambda: m[3]) + self.assertRaises(IndexError, lambda: m[-4]) + self.assertRaises(IndexError, lambda: m[sys.maxsize]) + self.assertRaises(IndexError, lambda: m[-sys.maxsize]) + m = None + self.assertEquals(sys.getrefcount(b), oldrefcount) + + def test_getitem_readonly(self): + self._test_getitem_with_type(bytes) + + def test_getitem_writable(self): + self._test_getitem_with_type(bytearray) + + def test_setitem_readonly(self): + b = self.base_object + oldrefcount = sys.getrefcount(b) + m = self._view(b) + def setitem(value): + m[0] = value + self.assertRaises(TypeError, setitem, b"a") + self.assertRaises(TypeError, setitem, 65) + self.assertRaises(TypeError, setitem, memoryview(b"a")) + m = None + self.assertEquals(sys.getrefcount(b), oldrefcount) + + def test_len(self): + self.assertEquals(len(self._view(self.base_object)), 3) + + def _test_attributes_with_type(self, tp): + b = tp(self.base_object) + m = self._view(b) + self.assertEquals(m.format, 'B') + self.assertEquals(m.itemsize, 1) + self.assertEquals(m.ndim, 1) + self.assertEquals(m.shape, (3,)) + self.assertEquals(m.size, 3) + self.assertEquals(m.strides, (1,)) + self.assertEquals(m.suboffsets, None) + return m + + def test_attributes_readonly(self): + m = self._test_attributes_with_type(bytes) + self.assertEquals(m.readonly, True) + + def test_attributes_writable(self): + m = self._test_attributes_with_type(bytearray) + self.assertEquals(m.readonly, False) + + +class MemoryviewTest(unittest.TestCase, CommonMemoryTests): + + def _view(self, obj): + return memoryview(obj) def test_constructor(self): ob = b'test' @@ -17,8 +87,32 @@ self.assertRaises(TypeError, memoryview, argument=ob) self.assertRaises(TypeError, memoryview, ob, argument=True) + +class MemorySliceTest(unittest.TestCase, CommonMemoryTests): + base_object = b"XabcY" + + def _view(self, obj): + m = memoryview(obj) + return m[1:4] + + def test_refs(self): + m = memoryview(b"ab") + oldrefcount = sys.getrefcount(m) + m[1:2] + self.assertEquals(sys.getrefcount(m), oldrefcount) + + +class MemorySliceSliceTest(unittest.TestCase, CommonMemoryTests): + base_object = b"XabcY" + + def _view(self, obj): + m = memoryview(obj) + return m[:4][1:] + + def test_main(): - test.support.run_unittest(MemoryviewTest) + test.support.run_unittest( + MemoryviewTest, MemorySliceTest, MemorySliceSliceTest) if __name__ == "__main__": diff -r fa4a44afda12 Objects/memoryobject.c --- a/Objects/memoryobject.c Tue Aug 19 08:38:12 2008 +0200 +++ b/Objects/memoryobject.c Tue Aug 19 14:37:17 2008 +0200 @@ -13,8 +13,8 @@ } if (self->view.obj == NULL) return 0; - return self->view.obj->ob_type->tp_as_buffer->bf_getbuffer(self->base, NULL, - PyBUF_FULL); + return self->view.obj->ob_type->tp_as_buffer->bf_getbuffer( + self->view.obj, NULL, PyBUF_FULL); } static void @@ -37,9 +37,14 @@ &PyMemoryView_Type); if (mview == NULL) return NULL; mview->base = NULL; + /* XXX there should be an API to duplicate a buffer object */ mview->view = *info; - if (info->obj) - Py_INCREF(mview->view.obj); + if (info->shape == &(info->len)) + mview->view.shape = &(mview->view.len); + if (info->strides == &(info->itemsize)) + mview->view.strides = &(mview->view.itemsize); + /* NOTE: mview->view.obj should already have been incref'ed as + part of PyBuffer_FillInfo(). */ return (PyObject *)mview; } @@ -412,7 +417,7 @@ memory_dealloc(PyMemoryViewObject *self) { if (self->view.obj != NULL) { - if (PyTuple_Check(self->base)) { + if (self->base && PyTuple_Check(self->base)) { /* Special case when first element is generic object with buffer interface and the second element is a contiguous "shadow" that must be copied back into @@ -511,7 +516,7 @@ if (result < 0) { result += view->shape[0]; } - if ((result < 0) || (result > view->shape[0])) { + if ((result < 0) || (result >= view->shape[0])) { PyErr_SetString(PyExc_IndexError, "index out of bounds"); return NULL; @@ -537,8 +542,35 @@ return PyMemoryView_FromMemory(&newview); } } + else if (PySlice_Check(key)) { + Py_ssize_t start, stop, step, slicelength; - /* Need to support getting a sliced view */ + if (PySlice_GetIndicesEx((PySliceObject*)key, self->view.len, + &start, &stop, &step, &slicelength) < 0) { + return NULL; + } + + if (step == 1 && view->ndim == 1) { + Py_buffer newview; + void *newbuf = (char *) self->view.buf + + start * self->view.itemsize; + int newflags = self->view.readonly + ? PyBUF_CONTIG_RO : PyBUF_CONTIG; + + /* XXX There should be an API to create a subbuffer */ + if (PyObject_GetBuffer(self->view.obj, + &newview, newflags) == -1) + return NULL; + newview.buf = newbuf; + newview.len = slicelength; + newview.format = self->view.format; + newview.shape = &(newview.len); + if (view->strides == &(view->itemsize)) + newview.strides = &(newview.itemsize); + return PyMemoryView_FromMemory(&newview); + } + } + Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } @@ -548,7 +580,12 @@ static int memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value) { - return 0; + if (self->view.readonly) { + PyErr_SetString(PyExc_TypeError, + "cannot modify read-only memory"); + return -1; + } + return 0; } /* As mapping */