Index: Lib/test/test_mmap.py =================================================================== --- Lib/test/test_mmap.py (revision 53047) +++ Lib/test/test_mmap.py (working copy) @@ -306,6 +306,40 @@ m[x] = ch = chr(x & 255) self.assertEqual(m[x], ch) + def test_extended_getslice(self): + # Test extended slicing by comparing with list slicing. + s = "".join(chr(c) for c in reversed(range(256))) + m = mmap.mmap(-1, len(s)) + m[:] = s + self.assertEqual(m[:], s) + indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300) + for start in indices: + for stop in indices: + # Skip step 0 (invalid) + for step in indices[1:]: + self.assertEqual(m[start:stop:step], + s[start:stop:step]) + + def test_extended_set_del_slice(self): + # Test extended slicing by comparing with list slicing. + s = "".join(chr(c) for c in reversed(range(256))) + m = mmap.mmap(-1, len(s)) + indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300) + for start in indices: + for stop in indices: + # Skip invalid step 0 + for step in indices[1:]: + m[:] = s + self.assertEqual(m[:], s) + L = list(s) + # Make sure we have a slice of exactly the right length, + # but with different data. + data = L[start:stop:step] + data = "".join(reversed(data)) + L[start:stop:step] = data + m[start:stop:step] = data + self.assertEquals(m[:], "".join(L)) + def test_main(): run_unittest(MmapTests) Index: Modules/mmapmodule.c =================================================================== --- Modules/mmapmodule.c (revision 53047) +++ Modules/mmapmodule.c (working copy) @@ -681,6 +681,60 @@ } static PyObject * +mmap_subscript(mmap_object *self, PyObject *item) +{ + CHECK_VALID(NULL); + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return NULL; + if (i < 0) + i += self->size; + if (i < 0 || i > self->size) { + PyErr_SetString(PyExc_IndexError, + "mmap index out of range"); + return NULL; + } + return PyString_FromStringAndSize(self->data + i, 1); + } + else if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelen; + + if (PySlice_GetIndicesEx((PySliceObject *)item, self->size, + &start, &stop, &step, &slicelen) < 0) { + return NULL; + } + + if (slicelen <= 0) + return PyString_FromStringAndSize("", 0); + else if (step == 1) + return PyString_FromStringAndSize(self->data + start, + slicelen); + else { + char *result_buf = (char *)PyMem_Malloc(slicelen); + Py_ssize_t cur, i; + PyObject *result; + + if (result_buf == NULL) + return PyErr_NoMemory(); + for (cur = start, i = 0; i < slicelen; + cur += step, i++) { + result_buf[i] = self->data[cur]; + } + result = PyString_FromStringAndSize(result_buf, + slicelen); + PyMem_Free(result_buf); + return result; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "mmap indices must be integers"); + return NULL; + } +} + +static PyObject * mmap_concat(mmap_object *self, PyObject *bb) { CHECK_VALID(NULL); @@ -764,6 +818,96 @@ return 0; } +static int +mmap_ass_subscript(mmap_object *self, PyObject *item, PyObject *value) +{ + CHECK_VALID(-1); + + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + const char *buf; + + if (i == -1 && PyErr_Occurred()) + return -1; + if (i < 0) + i += self->size; + if (i < 0 || i > self->size) { + PyErr_SetString(PyExc_IndexError, + "mmap index out of range"); + return -1; + } + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "mmap object doesn't support item deletion"); + return -1; + } + if (!PyString_Check(value) || PyString_Size(value) != 1) { + PyErr_SetString(PyExc_IndexError, + "mmap assignment must be single-character string"); + return -1; + } + if (!is_writeable(self)) + return -1; + buf = PyString_AsString(value); + self->data[i] = buf[0]; + return 0; + } + else if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelen; + + if (PySlice_GetIndicesEx((PySliceObject *)item, + self->size, &start, &stop, + &step, &slicelen) < 0) { + return -1; + } + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "mmap object doesn't support slice deletion"); + return -1; + } + if (!PyString_Check(value)) { + PyErr_SetString(PyExc_IndexError, + "mmap slice assignment must be a string"); + return -1; + } + if (PyString_Size(value) != slicelen) { + PyErr_SetString(PyExc_IndexError, + "mmap slice assignment is wrong size"); + return -1; + } + if (!is_writeable(self)) + return -1; + + if (slicelen == 0) + return 0; + else if (step == 1) { + const char *buf = PyString_AsString(value); + + if (buf == NULL) + return -1; + memcpy(self->data + start, buf, slicelen); + return 0; + } + else { + Py_ssize_t cur, i; + const char *buf = PyString_AsString(value); + + if (buf == NULL) + return -1; + for (cur = start, i = 0; i < slicelen; + cur += step, i++) { + self->data[cur] = buf[i]; + } + return 0; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "mmap indices must be integer"); + return -1; + } +} + static PySequenceMethods mmap_as_sequence = { (lenfunc)mmap_length, /*sq_length*/ (binaryfunc)mmap_concat, /*sq_concat*/ @@ -774,6 +918,12 @@ (ssizessizeobjargproc)mmap_ass_slice, /*sq_ass_slice*/ }; +static PyMappingMethods mmap_as_mapping = { + (lenfunc)mmap_length, + (binaryfunc)mmap_subscript, + (objobjargproc)mmap_ass_subscript, +}; + static PyBufferProcs mmap_as_buffer = { (readbufferproc)mmap_buffer_getreadbuf, (writebufferproc)mmap_buffer_getwritebuf, @@ -796,7 +946,7 @@ 0, /* tp_repr */ 0, /* tp_as_number */ &mmap_as_sequence, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ + &mmap_as_mapping, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/