diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py index 23468dc8ae3..c58d94989c6 100644 --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -349,6 +349,25 @@ class StructureTestCase(unittest.TestCase): self.assertTrue("from_address" in dir(type(Structure))) self.assertTrue("in_dll" in dir(type(Structure))) + def test_positional_args(self): + # see also http://bugs.python.org/issue5042 + class W(Structure): + _fields_ = [("a", c_int), ("b", c_int)] + class X(W): + _fields_ = [("c", c_int)] + class Y(X): + pass + class Z(Y): + _fields_ = [("d", c_int), ("e", c_int), ("f", c_int)] + + z = Z(1, 2, 3, 4, 5, 6) + self.assertEqual((z.a, z.b, z.c, z.d, z.e, z.f), + (1, 2, 3, 4, 5, 6)) + z = Z(1) + self.assertEqual((z.a, z.b, z.c, z.d, z.e, z.f), + (1, 0, 0, 0, 0, 0)) + self.assertRaises(TypeError, lambda: Z(1, 2, 3, 4, 5, 6, 7)) + class PointerMemberTestCase(unittest.TestCase): def test(self): diff --git a/Misc/NEWS b/Misc/NEWS index ee97832db24..1a9bda8da12 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -72,6 +72,9 @@ C-API Library ------- +- Issue #5042: Structure sub-subclass does now initialize correctly + with base class positional arguments. + - Issue #6882: Import uuid creates zombies processes. - Issue #6635: Fix profiler printing usage message. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index a62b7165c5f..41264d33ad2 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3936,82 +3936,97 @@ IBUG(char *msg) return -1; } +/* + This function is called to initialize a Structure or Union with positional + arguments. It calls itself recursively for all Structure or Union base + classes, then retrieves the _fields_ member to associate the argument + position with the correct field name. + + Returns -1 on error, or the index of next argument on success. + */ +static int +_init_pos_args(PyObject *self, PyTypeObject *type, + PyObject *args, PyObject *kwds, + int index) +{ + StgDictObject *dict; + PyObject *fields; + int i; + + if (PyType_stgdict((PyObject *)type->tp_base)) { + index = _init_pos_args(self, type->tp_base, + args, kwds, + index); + if (index == -1) + return -1; + } + + dict = PyType_stgdict((PyObject *)type); + fields = PyDict_GetItemString((PyObject *)dict, "_fields_"); + if (fields == NULL) + return index; + + for (i = 0; + i < dict->length && (i+index) < PyTuple_GET_SIZE(args); + ++i) { + PyObject *pair = PySequence_GetItem(fields, i); + PyObject *name, *val; + int res; + if (!pair) + return -1; + name = PySequence_GetItem(pair, 0); + if (!name) { + Py_DECREF(pair); + return -1; + } + val = PyTuple_GET_ITEM(args, i + index); + if (kwds && PyDict_GetItem(kwds, name)) { + char *field = PyBytes_AsString(name); + if (field == NULL) { + PyErr_Clear(); + field = "???"; + } + PyErr_Format(PyExc_TypeError, + "duplicate values for field '%s'", + field); + Py_DECREF(pair); + Py_DECREF(name); + return -1; + } + + res = PyObject_SetAttr(self, name, val); + Py_DECREF(pair); + Py_DECREF(name); + if (res == -1) + return -1; + } + return index + dict->length; +} + static int Struct_init(PyObject *self, PyObject *args, PyObject *kwds) { - int i; - PyObject *fields; + StgDictObject *stgdict = PyObject_stgdict(self); /* Optimization possible: Store the attribute names _fields_[x][0] * in C accessible fields somewhere ? */ -/* Check this code again for correctness! */ - if (!PyTuple_Check(args)) { PyErr_SetString(PyExc_TypeError, "args not a tuple?"); return -1; } if (PyTuple_GET_SIZE(args)) { - fields = PyObject_GetAttrString(self, "_fields_"); - if (!fields) { - PyErr_Clear(); - fields = PyTuple_New(0); - if (!fields) - return -1; - } - - if (PyTuple_GET_SIZE(args) > PySequence_Length(fields)) { - Py_DECREF(fields); + int res = _init_pos_args(self, Py_TYPE(self), + args, kwds, 0); + if (res == -1) + return -1; + if (res < PyTuple_GET_SIZE(args)) { PyErr_SetString(PyExc_TypeError, "too many initializers"); return -1; } - - for (i = 0; i < PyTuple_GET_SIZE(args); ++i) { - PyObject *pair = PySequence_GetItem(fields, i); - PyObject *name; - PyObject *val; - if (!pair) { - Py_DECREF(fields); - return IBUG("_fields_[i] failed"); - } - - name = PySequence_GetItem(pair, 0); - if (!name) { - Py_DECREF(pair); - Py_DECREF(fields); - return IBUG("_fields_[i][0] failed"); - } - - if (kwds && PyDict_GetItem(kwds, name)) { - char *field = PyBytes_AsString(name); - if (field == NULL) { - PyErr_Clear(); - field = "???"; - } - PyErr_Format(PyExc_TypeError, - "duplicate values for field %s", - field); - Py_DECREF(pair); - Py_DECREF(name); - Py_DECREF(fields); - return -1; - } - - val = PyTuple_GET_ITEM(args, i); - if (-1 == PyObject_SetAttr(self, name, val)) { - Py_DECREF(pair); - Py_DECREF(name); - Py_DECREF(fields); - return -1; - } - - Py_DECREF(name); - Py_DECREF(pair); - } - Py_DECREF(fields); } if (kwds) {