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,24 @@ 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]) + self.failUnlessEqual(p[3::-1], [4, 3, 2, 1]) + self.failUnlessEqual(p[:4:3], [1, 4]) c_int() self.failUnlessEqual(p[:4], [1, 2, 3, 4]) + self.failUnlessEqual(p[:4:], [1, 2, 3, 4]) + self.failUnlessEqual(p[3::-1], [4, 3, 2, 1]) + self.failUnlessEqual(p[:4:3], [1, 4]) p[2] = 96 self.failUnlessEqual(p[:4], [1, 2, 96, 4]) + self.failUnlessEqual(p[:4:], [1, 2, 96, 4]) + self.failUnlessEqual(p[3::-1], [4, 96, 2, 1]) + self.failUnlessEqual(p[:4:3], [1, 4]) c_int() self.failUnlessEqual(p[:4], [1, 2, 96, 4]) + self.failUnlessEqual(p[:4:], [1, 2, 96, 4]) + self.failUnlessEqual(p[3::-1], [4, 96, 2, 1]) + self.failUnlessEqual(p[:4:3], [1, 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,10 @@ self.failUnless(type(b[0]) is str) self.failUnlessEqual(b[0], "a") self.failUnlessEqual(b[:], "abc\0") + self.failUnlessEqual(b[::], "abc\0") + self.failUnlessEqual(b[::-1], "\0cba") + self.failUnlessEqual(b[::2], "ac") + self.failUnlessEqual(b[::5], "a") def test_string_conversion(self): b = create_string_buffer(u"abc") @@ -23,6 +27,10 @@ self.failUnless(type(b[0]) is str) self.failUnlessEqual(b[0], "a") self.failUnlessEqual(b[:], "abc\0") + self.failUnlessEqual(b[::], "abc\0") + self.failUnlessEqual(b[::-1], "\0cba") + self.failUnlessEqual(b[::2], "ac") + self.failUnlessEqual(b[::5], "a") try: c_wchar @@ -41,6 +49,10 @@ self.failUnless(type(b[0]) is unicode) self.failUnlessEqual(b[0], u"a") self.failUnlessEqual(b[:], "abc\0") + self.failUnlessEqual(b[::], "abc\0") + self.failUnlessEqual(b[::-1], "\0cba") + self.failUnlessEqual(b[::2], "ac") + self.failUnlessEqual(b[::5], "a") def test_unicode_conversion(self): b = create_unicode_buffer("abc") @@ -49,6 +61,10 @@ self.failUnless(type(b[0]) is unicode) self.failUnlessEqual(b[0], u"a") self.failUnlessEqual(b[:], "abc\0") + self.failUnlessEqual(b[::], "abc\0") + self.failUnlessEqual(b[::-1], "\0cba") + self.failUnlessEqual(b[::2], "ac") + self.failUnlessEqual(b[::5], "a") 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,10 @@ p = create_string_buffer("foo") sz = (c_char * 3).from_address(addressof(p)) self.failUnlessEqual(sz[:], "foo") + self.failUnlessEqual(sz[::], "foo") + self.failUnlessEqual(sz[::-1], "oof") + self.failUnlessEqual(sz[::3], "f") + self.failUnlessEqual(sz[1:4:2], "o") self.failUnlessEqual(sz.value, "foo") try: @@ -106,6 +110,10 @@ p = create_unicode_buffer("foo") sz = (c_wchar * 3).from_address(addressof(p)) self.failUnlessEqual(sz[:], "foo") + self.failUnlessEqual(sz[::], "foo") + self.failUnlessEqual(sz[::-1], "oof") + self.failUnlessEqual(sz[::3], "f") + self.failUnlessEqual(sz[1:4:2], "o") 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,13 @@ # 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)).a[::-1], [0, 0, 2, 1]) + self.failUnlessEqual(SomeInts((1, 2)).a[::2], [1, 0]) + self.failUnlessEqual(SomeInts((1, 2)).a[1:5:6], [2]) + self.failUnlessEqual(SomeInts((1, 2)).a[6:4:-1], []) 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,9 @@ 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[:2:-1] == "ba") + self.failUnless(c_string("ab", 4).raw[:2:2] == "a") 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,14 @@ 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]) + self.failUnlessEqual(cast(a, POINTER(c_byte))[6::-1], + [0, 102, 101, 100, 99, 98, 97]) + self.failUnlessEqual(cast(a, POINTER(c_byte))[:7:2], + [97, 99, 101, 0]) + self.failUnlessEqual(cast(a, POINTER(c_byte))[:7:7], + [97]) 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,19 @@ 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") + self.failUnlessEqual(buf[::-1], u"\0\uFFFD\uFFFD\uFFFDba") + self.failUnlessEqual(buf[::2], u"a\uFFFD\uFFFD") + self.failUnlessEqual(buf[6:5:-1], u"") 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") + self.failUnlessEqual(buf[::-1], u"\0\0\0\0ba") + self.failUnlessEqual(buf[::2], u"a\0\0") + self.failUnlessEqual(buf[6:5:-1], u"") import _ctypes_test func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p @@ -105,11 +113,17 @@ ctypes.set_conversion_mode("ascii", "replace") buf = ctypes.create_string_buffer(u"abäöü") self.failUnlessEqual(buf[:], "ab???\0") + self.failUnlessEqual(buf[::], "ab???\0") + self.failUnlessEqual(buf[::-1], "\0???ba") + self.failUnlessEqual(buf[::2], "a??") + self.failUnlessEqual(buf[6:5:-1], "") 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") + self.failUnlessEqual(buf[::-1], "\0\0\0\0ba") 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,22 @@ 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[::]) + self.failUnlessEqual(a[10::-1], b[10::-1]) + self.failUnlessEqual(a[30:20:-1], b[30:20:-1]) + self.failUnlessEqual(a[:12:6], b[:12:6]) + self.failUnlessEqual(a[2:6:4], b[2:6:4]) a[0:5] = range(5, 10) self.failUnlessEqual(a[0:5], range(5, 10)) + self.failUnlessEqual(a[0:5:], range(5, 10)) + self.failUnlessEqual(a[4::-1], range(9, 4, -1)) def test_setslice_cint(self): a = (c_int * 100)(*xrange(1100, 1200)) @@ -22,17 +31,36 @@ 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)) + a[46:31:-1] = range(232, 247) + self.failUnlessEqual(a[32:47:1], range(246, 231, -1)) + + a[32:47] = range(1132, 1147) + self.failUnlessEqual(a[:], b) + a[32:47:7] = range(3) + b[32:47:7] = range(3) + self.failUnlessEqual(a[:], b) + a[33::-3] = range(12) + b[33::-3] = range(12) + self.failUnlessEqual(a[:], b) - 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 +70,21 @@ dll.my_free.restype = None res = dll.my_strdup(s) self.failUnlessEqual(res[:len(s)], s) + self.failUnlessEqual(res[:len(s):], s) + self.failUnlessEqual(res[len(s)-1::-1], s[::-1]) + self.failUnlessEqual(res[len(s)-1:5:-7], s[:5:-7]) 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 +114,10 @@ p = (c_char * 27)(*s) self.failUnlessEqual(p[:], s) + self.failUnlessEqual(p[::], s) + self.failUnlessEqual(p[::-1], s[::-1]) + self.failUnlessEqual(p[5::-2], s[5::-2]) + self.failUnlessEqual(p[2:5:-3], s[2:5:-3]) try: @@ -96,10 +134,15 @@ dll.my_free.restype = None res = dll.my_wcsdup(s) self.failUnlessEqual(res[:len(s)], s) + self.failUnlessEqual(res[:len(s):], s) + self.failUnlessEqual(res[len(s)-1::-1], s[::-1]) + self.failUnlessEqual(res[len(s)-1:5:-7], s[:5:-7]) 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): @@ -111,7 +154,11 @@ else: return res = dll.my_wcsdup(s) - self.failUnlessEqual(res[:len(s)-1], range(ord("a"), ord("z")+1)) + tmpl = range(ord("a"), ord("z")+1) + self.failUnlessEqual(res[:len(s)-1], tmpl) + self.failUnlessEqual(res[:len(s)-1:], tmpl) + self.failUnlessEqual(res[len(s)-2::-1], tmpl[::-1]) + self.failUnlessEqual(res[len(s)-2:5:-7], tmpl[:5:-7]) dll.my_free(res) ################################################################ Index: Modules/_ctypes/_ctypes.c =================================================================== --- Modules/_ctypes/_ctypes.c (revision 57617) +++ Modules/_ctypes/_ctypes.c (working copy) @@ -3768,6 +3768,108 @@ 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; + PyObject *np; + Py_ssize_t start, stop, step, slicelen, cur, i; + + if (PySlice_GetIndicesEx((PySliceObject *)item, + self->b_length, &start, &stop, + &step, &slicelen) < 0) { + 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; + char *dest; + + if (slicelen <= 0) + return PyString_FromString(""); + if (step == 1) { + return PyString_FromStringAndSize(ptr + start, + slicelen); + } + dest = (char *)PyMem_Malloc(slicelen); + + if (dest == NULL) + return PyErr_NoMemory(); + + for (cur = start, i = 0; i < slicelen; + cur += step, i++) { + dest[i] = ptr[cur]; + } + + np = PyString_FromStringAndSize(dest, slicelen); + PyMem_Free(dest); + return np; + } +#ifdef CTYPES_UNICODE + if (itemdict->getfunc == getentry("u")->getfunc) { + wchar_t *ptr = (wchar_t *)self->b_ptr; + wchar_t *dest; + + if (slicelen <= 0) + return PyUnicode_FromUnicode(NULL, 0); + if (step == 1) { + return PyUnicode_FromWideChar(ptr + start, + slicelen); + } + + dest = (wchar_t *)PyMem_Malloc( + slicelen * sizeof(wchar_t)); + + for (cur = start, i = 0; i < slicelen; + cur += step, i++) { + dest[i] = ptr[cur]; + } + + np = PyUnicode_FromWideChar(dest, slicelen); + PyMem_Free(dest); + return np; + } +#endif + + np = PyList_New(slicelen); + if (np == NULL) + return NULL; + + for (cur = start, i = 0; i < slicelen; + cur += step, i++) { + PyObject *v = Array_item(_self, cur); + PyList_SET_ITEM(np, i, v); + } + return 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 +3941,63 @@ 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, cur; + + if (PySlice_GetIndicesEx((PySliceObject *)item, + self->b_length, &start, &stop, + &step, &slicelen) < 0) { + 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 (cur = start, i = 0; i < otherlen; cur += step, i++) { + PyObject *item = PySequence_GetItem(value, i); + int result; + if (item == NULL) + return -1; + result = Array_ass_item(_self, cur, 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 +4019,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 +4038,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 +4524,97 @@ 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)) { + Py_ssize_t start, stop, step; + PyObject *np; + StgDictObject *stgdict, *itemdict; + PyObject *proto; + Py_ssize_t i, len, cur; + + /* Since pointers have no length, we pretend the length is + PY_SSIZE_T_MAX.*/ + if (PySlice_GetIndicesEx((PySliceObject *)item, + PY_SSIZE_T_MAX, &start, &stop, + &step, &len) < 0) { + return NULL; + } + + stgdict = PyObject_stgdict((PyObject *)self); + assert(stgdict); /* Cannot be NULL for 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; + char *dest; + + if (len <= 0) + return PyString_FromString(""); + if (step == 1) { + return PyString_FromStringAndSize(ptr + start, + len); + } + dest = (char *)PyMem_Malloc(len); + if (dest == NULL) + return PyErr_NoMemory(); + for (cur = start, i = 0; i < len; cur += step, i++) { + dest[i] = ptr[cur]; + } + np = PyString_FromStringAndSize(dest, len); + PyMem_Free(dest); + return np; + } +#ifdef CTYPES_UNICODE + if (itemdict->getfunc == getentry("u")->getfunc) { + wchar_t *ptr = *(wchar_t **)self->b_ptr; + wchar_t *dest; + + if (len <= 0) + return PyUnicode_FromUnicode(NULL, 0); + if (step == 1) { + return PyUnicode_FromWideChar(ptr + start, + len); + } + dest = (wchar_t *)PyMem_Malloc(len * sizeof(wchar_t)); + if (dest == NULL) + return PyErr_NoMemory(); + for (cur = start, i = 0; i < len; cur += step, i++) { + dest[i] = ptr[cur]; + } + np = PyUnicode_FromWideChar(dest, len); + PyMem_Free(dest); + return np; + } +#endif + + np = PyList_New(len); + if (np == NULL) + return NULL; + + for (cur = start, i = 0; i < len; cur += step, i++) { + PyObject *v = Pointer_item(_self, cur); + PyList_SET_ITEM(np, i, v); + } + return 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 +4629,11 @@ 0, /* intargfunc sq_inplace_repeat; */ }; +static PyMappingMethods Pointer_as_mapping = { + 0, + Pointer_subscript, +}; + static int Pointer_nonzero(CDataObject *self) { @@ -4406,7 +4667,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 */