summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2022-08-03 17:56:24 (GMT)
committerGitHub <noreply@github.com>2022-08-03 17:56:24 (GMT)
commit906e4509328917fe9951f85457897f6a841e0529 (patch)
tree0ee26881b28d07bc0ec00ae72e3af3f8f134f8a6
parent89f52293281b6efc4ef666ef25e677683821f4b9 (diff)
downloadcpython-906e4509328917fe9951f85457897f6a841e0529.zip
cpython-906e4509328917fe9951f85457897f6a841e0529.tar.gz
cpython-906e4509328917fe9951f85457897f6a841e0529.tar.bz2
GH-92678: Fix tp_dictoffset inheritance. (GH-95596)
* Add test for inheriting explicit __dict__ and weakref. * Restore 3.10 behavior for multiple inheritance of C extension classes that store their dictionary at the end of the struct.
-rw-r--r--Lib/test/test_capi.py19
-rw-r--r--Misc/NEWS.d/next/C API/2022-08-03-14-39-08.gh-issue-92678.ozFTEx.rst2
-rw-r--r--Modules/_testcapi/heaptype.c28
-rw-r--r--Objects/typeobject.c19
4 files changed, 65 insertions, 3 deletions
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index a88a17d..013229a 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -619,6 +619,25 @@ class CAPITest(unittest.TestCase):
with self.assertRaisesRegex(TypeError, msg):
t = _testcapi.pytype_fromspec_meta(_testcapi.HeapCTypeMetaclassCustomNew)
+ def test_multiple_inheritance_ctypes_with_weakref_or_dict(self):
+
+ class Both1(_testcapi.HeapCTypeWithWeakref, _testcapi.HeapCTypeWithDict):
+ pass
+ class Both2(_testcapi.HeapCTypeWithDict, _testcapi.HeapCTypeWithWeakref):
+ pass
+
+ for cls in (_testcapi.HeapCTypeWithDict, _testcapi.HeapCTypeWithDict2,
+ _testcapi.HeapCTypeWithWeakref, _testcapi.HeapCTypeWithWeakref2):
+ for cls2 in (_testcapi.HeapCTypeWithDict, _testcapi.HeapCTypeWithDict2,
+ _testcapi.HeapCTypeWithWeakref, _testcapi.HeapCTypeWithWeakref2):
+ if cls is not cls2:
+ class S(cls, cls2):
+ pass
+ class B1(Both1, cls):
+ pass
+ class B2(Both1, cls):
+ pass
+
def test_pytype_fromspec_with_repeated_slots(self):
for variant in range(2):
with self.subTest(variant=variant):
diff --git a/Misc/NEWS.d/next/C API/2022-08-03-14-39-08.gh-issue-92678.ozFTEx.rst b/Misc/NEWS.d/next/C API/2022-08-03-14-39-08.gh-issue-92678.ozFTEx.rst
new file mode 100644
index 0000000..6bf3d4b
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2022-08-03-14-39-08.gh-issue-92678.ozFTEx.rst
@@ -0,0 +1,2 @@
+Restore the 3.10 behavior for multiple inheritance of C extension classes
+that store their dictionary at the end of the struct.
diff --git a/Modules/_testcapi/heaptype.c b/Modules/_testcapi/heaptype.c
index 9fb0051..12889e8 100644
--- a/Modules/_testcapi/heaptype.c
+++ b/Modules/_testcapi/heaptype.c
@@ -737,6 +737,14 @@ static PyType_Spec HeapCTypeWithDict_spec = {
HeapCTypeWithDict_slots
};
+static PyType_Spec HeapCTypeWithDict2_spec = {
+ "_testcapi.HeapCTypeWithDict2",
+ sizeof(HeapCTypeWithDictObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ HeapCTypeWithDict_slots
+};
+
static struct PyMemberDef heapctypewithnegativedict_members[] = {
{"dictobj", T_OBJECT, offsetof(HeapCTypeWithDictObject, dict)},
{"__dictoffset__", T_PYSSIZET, -(Py_ssize_t)sizeof(void*), READONLY},
@@ -796,6 +804,14 @@ static PyType_Spec HeapCTypeWithWeakref_spec = {
HeapCTypeWithWeakref_slots
};
+static PyType_Spec HeapCTypeWithWeakref2_spec = {
+ "_testcapi.HeapCTypeWithWeakref2",
+ sizeof(HeapCTypeWithWeakrefObject),
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ HeapCTypeWithWeakref_slots
+};
+
PyDoc_STRVAR(heapctypesetattr__doc__,
"A heap type without GC, but with overridden __setattr__.\n\n"
"The 'value' attribute is set to 10 in __init__ and updated via attribute setting.");
@@ -919,6 +935,12 @@ _PyTestCapi_Init_Heaptype(PyObject *m) {
}
PyModule_AddObject(m, "HeapCTypeWithDict", HeapCTypeWithDict);
+ PyObject *HeapCTypeWithDict2 = PyType_FromSpec(&HeapCTypeWithDict2_spec);
+ if (HeapCTypeWithDict2 == NULL) {
+ return -1;
+ }
+ PyModule_AddObject(m, "HeapCTypeWithDict2", HeapCTypeWithDict2);
+
PyObject *HeapCTypeWithNegativeDict = PyType_FromSpec(&HeapCTypeWithNegativeDict_spec);
if (HeapCTypeWithNegativeDict == NULL) {
return -1;
@@ -931,6 +953,12 @@ _PyTestCapi_Init_Heaptype(PyObject *m) {
}
PyModule_AddObject(m, "HeapCTypeWithWeakref", HeapCTypeWithWeakref);
+ PyObject *HeapCTypeWithWeakref2 = PyType_FromSpec(&HeapCTypeWithWeakref2_spec);
+ if (HeapCTypeWithWeakref2 == NULL) {
+ return -1;
+ }
+ PyModule_AddObject(m, "HeapCTypeWithWeakref2", HeapCTypeWithWeakref2);
+
PyObject *HeapCTypeWithBuffer = PyType_FromSpec(&HeapCTypeWithBuffer_spec);
if (HeapCTypeWithBuffer == NULL) {
return -1;
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index d33befc0..1980fcb 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -2316,6 +2316,11 @@ best_base(PyObject *bases)
return base;
}
+#define ADDED_FIELD_AT_OFFSET(name, offset) \
+ (type->tp_ ## name && (base->tp_ ##name == 0) && \
+ type->tp_ ## name + sizeof(PyObject *) == (offset) && \
+ type->tp_flags & Py_TPFLAGS_HEAPTYPE)
+
static int
extra_ivars(PyTypeObject *type, PyTypeObject *base)
{
@@ -2328,10 +2333,18 @@ extra_ivars(PyTypeObject *type, PyTypeObject *base)
return t_size != b_size ||
type->tp_itemsize != base->tp_itemsize;
}
- if (type->tp_weaklistoffset && base->tp_weaklistoffset == 0 &&
- type->tp_weaklistoffset + sizeof(PyObject *) == t_size &&
- type->tp_flags & Py_TPFLAGS_HEAPTYPE)
+ /* Check for __dict__ and __weakrefs__ slots in either order */
+ if (ADDED_FIELD_AT_OFFSET(weaklistoffset, t_size)) {
+ t_size -= sizeof(PyObject *);
+ }
+ if ((type->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0 &&
+ ADDED_FIELD_AT_OFFSET(dictoffset, t_size)) {
t_size -= sizeof(PyObject *);
+ }
+ /* Check __weakrefs__ again, in case it precedes __dict__ */
+ if (ADDED_FIELD_AT_OFFSET(weaklistoffset, t_size)) {
+ t_size -= sizeof(PyObject *);
+ }
return t_size != b_size;
}