Index: Lib/ctypes/test/test_cast.py =================================================================== --- Lib/ctypes/test/test_cast.py (revision 57617) +++ Lib/ctypes/test/test_cast.py (working copy) @@ -50,12 +50,16 @@ def test_other(self): p = cast((c_int * 4)(1, 2, 3, 4), POINTER(c_int)) self.failUnlessEqual(p[:4], [1,2, 3, 4]) + self.failUnlessEqual(p[:4:], [1,2, 3, 4]) c_int() self.failUnlessEqual(p[:4], [1, 2, 3, 4]) + self.failUnlessEqual(p[:4:], [1, 2, 3, 4]) p[2] = 96 self.failUnlessEqual(p[:4], [1, 2, 96, 4]) + self.failUnlessEqual(p[:4:], [1, 2, 96, 4]) c_int() self.failUnlessEqual(p[:4], [1, 2, 96, 4]) + self.failUnlessEqual(p[:4:], [1, 2, 96, 4]) def test_char_p(self): # This didn't work: bad argument to internal function Index: Lib/ctypes/test/test_buffers.py =================================================================== --- Lib/ctypes/test/test_buffers.py (revision 57617) +++ Lib/ctypes/test/test_buffers.py (working copy) @@ -15,6 +15,7 @@ self.failUnless(type(b[0]) is str) self.failUnlessEqual(b[0], "a") self.failUnlessEqual(b[:], "abc\0") + self.failUnlessEqual(b[::], "abc\0") def test_string_conversion(self): b = create_string_buffer(u"abc") @@ -23,6 +24,7 @@ self.failUnless(type(b[0]) is str) self.failUnlessEqual(b[0], "a") self.failUnlessEqual(b[:], "abc\0") + self.failUnlessEqual(b[::], "abc\0") try: c_wchar @@ -41,6 +43,7 @@ self.failUnless(type(b[0]) is unicode) self.failUnlessEqual(b[0], u"a") self.failUnlessEqual(b[:], "abc\0") + self.failUnlessEqual(b[::], "abc\0") def test_unicode_conversion(self): b = create_unicode_buffer("abc") @@ -49,6 +52,7 @@ self.failUnless(type(b[0]) is unicode) self.failUnlessEqual(b[0], u"a") self.failUnlessEqual(b[:], "abc\0") + self.failUnlessEqual(b[::], "abc\0") if __name__ == "__main__": unittest.main() Index: Lib/ctypes/test/test_arrays.py =================================================================== --- Lib/ctypes/test/test_arrays.py (revision 57617) +++ Lib/ctypes/test/test_arrays.py (working copy) @@ -95,6 +95,7 @@ p = create_string_buffer("foo") sz = (c_char * 3).from_address(addressof(p)) self.failUnlessEqual(sz[:], "foo") + self.failUnlessEqual(sz[::], "foo") self.failUnlessEqual(sz.value, "foo") try: @@ -106,6 +107,7 @@ p = create_unicode_buffer("foo") sz = (c_wchar * 3).from_address(addressof(p)) self.failUnlessEqual(sz[:], "foo") + self.failUnlessEqual(sz[::], "foo") self.failUnlessEqual(sz.value, "foo") if __name__ == '__main__': Index: Lib/ctypes/test/test_structures.py =================================================================== --- Lib/ctypes/test/test_structures.py (revision 57617) +++ Lib/ctypes/test/test_structures.py (working copy) @@ -236,7 +236,9 @@ # can use tuple to initialize array (but not list!) self.failUnlessEqual(SomeInts((1, 2)).a[:], [1, 2, 0, 0]) + self.failUnlessEqual(SomeInts((1, 2)).a[::], [1, 2, 0, 0]) self.failUnlessEqual(SomeInts((1, 2, 3, 4)).a[:], [1, 2, 3, 4]) + self.failUnlessEqual(SomeInts((1, 2, 3, 4)).a[::], [1, 2, 3, 4]) # too long # XXX Should raise ValueError?, not RuntimeError self.assertRaises(RuntimeError, SomeInts, (1, 2, 3, 4, 5)) Index: Lib/ctypes/test/test_strings.py =================================================================== --- Lib/ctypes/test/test_strings.py (revision 57617) +++ Lib/ctypes/test/test_strings.py (working copy) @@ -121,6 +121,7 @@ def XX_test_initialized_strings(self): self.failUnless(c_string("ab", 4).raw[:2] == "ab") + self.failUnless(c_string("ab", 4).raw[:2:] == "ab") self.failUnless(c_string("ab", 4).raw[-1] == "\000") self.failUnless(c_string("ab", 2).raw == "a\000") Index: Lib/ctypes/test/test_memfunctions.py =================================================================== --- Lib/ctypes/test/test_memfunctions.py (revision 57617) +++ Lib/ctypes/test/test_memfunctions.py (working copy) @@ -30,6 +30,8 @@ self.failUnlessEqual(cast(a, c_char_p).value, "abcdef") self.failUnlessEqual(cast(a, POINTER(c_byte))[:7], [97, 98, 99, 100, 101, 102, 0]) + self.failUnlessEqual(cast(a, POINTER(c_byte))[:7:], + [97, 98, 99, 100, 101, 102, 0]) def test_string_at(self): s = string_at("foo bar") Index: Lib/ctypes/test/test_unicode.py =================================================================== --- Lib/ctypes/test/test_unicode.py (revision 57617) +++ Lib/ctypes/test/test_unicode.py (working copy) @@ -59,11 +59,13 @@ ctypes.set_conversion_mode("ascii", "replace") buf = ctypes.create_unicode_buffer("abäöü") self.failUnlessEqual(buf[:], u"ab\uFFFD\uFFFD\uFFFD\0") + self.failUnlessEqual(buf[::], u"ab\uFFFD\uFFFD\uFFFD\0") ctypes.set_conversion_mode("ascii", "ignore") buf = ctypes.create_unicode_buffer("abäöü") # is that correct? not sure. But with 'ignore', you get what you pay for.. self.failUnlessEqual(buf[:], u"ab\0\0\0\0") + self.failUnlessEqual(buf[::], u"ab\0\0\0\0") import _ctypes_test func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p @@ -105,11 +107,13 @@ ctypes.set_conversion_mode("ascii", "replace") buf = ctypes.create_string_buffer(u"abäöü") self.failUnlessEqual(buf[:], "ab???\0") + self.failUnlessEqual(buf[::], "ab???\0") ctypes.set_conversion_mode("ascii", "ignore") buf = ctypes.create_string_buffer(u"abäöü") # is that correct? not sure. But with 'ignore', you get what you pay for.. self.failUnlessEqual(buf[:], "ab\0\0\0\0") + self.failUnlessEqual(buf[::], "ab\0\0\0\0") if __name__ == '__main__': unittest.main() Index: Lib/ctypes/test/test_slicing.py =================================================================== --- Lib/ctypes/test/test_slicing.py (revision 57617) +++ Lib/ctypes/test/test_slicing.py (working copy) @@ -8,13 +8,17 @@ a = (c_int * 100)(*xrange(1100, 1200)) b = range(1100, 1200) self.failUnlessEqual(a[0:2], b[0:2]) + self.failUnlessEqual(a[0:2:], b[0:2:]) self.failUnlessEqual(len(a), len(b)) self.failUnlessEqual(a[5:7], b[5:7]) + self.failUnlessEqual(a[5:7:], b[5:7:]) self.failUnlessEqual(a[-1], b[-1]) self.failUnlessEqual(a[:], b[:]) + self.failUnlessEqual(a[::], b[::]) a[0:5] = range(5, 10) self.failUnlessEqual(a[0:5], range(5, 10)) + self.failUnlessEqual(a[0:5:], range(5, 10)) def test_setslice_cint(self): a = (c_int * 100)(*xrange(1100, 1200)) @@ -22,17 +26,25 @@ a[32:47] = range(32, 47) self.failUnlessEqual(a[32:47], range(32, 47)) + a[32:47] = range(132, 147) + self.failUnlessEqual(a[32:47:], range(132, 147)) - from operator import setslice + from operator import setslice, setitem # TypeError: int expected instead of str instance self.assertRaises(TypeError, setslice, a, 0, 5, "abcde") + self.assertRaises(TypeError, setitem, a, slice(0, 5), "abcde") # TypeError: int expected instead of str instance self.assertRaises(TypeError, setslice, a, 0, 5, ["a", "b", "c", "d", "e"]) + self.assertRaises(TypeError, setitem, a, slice(0, 5), + ["a", "b", "c", "d", "e"]) # TypeError: int expected instead of float instance self.assertRaises(TypeError, setslice, a, 0, 5, [1, 2, 3, 4, 3.14]) + self.assertRaises(TypeError, setitem, a, slice(0, 5), + [1, 2, 3, 4, 3.14]) # ValueError: Can only assign sequence of same size self.assertRaises(ValueError, setslice, a, 0, 5, range(32)) + self.assertRaises(ValueError, setitem, a, slice(0, 5), range(32)) def test_char_ptr(self): s = "abcdefghijklmnopqrstuvwxyz" @@ -42,15 +54,19 @@ dll.my_free.restype = None res = dll.my_strdup(s) self.failUnlessEqual(res[:len(s)], s) + self.failUnlessEqual(res[:len(s):], s) import operator self.assertRaises(TypeError, operator.setslice, res, 0, 5, u"abcde") + self.assertRaises(TypeError, operator.setitem, + res, slice(0, 5), u"abcde") dll.my_free(res) dll.my_strdup.restype = POINTER(c_byte) res = dll.my_strdup(s) self.failUnlessEqual(res[:len(s)], range(ord("a"), ord("z")+1)) + self.failUnlessEqual(res[:len(s):], range(ord("a"), ord("z")+1)) dll.my_free(res) def test_char_ptr_with_free(self): @@ -80,6 +96,7 @@ p = (c_char * 27)(*s) self.failUnlessEqual(p[:], s) + self.failUnlessEqual(p[::], s) try: @@ -96,10 +113,13 @@ dll.my_free.restype = None res = dll.my_wcsdup(s) self.failUnlessEqual(res[:len(s)], s) + self.failUnlessEqual(res[:len(s):], s) import operator self.assertRaises(TypeError, operator.setslice, res, 0, 5, u"abcde") + self.assertRaises(TypeError, operator.setitem, + res, slice(0, 5), u"abcde") dll.my_free(res) if sizeof(c_wchar) == sizeof(c_short): @@ -112,6 +132,7 @@ return res = dll.my_wcsdup(s) self.failUnlessEqual(res[:len(s)-1], range(ord("a"), ord("z")+1)) + self.failUnlessEqual(res[:len(s)-1:], range(ord("a"), ord("z")+1)) dll.my_free(res) ################################################################ Index: Modules/_ctypes/_ctypes.c =================================================================== --- Modules/_ctypes/_ctypes.c (revision 57617) +++ Modules/_ctypes/_ctypes.c (working copy) @@ -3768,6 +3768,74 @@ return (PyObject *)np; } +static PyObject * +Array_subscript(PyObject *_self, PyObject *item) +{ + CDataObject *self = (CDataObject *)_self; + + 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->b_length; + return Array_item(_self, i); + } + else if PySlice_Check(item) { + StgDictObject *stgdict, *itemdict; + PyObject *proto; + PyListObject *np; + Py_ssize_t start, stop, step, slicelen, i; + + if (PySlice_GetIndicesEx((PySliceObject *)item, + self->b_length, &start, &stop, + &step, &slicelen) < 0) { + return NULL; + } + if (step != 1) { + PyErr_Format(PyExc_TypeError, "slice-steps other " + "than 1 not supported (%zd given)", step); + return NULL; + } + + stgdict = PyObject_stgdict((PyObject *)self); + assert(stgdict); /* Cannot be NULL for array object instances */ + proto = stgdict->proto; + itemdict = PyType_stgdict(proto); + assert(itemdict); /* proto is the item type of the array, a + ctypes type, so this cannot be NULL */ + + if (itemdict->getfunc == getentry("c")->getfunc) { + char *ptr = (char *)self->b_ptr; + return PyString_FromStringAndSize(ptr + start, + slicelen); +#ifdef CTYPES_UNICODE + } else if (itemdict->getfunc == getentry("u")->getfunc) { + wchar_t *ptr = (wchar_t *)self->b_ptr; + return PyUnicode_FromWideChar(ptr + start, + slicelen); +#endif + } + + np = (PyListObject *) PyList_New(slicelen); + if (np == NULL) + return NULL; + + for (i = 0; i < slicelen; i++) { + PyObject *v = Array_item(_self, i+start); + PyList_SET_ITEM(np, i, v); + } + return (PyObject *)np; + } + else { + PyErr_SetString(PyExc_TypeError, + "indices must be integers"); + return NULL; + } + +} + static int Array_ass_item(PyObject *_self, Py_ssize_t index, PyObject *value) { @@ -3839,6 +3907,69 @@ return 0; } +static int +Array_ass_subscript(PyObject *_self, PyObject *item, PyObject *value) +{ + CDataObject *self = (CDataObject *)_self; + + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "Array does not support item deletion"); + return -1; + } + + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + + if (i == -1 && PyErr_Occurred()) + return -1; + if (i < 0) + i += self->b_length; + return Array_ass_item(_self, i, value); + } + else if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelen, otherlen, i; + + if (PySlice_GetIndicesEx((PySliceObject *)item, + self->b_length, &start, &stop, + &step, &slicelen) < 0) { + return -1; + } + /* XXX(twouters) fix this */ + if (step != 1) { + PyErr_Format(PyExc_TypeError, "slice-steps other " + "than 1 not supported (%zd given)", step); + return -1; + } + if ((step < 0 && start < stop) || + (step > 0 && start > stop)) + stop = start; + + otherlen = PySequence_Length(value); + if (otherlen != slicelen) { + PyErr_SetString(PyExc_ValueError, + "Can only assign sequence of same size"); + return -1; + } + for (i = 0; i < otherlen; i++) { + PyObject *item = PySequence_GetItem(value, i); + int result; + if (item == NULL) + return -1; + result = Array_ass_item(_self, i+start, item); + Py_DECREF(item); + if (result == -1) + return -1; + } + return 0; + } + else { + PyErr_SetString(PyExc_TypeError, + "indices must be integer"); + return -1; + } +} + static Py_ssize_t Array_length(PyObject *_self) { @@ -3860,6 +3991,12 @@ 0, /* sq_inplace_repeat; */ }; +static PyMappingMethods Array_as_mapping = { + Array_length, + Array_subscript, + Array_ass_subscript, +}; + PyTypeObject Array_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_ctypes.Array", @@ -3873,7 +4010,7 @@ 0, /* tp_repr */ 0, /* tp_as_number */ &Array_as_sequence, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + &Array_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ @@ -4359,6 +4496,94 @@ return (PyObject *)np; } +static PyObject * +Pointer_subscript(PyObject *_self, PyObject *item) +{ + CDataObject *self = (CDataObject *)_self; + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return NULL; + return Pointer_item(_self, i); + } + else if (PySlice_Check(item)) { + /* Since pointers have no length, we cannot use any + of the PySlice API functions, and have to fiddle + with the attributes directly */ + PySliceObject *slice = (PySliceObject *)item; + Py_ssize_t start, stop; + PyListObject *np; + StgDictObject *stgdict, *itemdict; + PyObject *proto; + Py_ssize_t i, len; + + if (slice->step != Py_None) { + Py_ssize_t step = PyNumber_AsSsize_t(slice->step, + PyExc_IndexError); + if (step == -1 && PyErr_Occurred()) + return NULL; + if (step != 1) { + PyErr_Format(PyExc_TypeError, + "unsupported step size %zd", + step); + return NULL; + } + } + if (slice->start == Py_None) + start = 0; + else { + start = PyNumber_AsSsize_t(slice->start, + PyExc_IndexError); + if (start == -1 && PyErr_Occurred()) + return NULL; + if (start < 0) + start = 0; + } + if (slice->stop == Py_None) + stop = PY_SSIZE_T_MAX; + else { + stop = PyNumber_AsSsize_t(slice->stop, + PyExc_IndexError); + if (stop == -1 && PyErr_Occurred()) + return NULL; + } + if (stop < start) + stop = start; + len = stop - start; + + stgdict = PyObject_stgdict((PyObject *)self); + assert(stgdict); /* Cannot be NULL fr pointer instances */ + proto = stgdict->proto; + assert(proto); + itemdict = PyType_stgdict(proto); + assert(itemdict); + if (itemdict->getfunc == getentry("c")->getfunc) { + char *ptr = *(char **)self->b_ptr; + return PyString_FromStringAndSize(ptr + start, len); +#ifdef CTYPES_UNICODE + } else if (itemdict->getfunc == getentry("u")->getfunc) { + wchar_t *ptr = *(wchar_t **)self->b_ptr; + return PyUnicode_FromWideChar(ptr + start, len); +#endif + } + + np = (PyListObject *) PyList_New(len); + if (np == NULL) + return NULL; + + for (i = 0; i < len; i++) { + PyObject *v = Pointer_item(_self, i+start); + PyList_SET_ITEM(np, i, v); + } + return (PyObject *)np; + } + else { + PyErr_SetString(PyExc_TypeError, + "Pointer indices must be integer"); + return NULL; + } +} + static PySequenceMethods Pointer_as_sequence = { 0, /* inquiry sq_length; */ 0, /* binaryfunc sq_concat; */ @@ -4373,6 +4598,11 @@ 0, /* intargfunc sq_inplace_repeat; */ }; +static PyMappingMethods Pointer_as_mapping = { + 0, + Pointer_subscript, +}; + static int Pointer_nonzero(CDataObject *self) { @@ -4406,7 +4636,7 @@ 0, /* tp_repr */ &Pointer_as_number, /* tp_as_number */ &Pointer_as_sequence, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + &Pointer_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */