From 8baced5fd074bc963e3cc12cd5203bd554e6d873 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 3 Aug 2022 14:20:16 +0100 Subject: [PATCH 1/6] Add failing test for inheriting explicit __dict__ and weakref. --- Lib/test/test_capi.py | 19 +++++++++++++++++++ Modules/_testcapi/heaptype.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index a88a17d3c55788..013229a6cdc97a 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -619,6 +619,25 @@ def test_heaptype_with_custom_metaclass(self): 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/Modules/_testcapi/heaptype.c b/Modules/_testcapi/heaptype.c index 9fb0051ca125e4..12889e825d5581 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; From a95417af922901be36a1c38dabcd3737a7db4511 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 3 Aug 2022 14:37:23 +0100 Subject: [PATCH 2/6] Restore 3.10 for multiple inheritance of C extension classes that store their dictionary at the end of the struct. --- Objects/typeobject.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index d33befc05d7ab8..280df5be02cb48 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,16 @@ 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) + 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 *); + } + if (ADDED_FIELD_AT_OFFSET(weaklistoffset, t_size)) { + t_size -= sizeof(PyObject *); + } return t_size != b_size; } From 6fe801e2b1716994f8c8a895fc0ac695a39bdb47 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 3 Aug 2022 14:39:19 +0100 Subject: [PATCH 3/6] Add news --- .../next/C API/2022-08-03-14-39-08.gh-issue-92678.ozFTEx.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/C API/2022-08-03-14-39-08.gh-issue-92678.ozFTEx.rst 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 00000000000000..6bf3d4b1abbf5a --- /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. From 24ed566d8124c195974c36111344884ed4797efb Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 3 Aug 2022 14:50:18 +0100 Subject: [PATCH 4/6] Add comment --- Objects/typeobject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 280df5be02cb48..b6233da0651f7f 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2333,6 +2333,7 @@ extra_ivars(PyTypeObject *type, PyTypeObject *base) return t_size != b_size || type->tp_itemsize != base->tp_itemsize; } + /* Check for __dict__ and __weakrefs__ slots in either order */ if (ADDED_FIELD_AT_OFFSET(weaklistoffset, t_size)) { t_size -= sizeof(PyObject *); } From a7c2008e4f6cfa14eda685ed612ed5465e6dfb76 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 3 Aug 2022 17:50:41 +0100 Subject: [PATCH 5/6] PEP 7 formatting Co-authored-by: Eric Snow --- Objects/typeobject.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index b6233da0651f7f..2a03ac6e1440ae 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2317,9 +2317,9 @@ best_base(PyObject *bases) } #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) + (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) From 0b52db920596a03321db5c7c077e878c3c634194 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 3 Aug 2022 18:19:45 +0100 Subject: [PATCH 6/6] Add another comment. --- Objects/typeobject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 2a03ac6e1440ae..1980fcbf9323fd 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2341,6 +2341,7 @@ extra_ivars(PyTypeObject *type, PyTypeObject *base) 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 *); }