summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Heller <theller@ctypes.org>2009-09-18 18:55:17 (GMT)
committerThomas Heller <theller@ctypes.org>2009-09-18 18:55:17 (GMT)
commit7a352c0ed8a12d868d9b682f4adf33f558d19855 (patch)
treee1f8936cff27bcce3b1cd9730092669a518fddd1
parentdb9925a56bd522aebe93afb7dc76800a58e59a07 (diff)
downloadcpython-7a352c0ed8a12d868d9b682f4adf33f558d19855.zip
cpython-7a352c0ed8a12d868d9b682f4adf33f558d19855.tar.gz
cpython-7a352c0ed8a12d868d9b682f4adf33f558d19855.tar.bz2
Issue #5042: Structure sub-subclass does now initialize correctly with
base class positional arguments.
-rw-r--r--Lib/ctypes/test/test_structures.py19
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/_ctypes/_ctypes.c131
3 files changed, 95 insertions, 58 deletions
diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py
index 168c3cd..32f87d8 100644
--- a/Lib/ctypes/test/test_structures.py
+++ b/Lib/ctypes/test/test_structures.py
@@ -355,6 +355,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.failUnlessEqual((z.a, z.b, z.c, z.d, z.e, z.f),
+ (1, 2, 3, 4, 5, 6))
+ z = Z(1)
+ self.failUnlessEqual((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 8f674c8..12815a8 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -376,6 +376,9 @@ Core and Builtins
Library
-------
+- Issue #5042: Structure sub-subclass does now initialize correctly
+ with base class positional arguments.
+
- Issue #6938: Fix a TypeError in string formatting of a multiprocessing
debug message.
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index 930ee95..a5c00cd 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -4017,82 +4017,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
-Struct_init(PyObject *self, PyObject *args, PyObject *kwds)
+_init_pos_args(PyObject *self, PyTypeObject *type,
+ PyObject *args, PyObject *kwds,
+ int index)
{
- int i;
+ 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 = PyString_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)
+{
+ 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 = PyString_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) {