diff -r dc38c4d65cd9 Lib/test/test_list.py --- a/Lib/test/test_list.py Sun Mar 20 04:58:29 2011 +0100 +++ b/Lib/test/test_list.py Sun Mar 20 13:19:13 2011 +0100 @@ -59,6 +59,161 @@ self.assertRaises((MemoryError, OverflowError), mul, lst, n) self.assertRaises((MemoryError, OverflowError), imul, lst, n) + def test_capi(self): + # issue10977: concrete object C API shouldn't bypass methods defined on subclasses + support.import_module('ctypes') + from ctypes import pythonapi, py_object + PyList_SetItem = pythonapi.PyList_SetItem + PyList_Insert = pythonapi.PyList_Insert + PyList_Append = pythonapi.PyList_Append + PyList_SetSlice = pythonapi.PyList_SetSlice + PyList_Sort = pythonapi.PyList_Sort + PyList_Reverse = pythonapi.PyList_Reverse + _PyList_Extend = pythonapi._PyList_Extend + + # PyList_SetItem: + l = MyList([1, 2, 3, None, 4, None]) + self.assertEqual(2, l.n) + PyList_SetItem(py_object(l), 3, py_object(0)) + self.assertEqual(0, l[3]) + self.assertEqual(1, l.n) + PyList_SetItem(py_object(l), -1, py_object(0)) + self.assertEqual(0, l[-1]) + self.assertEqual(0, l.n) + PyList_SetItem(py_object(l), 0, py_object(None)) + self.assertIs(None, l[0]) + self.assertEqual(1, l.n) + + # PyList_Insert: + l = MyList([1, 2, None]) + self.assertEqual(1, l.n) + PyList_Insert(py_object(l), 1, py_object(None)) + self.assertEqual([1, None, 2, None], l) + self.assertEqual(2, l.n) + + # PyList_Append: + PyList_Append(py_object(l), py_object(None)) + self.assertEqual([1, None, 2, None, None], l) + self.assertEqual(3, l.n) + + # PyList_SetSlice: + l = MyList([1, 2, None]) + self.assertEqual(1, l.n) + PyList_SetSlice(py_object(l), 0, 2, py_object([None])) + self.assertEqual([None, None], l) + self.assertEqual(2, l.n) + PyList_SetSlice(py_object(l), 0, 1, py_object()) # NULL -> slice deletion + self.assertEqual([None], l) + self.assertEqual(1, l.n) + + # PyList_Sort: + l = MyList([3, 2, 1]) + self.assertEqual(0, l.i) + PyList_Sort(py_object(l)) + self.assertEqual([1, 2, 3], l) + self.assertEqual(1, l.i) + + # PyList_Reverse: + l = MyList([1, None, 2]) + self.assertEqual(0, l.i) + PyList_Reverse(py_object(l)) + self.assertEqual([2, None, 1], l) + self.assertEqual(1, l.i) + + # _PyList_Extend: + l = MyList([1, None, 2]) + self.assertEqual(1, l.n) + _PyList_Extend(py_object(l), py_object([None, None])) + self.assertEqual([1, None, 2, None, None], l) + self.assertEqual(3, l.n) + + +class MyList(list): + + """list subclass to test the handling of subclasses.""" + + def __init__(self, iterable): + super().__init__(iterable) + # state that should be updated correctly: + self.n = self.count(None) + self.i = 0 + + def __delitem__(self, idx): + if isinstance(idx, slice): + self._delslice(idx) + else: + if self[idx] is None: + self.n -= 1 + super().__delitem__(idx) + + def _delslice(self, slic): + self.n -= self[slic].count(None) + super().__delitem__(slic) + + def __iadd__(self, iterable): # currently not used by the C API + items = list(iterable) + self.n += items.count(None) + return super().__iadd__(items) + + def __imul__(self, i): # currently not used by the C API + if i > 0: + self.n *= i + else: + self.n = 0 + return super().__imul__(i) + + def __setitem__(self, idx, value): + if isinstance(idx, slice): + self._setslice(idx, value) + else: + if (self[idx] is None) and (value is not None): + self.n -= 1 + elif (self[idx] is not None) and (value is None): + self.n += 1 + super().__setitem__(idx, value) + + def _setslice(self, slic, iterable): + items = list(iterable) + n = self[slic].count(None) + m = items.count(None) + self.n += (-n + m) + super().__setitem__(slic, items) + + def append(self, value): + if value is None: + self.n += 1 + super().append(value) + + def extend(self, iterable): + items = list(iterable) + self.n += items.count(None) + super().extend(items) + + def insert(self, idx, value): + if value is None: + self.n += 1 + super().insert(idx, value) + + def pop(self): # currently not used by the C API + v = super().pop() + if v is None: + self.n -= 1 + return v + + def remove(self, value): # currently not used by the C API + super().remove(value) + if value is None: + self.n -= 1 + + def reverse(self): + super().reverse() + self.i += 1 # just to detect if this method was called + + def sort(self, key=None, reverse=False): + super().sort(key=key, reverse=reverse) + self.i += 1 # just to detect if this method was called + + def test_main(verbose=None): support.run_unittest(ListTest) diff -r dc38c4d65cd9 Objects/listobject.c --- a/Objects/listobject.c Sun Mar 20 04:58:29 2011 +0100 +++ b/Objects/listobject.c Sun Mar 20 13:19:13 2011 +0100 @@ -206,17 +206,31 @@ PyErr_BadInternalCall(); return -1; } - if (i < 0 || i >= Py_SIZE(op)) { - Py_XDECREF(newitem); - PyErr_SetString(PyExc_IndexError, - "list assignment index out of range"); - return -1; + if (PyList_CheckExact(op)) { + if (i < 0 || i >= Py_SIZE(op)) { + Py_XDECREF(newitem); + PyErr_SetString(PyExc_IndexError, + "list assignment index out of range"); + return -1; + } + p = ((PyListObject *)op) -> ob_item + i; + olditem = *p; + *p = newitem; + Py_XDECREF(olditem); + return 0; } - p = ((PyListObject *)op) -> ob_item + i; - olditem = *p; - *p = newitem; - Py_XDECREF(olditem); - return 0; + else { + int res; + PyObject *idx = PyLong_FromSsize_t(i); + if (idx == NULL) { + Py_XDECREF(newitem); + return -1; + } + res = PyObject_SetItem(op, idx, newitem); + Py_XDECREF(newitem); /* PyObject_SetItem doesn't steal a reference */ + Py_DECREF(idx); + return res; + } } static int @@ -259,7 +273,17 @@ PyErr_BadInternalCall(); return -1; } - return ins1((PyListObject *)op, where, newitem); + if (PyList_CheckExact(op)) { + return ins1((PyListObject *)op, where, newitem); + } + else { + op = PyObject_CallMethod(op, "insert", "nO", where, newitem); + if (op == NULL) { + return -1; + } + Py_DECREF(op); + return 0; + } } static int @@ -285,8 +309,19 @@ int PyList_Append(PyObject *op, PyObject *newitem) { - if (PyList_Check(op) && (newitem != NULL)) - return app1((PyListObject *)op, newitem); + if (PyList_Check(op) && (newitem != NULL)) { + if (PyList_CheckExact(op)) { + return app1((PyListObject *)op, newitem); + } + else { + op = PyObject_CallMethod(op, "append", "(O)", newitem); + if (op == NULL) { + return -1; + } + Py_DECREF(op); + return 0; + } + } PyErr_BadInternalCall(); return -1; } @@ -674,7 +709,17 @@ PyErr_BadInternalCall(); return -1; } - return list_ass_slice((PyListObject *)a, ilow, ihigh, v); + if (PyList_CheckExact(a)) { + return list_ass_slice((PyListObject *)a, ilow, ihigh, v); + } + else { + if (v == NULL) { + return PySequence_DelSlice(a, ilow, ihigh); + } + else { + return PySequence_SetSlice(a, ilow, ihigh, v); + } + } } static PyObject * @@ -879,7 +924,12 @@ PyObject * _PyList_Extend(PyListObject *self, PyObject *b) { - return listextend(self, b); + if (PyList_CheckExact(self)) { + return listextend(self, b); + } + else { + return PyObject_CallMethod((PyObject *)self, "extend", "(O)", b); + } } static PyObject * @@ -2074,9 +2124,15 @@ PyErr_BadInternalCall(); return -1; } - v = listsort((PyListObject *)v, (PyObject *)NULL, (PyObject *)NULL); - if (v == NULL) + if (PyList_CheckExact(v)) { + v = listsort((PyListObject *)v, (PyObject *)NULL, (PyObject *)NULL); + } + else { + v = PyObject_CallMethod(v, "sort", NULL); + } + if (v == NULL) { return -1; + } Py_DECREF(v); return 0; } @@ -2098,9 +2154,19 @@ PyErr_BadInternalCall(); return -1; } - if (Py_SIZE(self) > 1) - reverse_slice(self->ob_item, self->ob_item + Py_SIZE(self)); - return 0; + if (PyList_CheckExact(v)) { + if (Py_SIZE(self) > 1) + reverse_slice(self->ob_item, self->ob_item + Py_SIZE(self)); + return 0; + } + else { + v = PyObject_CallMethod(v, "reverse", NULL); + if (v == NULL) { + return -1; + } + Py_DECREF(v); + return 0; + } } PyObject *