diff -r 136adbcefa5f Doc/library/collections.rst --- a/Doc/library/collections.rst Sun Jan 03 18:00:31 2016 -0800 +++ b/Doc/library/collections.rst Mon Jan 04 18:13:42 2016 +0200 @@ -527,8 +527,14 @@ the :keyword:`in` operator, and subscrip access is O(1) at both ends but slows to O(n) in the middle. For fast random access, use lists instead. -Starting in version 3.5, deques support ``__add__()``, ``__mul__()``, -and ``__imul__()``. +.. versionadded:: 3.5 + + Starting in version 3.5, deques support ``__add__()``, ``__mul__()``, + and ``__imul__()``. + +.. versionadded:: 3.6 + + Starting in version 3.6, deques support slicing. Example: diff -r 136adbcefa5f Lib/test/seq_tests.py --- a/Lib/test/seq_tests.py Sun Jan 03 18:00:31 2016 -0800 +++ b/Lib/test/seq_tests.py Mon Jan 04 18:13:42 2016 +0200 @@ -96,6 +96,7 @@ class LyingList(list): class CommonTest(unittest.TestCase): # The type to be tested type2test = None + slicetype = None def test_constructors(self): l0 = [] @@ -176,38 +177,39 @@ class CommonTest(unittest.TestCase): def test_getslice(self): l = [0, 1, 2, 3, 4] u = self.type2test(l) + slicetype = self.slicetype or self.type2test - self.assertEqual(u[0:0], self.type2test()) - self.assertEqual(u[1:2], self.type2test([1])) - self.assertEqual(u[-2:-1], self.type2test([3])) - self.assertEqual(u[-1000:1000], u) - self.assertEqual(u[1000:-1000], self.type2test([])) - self.assertEqual(u[:], u) - self.assertEqual(u[1:None], self.type2test([1, 2, 3, 4])) - self.assertEqual(u[None:3], self.type2test([0, 1, 2])) + self.assertEqual(u[0:0], slicetype()) + self.assertEqual(u[1:2], slicetype([1])) + self.assertEqual(u[-2:-1], slicetype([3])) + self.assertEqual(u[-1000:1000], slicetype(u)) + self.assertEqual(u[1000:-1000], slicetype([])) + self.assertEqual(u[:], slicetype(u)) + self.assertEqual(u[1:None], slicetype([1, 2, 3, 4])) + self.assertEqual(u[None:3], slicetype([0, 1, 2])) # Extended slices - self.assertEqual(u[::], u) - self.assertEqual(u[::2], self.type2test([0, 2, 4])) - self.assertEqual(u[1::2], self.type2test([1, 3])) - self.assertEqual(u[::-1], self.type2test([4, 3, 2, 1, 0])) - self.assertEqual(u[::-2], self.type2test([4, 2, 0])) - self.assertEqual(u[3::-2], self.type2test([3, 1])) - self.assertEqual(u[3:3:-2], self.type2test([])) - self.assertEqual(u[3:2:-2], self.type2test([3])) - self.assertEqual(u[3:1:-2], self.type2test([3])) - self.assertEqual(u[3:0:-2], self.type2test([3, 1])) - self.assertEqual(u[::-100], self.type2test([4])) - self.assertEqual(u[100:-100:], self.type2test([])) - self.assertEqual(u[-100:100:], u) + self.assertEqual(u[::], slicetype(u)) + self.assertEqual(u[::2], slicetype([0, 2, 4])) + self.assertEqual(u[1::2], slicetype([1, 3])) + self.assertEqual(u[::-1], slicetype([4, 3, 2, 1, 0])) + self.assertEqual(u[::-2], slicetype([4, 2, 0])) + self.assertEqual(u[3::-2], slicetype([3, 1])) + self.assertEqual(u[3:3:-2], slicetype([])) + self.assertEqual(u[3:2:-2], slicetype([3])) + self.assertEqual(u[3:1:-2], slicetype([3])) + self.assertEqual(u[3:0:-2], slicetype([3, 1])) + self.assertEqual(u[::-100], slicetype([4])) + self.assertEqual(u[100:-100:], slicetype([])) + self.assertEqual(u[-100:100:], slicetype(u)) self.assertEqual(u[100:-100:-1], u[::-1]) - self.assertEqual(u[-100:100:-1], self.type2test([])) - self.assertEqual(u[-100:100:2], self.type2test([0, 2, 4])) + self.assertEqual(u[-100:100:-1], slicetype([])) + self.assertEqual(u[-100:100:2], slicetype([0, 2, 4])) # Test extreme cases with long ints a = self.type2test([0,1,2,3,4]) - self.assertEqual(a[ -pow(2,128): 3 ], self.type2test([0,1,2])) - self.assertEqual(a[ 3: pow(2,145) ], self.type2test([3,4])) + self.assertEqual(a[ -pow(2,128): 3 ], slicetype([0, 1, 2])) + self.assertEqual(a[ 3: pow(2,145) ], slicetype([3, 4])) def test_contains(self): u = self.type2test([0, 1, 2]) @@ -327,17 +329,18 @@ class CommonTest(unittest.TestCase): def test_subscript(self): a = self.type2test([10, 11]) + slicetype = self.slicetype or self.type2test self.assertEqual(a.__getitem__(0), 10) self.assertEqual(a.__getitem__(1), 11) self.assertEqual(a.__getitem__(-2), 10) self.assertEqual(a.__getitem__(-1), 11) self.assertRaises(IndexError, a.__getitem__, -3) self.assertRaises(IndexError, a.__getitem__, 3) - self.assertEqual(a.__getitem__(slice(0,1)), self.type2test([10])) - self.assertEqual(a.__getitem__(slice(1,2)), self.type2test([11])) - self.assertEqual(a.__getitem__(slice(0,2)), self.type2test([10, 11])) - self.assertEqual(a.__getitem__(slice(0,3)), self.type2test([10, 11])) - self.assertEqual(a.__getitem__(slice(3,5)), self.type2test([])) + self.assertEqual(a.__getitem__(slice(0,1)), slicetype([10])) + self.assertEqual(a.__getitem__(slice(1,2)), slicetype([11])) + self.assertEqual(a.__getitem__(slice(0,2)), slicetype([10, 11])) + self.assertEqual(a.__getitem__(slice(0,3)), slicetype([10, 11])) + self.assertEqual(a.__getitem__(slice(3,5)), slicetype([])) self.assertRaises(ValueError, a.__getitem__, slice(0, 10, 0)) self.assertRaises(TypeError, a.__getitem__, 'x') diff -r 136adbcefa5f Lib/test/test_deque.py --- a/Lib/test/test_deque.py Sun Jan 03 18:00:31 2016 -0800 +++ b/Lib/test/test_deque.py Mon Jan 04 18:13:42 2016 +0200 @@ -242,11 +242,13 @@ class TestBasic(unittest.TestCase): d.append(i) l.append(i) for j in range(1-len(l), len(l)): - assert d[j] == l[j] + self.assertEqual(d[j], l[j]) d = deque('superman') self.assertEqual(d[0], 's') self.assertEqual(d[-1], 'n') + self.assertRaises(IndexError, d.__getitem__, 8) + self.assertRaises(IndexError, d.__getitem__, -9) d = deque() self.assertRaises(IndexError, d.__getitem__, 0) self.assertRaises(IndexError, d.__getitem__, -1) @@ -384,6 +386,7 @@ class TestBasic(unittest.TestCase): val = d[j] self.assertIn(val, d) del d[j] + #print(d) self.assertNotIn(val, d) self.assertEqual(len(d), 0) @@ -859,18 +862,7 @@ class TestSubclassWithKwargs(unittest.Te class TestSequence(seq_tests.CommonTest): type2test = deque - - def test_getitem(self): - # For now, bypass tests that require slicing - pass - - def test_getslice(self): - # For now, bypass tests that require slicing - pass - - def test_subscript(self): - # For now, bypass tests that require slicing - pass + slicetype = list #============================================================================== diff -r 136adbcefa5f Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c Sun Jan 03 18:00:31 2016 -0800 +++ b/Modules/_collectionsmodule.c Mon Jan 04 18:13:42 2016 +0200 @@ -175,6 +175,92 @@ deque_new(PyTypeObject *type, PyObject * return (PyObject *)deque; } +/* Advance block/index pair */ +#define INCRPOS(b, i) do { \ + if (++(i) == BLOCKLEN) { \ + (b) = (b)->rightlink; \ + (i) = 0; \ + } \ + } while (0); + +/* Step backwards with block/index pair */ +#define DECRPOS(b, i) do { \ + if ((i)-- == 0) { \ + (b) = (b)->leftlink; \ + (i) = BLOCKLEN - 1; \ + } \ + } while (0); + +#define CHANGEPOS(b, i, step) do { \ + (i) += (step); \ + if ((step) > 0) { \ + while ((i) >= BLOCKLEN) { \ + (b) = (b)->rightlink; \ + (i) -= BLOCKLEN; \ + } \ + } \ + else { \ + while ((i) < 0) { \ + (b) = (b)->leftlink; \ + (i) += BLOCKLEN; \ + } \ + } \ + } while (0); + +/* Copy items from left to right */ +#define COPY_LEFT(dstblock, dstindex, srcblock, srcindex, len) do { \ + Py_ssize_t _counter = (len); \ + while (_counter--) { \ + (dstblock)->data[(dstindex)] = (srcblock)->data[(srcindex)]; \ + INCRPOS((dstblock), (dstindex)); \ + CHECK_NOT_END((dstblock)); \ + INCRPOS((srcblock), (srcindex)); \ + } \ + } while (0); + +/* Copy items from right to left */ +#define COPY_RIGHT(dstblock, dstindex, srcblock, srcindex, len) do { \ + Py_ssize_t _counter = (len); \ + while (_counter--) { \ + (dstblock)->data[(dstindex)] = (srcblock)->data[(srcindex)]; \ + DECRPOS((dstblock), (dstindex)); \ + CHECK_NOT_END((dstblock)); \ + DECRPOS((srcblock), (srcindex)); \ + } \ + } while (0); + +static block * +deque_get_block(dequeobject *deque, Py_ssize_t *index) +{ + Py_ssize_t i = *index, m = i, n; + block *b; + + if (i == 0) { + *index = deque->leftindex; + b = deque->leftblock; + } else if (i == Py_SIZE(deque) - 1) { + *index = deque->rightindex; + b = deque->rightblock; + } else { + i += deque->leftindex; + n = (Py_ssize_t)((size_t) i / BLOCKLEN); + *index = (Py_ssize_t)((size_t) i % BLOCKLEN); + if (m < (Py_SIZE(deque) >> 1)) { + b = deque->leftblock; + while (n--) + b = b->rightlink; + } else { + n = (Py_ssize_t)( + ((size_t)(deque->leftindex + Py_SIZE(deque) - 1)) + / BLOCKLEN - n); + b = deque->rightblock; + while (n--) + b = b->leftlink; + } + } + return b; +} + static PyObject * deque_pop(dequeobject *deque, PyObject *unused) { @@ -917,19 +1003,8 @@ deque_reverse(dequeobject *deque, PyObje leftblock->data[leftindex] = rightblock->data[rightindex]; rightblock->data[rightindex] = tmp; - /* Advance left block/index pair */ - leftindex++; - if (leftindex == BLOCKLEN) { - leftblock = leftblock->rightlink; - leftindex = 0; - } - - /* Step backwards with the right block/index pair */ - rightindex--; - if (rightindex < 0) { - rightblock = rightblock->leftlink; - rightindex = BLOCKLEN - 1; - } + INCRPOS(leftblock, leftindex); + DECRPOS(rightblock, rightindex); } Py_RETURN_NONE; } @@ -962,12 +1037,7 @@ deque_count(dequeobject *deque, PyObject return NULL; } - /* Advance left block/index pair */ - index++; - if (index == BLOCKLEN) { - b = b->rightlink; - index = 0; - } + INCRPOS(b, index); } return PyLong_FromSsize_t(count); } @@ -997,11 +1067,7 @@ deque_contains(dequeobject *deque, PyObj "deque mutated during iteration"); return -1; } - index++; - if (index == BLOCKLEN) { - b = b->rightlink; - index = 0; - } + INCRPOS(b, index); } return 0; } @@ -1015,7 +1081,7 @@ deque_len(dequeobject *deque) static PyObject * deque_index(dequeobject *deque, PyObject *args) { - Py_ssize_t i, n, start=0, stop=Py_SIZE(deque); + Py_ssize_t n, start=0, stop=Py_SIZE(deque); PyObject *v, *item; block *b = deque->leftblock; Py_ssize_t index = deque->leftindex; @@ -1042,16 +1108,10 @@ deque_index(dequeobject *deque, PyObject start = stop; assert(0 <= start && start <= stop && stop <= Py_SIZE(deque)); - /* XXX Replace this loop with faster code from deque_item() */ - for (i=0 ; irightlink; - index = 0; - } - } + index = start; + b = deque_get_block(deque, &index); - n = stop - i + 1; + n = stop - start + 1; while (--n) { CHECK_NOT_END(b); item = b->data[index]; @@ -1065,11 +1125,7 @@ deque_index(dequeobject *deque, PyObject "deque mutated during iteration"); return NULL; } - index++; - if (index == BLOCKLEN) { - b = b->rightlink; - index = 0; - } + INCRPOS(b, index); } PyErr_Format(PyExc_ValueError, "%R is not in deque", v); return NULL; @@ -1166,41 +1222,65 @@ deque_item(dequeobject *deque, Py_ssize_ { block *b; PyObject *item; - Py_ssize_t n, index=i; if (!valid_index(i, Py_SIZE(deque))) { PyErr_SetString(PyExc_IndexError, "deque index out of range"); return NULL; } - if (i == 0) { - i = deque->leftindex; - b = deque->leftblock; - } else if (i == Py_SIZE(deque) - 1) { - i = deque->rightindex; - b = deque->rightblock; - } else { - i += deque->leftindex; - n = (Py_ssize_t)((size_t) i / BLOCKLEN); - i = (Py_ssize_t)((size_t) i % BLOCKLEN); - if (index < (Py_SIZE(deque) >> 1)) { - b = deque->leftblock; - while (n--) - b = b->rightlink; - } else { - n = (Py_ssize_t)( - ((size_t)(deque->leftindex + Py_SIZE(deque) - 1)) - / BLOCKLEN - n); - b = deque->rightblock; - while (n--) - b = b->leftlink; - } - } + b = deque_get_block(deque, &i); item = b->data[i]; Py_INCREF(item); return item; } +static PyObject * +deque_subscr(dequeobject *deque, PyObject *index) +{ + if (PyIndex_Check(index)) { + Py_ssize_t i = PyNumber_AsSsize_t(index, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return NULL; + if (i < 0) + i += Py_SIZE(deque); + return deque_item(deque, i); + } + + if (PySlice_Check(index)) { + Py_ssize_t start, stop, step, slicelen, i; + PyObject *list; + block *b; + + if (PySlice_GetIndicesEx(index, + Py_SIZE(deque), + &start, &stop, &step, &slicelen) < 0) { + return NULL; + } + + list = PyList_New(slicelen); + if (slicelen == 0 || list == NULL) + return list; + + b = deque_get_block(deque, &start); + i = 0; + while (1) { + PyObject *item = b->data[start]; + Py_INCREF(item); + PyList_SET_ITEM(list, i, item); + if (++i >= slicelen) + break; + CHANGEPOS(b, start, step); + } + + return list; + } + + PyErr_Format(PyExc_TypeError, + "deque indices must be integers or slices, not %.200s", + index->ob_type->tp_name); + return NULL; +} + static int deque_del_item(dequeobject *deque, Py_ssize_t i) { @@ -1221,35 +1301,202 @@ static int deque_ass_item(dequeobject *deque, Py_ssize_t i, PyObject *v) { block *b; - Py_ssize_t n, len=Py_SIZE(deque), halflen=(len+1)>>1, index=i; - - if (!valid_index(i, len)) { + if (!valid_index(i, Py_SIZE(deque))) { PyErr_SetString(PyExc_IndexError, "deque index out of range"); return -1; } if (v == NULL) return deque_del_item(deque, i); - i += deque->leftindex; - n = (Py_ssize_t)((size_t) i / BLOCKLEN); - i = (Py_ssize_t)((size_t) i % BLOCKLEN); - if (index <= halflen) { - b = deque->leftblock; - while (n--) - b = b->rightlink; - } else { - n = (Py_ssize_t)( - ((size_t)(deque->leftindex + Py_SIZE(deque) - 1)) - / BLOCKLEN - n); - b = deque->rightblock; - while (n--) - b = b->leftlink; - } + b = deque_get_block(deque, &i); Py_INCREF(v); Py_SETREF(b->data[i], v); return 0; } +static int +deque_ass_subscr(dequeobject *deque, PyObject *index, PyObject *value) +{ + if (PyIndex_Check(index)) { + Py_ssize_t i = PyNumber_AsSsize_t(index, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return -1; + if (i < 0) + i += Py_SIZE(deque); + return deque_ass_item(deque, i, value); + } + + if (PySlice_Check(index)) { + Py_ssize_t start, stop, step, slicelen; + + if (PySlice_GetIndicesEx(index, + Py_SIZE(deque), + &start, &stop, &step, &slicelen) < 0) { + return -1; + } + if (value == NULL) { + /* delete slice */ + PyObject **garbage; + block *srcblock, *dstblock; + Py_ssize_t i, srcindex, dstindex; + + if (slicelen <= 0) + return 0; + + if (slicelen == Py_SIZE(deque)) { + deque_clear(deque); + return 0; + } + + assert((size_t)slicelen <= PY_SIZE_MAX / sizeof(PyObject*)); + garbage = (PyObject**) PyMem_MALLOC(slicelen * sizeof(PyObject*)); + if (!garbage) { + PyErr_NoMemory(); + return -1; + } + + /* Since we're deleting, the direction of the range doesn't matter. + */ + if (step < 0) { + stop = start + 1; + start = stop + step * (slicelen - 1) - 1; + step = -step; + } + else { + stop = start + 1 - step * (slicelen - 1); + } + /* Choose the direction that needs less moving. + */ + if (Py_SIZE(deque) - stop > start) { + srcindex = stop - 1; + srcblock = deque_get_block(deque, &srcindex); + dstblock = srcblock; + dstindex = srcindex; + i = 0; + while (1) { + garbage[i] = srcblock->data[srcindex]; + DECRPOS(srcblock, srcindex); + if (++i >= slicelen) + break; + COPY_RIGHT(dstblock, dstindex, srcblock, srcindex, step - 1); + } + COPY_RIGHT(dstblock, dstindex, srcblock, srcindex, start); + /* Free unused blocks */ + INCRPOS(dstblock, dstindex); + srcblock = deque->leftblock; + deque->leftblock = dstblock; + deque->leftindex = dstindex; + MARK_END(dstblock->leftlink); + while (srcblock != dstblock) { + block *prevblock = srcblock; + CHECK_NOT_END(srcblock->rightlink); + srcblock = srcblock->rightlink; + freeblock(prevblock); + } + } + else { + srcindex = start; + srcblock = deque_get_block(deque, &srcindex); + dstblock = srcblock; + dstindex = srcindex; + i = 0; + while (1) { + garbage[i] = srcblock->data[srcindex]; + INCRPOS(srcblock, srcindex); + if (++i >= slicelen) + break; + COPY_LEFT(dstblock, dstindex, srcblock, srcindex, step - 1); + } + COPY_LEFT(dstblock, dstindex, srcblock, srcindex, + Py_SIZE(deque) - (start + step * (slicelen - 1) + 1)); + /* Free unused blocks */ + DECRPOS(dstblock, dstindex); + srcblock = deque->rightblock; + deque->rightblock = dstblock; + deque->rightindex = dstindex; + MARK_END(dstblock->rightlink); + while (srcblock != dstblock) { + block *prevblock = srcblock; + CHECK_NOT_END(srcblock->leftlink); + srcblock = srcblock->leftlink; + freeblock(prevblock); + } + } + Py_SIZE(deque) -= slicelen; + + for (i = 0; i < slicelen; i++) { + Py_DECREF(garbage[i]); + } + PyMem_FREE(garbage); + + return 0; + } + else { + /* assign slice */ + PyObject *item, *seq; + PyObject **garbage, **seqitems; + Py_ssize_t i; + block *b; + + seq = PySequence_Fast(value, + "must assign iterable " + "to extended slice"); + if (!seq) + return -1; + + if (PySequence_Fast_GET_SIZE(seq) != slicelen) { + PyErr_Format(PyExc_ValueError, + "attempt to assign sequence of " + "size %zd to extended slice of " + "size %zd", + PySequence_Fast_GET_SIZE(seq), + slicelen); + Py_DECREF(seq); + return -1; + } + + if (!slicelen) { + Py_DECREF(seq); + return 0; + } + + garbage = (PyObject**) PyMem_MALLOC(slicelen * sizeof(PyObject*)); + if (!garbage) { + Py_DECREF(seq); + PyErr_NoMemory(); + return -1; + } + + b = deque_get_block(deque, &start); + seqitems = PySequence_Fast_ITEMS(seq); + i = 0; + while (1) { + garbage[i] = b->data[start]; + item = seqitems[i]; + Py_INCREF(item); + b->data[start] = item; + if (++i >= slicelen) + break; + CHANGEPOS(b, start, step); + } + + for (i = 0; i < slicelen; i++) { + Py_DECREF(garbage[i]); + } + + PyMem_FREE(garbage); + Py_DECREF(seq); + + return 0; + } + } + + PyErr_Format(PyExc_TypeError, + "deque indices must be integers or slices, not %.200s", + index->ob_type->tp_name); + return -1; +} + static void deque_dealloc(dequeobject *deque) { @@ -1518,6 +1765,12 @@ static PySequenceMethods deque_as_sequen (ssizeargfunc)deque_inplace_repeat, /* sq_inplace_repeat */ }; +static PyMappingMethods deque_as_mapping = { + (lenfunc) deque_len, /* mp_length */ + (binaryfunc) deque_subscr, /* mp_subscript */ + (objobjargproc) deque_ass_subscr, /* mp_ass_subscript */ +}; + static PyNumberMethods deque_as_number = { 0, /* nb_add */ 0, /* nb_subtract */ @@ -1596,7 +1849,7 @@ static PyTypeObject deque_type = { deque_repr, /* tp_repr */ &deque_as_number, /* tp_as_number */ &deque_as_sequence, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + &deque_as_mapping, /* tp_as_mapping */ PyObject_HashNotImplemented, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ @@ -1688,13 +1941,9 @@ dequeiter_next(dequeiterobject *it) it->index > it->deque->rightindex)); item = it->b->data[it->index]; - it->index++; it->counter--; - if (it->index == BLOCKLEN && it->counter > 0) { - CHECK_NOT_END(it->b->rightlink); - it->b = it->b->rightlink; - it->index = 0; - } + if (it->counter > 0) + INCRPOS(it->b, it->index); Py_INCREF(item); return item; } @@ -1830,13 +2079,9 @@ dequereviter_next(dequeiterobject *it) it->index < it->deque->leftindex)); item = it->b->data[it->index]; - it->index--; it->counter--; - if (it->index < 0 && it->counter > 0) { - CHECK_NOT_END(it->b->leftlink); - it->b = it->b->leftlink; - it->index = BLOCKLEN - 1; - } + if (it->counter > 0) + DECRPOS(it->b, it->index); Py_INCREF(item); return item; }