From 05940a881404ed86c0b58758e9d6da46c0d7e4e7 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 20 May 2022 13:22:32 +0200 Subject: [PATCH 1/6] gh-60074: add new stable API function PyType_FromMetaclass Added a new stable API function ``PyType_FromMetaclass``, which mirrors the behavior of ``PyType_FromModuleAndSpec`` except that it takes an additional metaclass argument. This is, e.g., useful for language binding tools that need to store additional information in the type object. --- Doc/c-api/type.rst | 17 ++++- Doc/c-api/typeobj.rst | 2 +- Doc/data/stable_abi.dat | 1 + Doc/whatsnew/3.11.rst | 5 ++ Include/object.h | 1 + Lib/test/test_capi.py | 13 ++++ Lib/test/test_stable_abi_ctypes.py | 1 + ...2-05-20-13-32-24.gh-issue-93012.e9B-pv.rst | 8 ++ Misc/stable_abi.toml | 2 + Modules/_testcapimodule.c | 73 +++++++++++++++++++ Objects/typeobject.c | 15 +++- PC/python3dll.c | 1 + 12 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-05-20-13-32-24.gh-issue-93012.e9B-pv.rst diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index d740e4eb0897e5..d02fddabe7f5e5 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -192,7 +192,7 @@ The following functions and structs are used to create .. c:function:: PyObject* PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) - Creates and returns a :ref:`heap type ` from the *spec* + Create and return a :ref:`heap type ` from the *spec* (:const:`Py_TPFLAGS_HEAPTYPE`). The *bases* argument can be used to specify base classes; it can either @@ -208,7 +208,9 @@ The following functions and structs are used to create The associated module is not inherited by subclasses; it must be specified for each class individually. - This function calls :c:func:`PyType_Ready` on the new type. + This function calls :c:func:`PyType_Ready` on the new type. Its behavior is + equivalent to ``PyType_FromMetaclass(&PyType_Type, NULL, spec, + bases)``. .. versionadded:: 3.9 @@ -217,6 +219,17 @@ The following functions and structs are used to create The function now accepts a single class as the *bases* argument and ``NULL`` as the ``tp_doc`` slot. +.. c:function:: PyObject* PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) + + Create and return a :ref:`heap type ` from the *spec* + (:const:`Py_TPFLAGS_HEAPTYPE`). This function is a generalization of + :c:func:`PyType_FromModuleAndSpec`, with the main difference being that + the metaclass *metaclass* is used to construct the resulting type object. + + Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not supported. + + .. versionadded:: 3.11 + .. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) Equivalent to ``PyType_FromModuleAndSpec(NULL, spec, bases)``. diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index b3f371bb9c0623..df479046d4aeb7 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -2071,7 +2071,7 @@ flag set. This is done by filling a :c:type:`PyType_Spec` structure and calling :c:func:`PyType_FromSpec`, :c:func:`PyType_FromSpecWithBases`, -or :c:func:`PyType_FromModuleAndSpec`. +:c:func:`PyType_FromModuleAndSpec`, or :c:func:`PyType_FromMetaclass`. .. _number-structs: diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 3912a7c1242de0..c0ee7ce7cbbd89 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -653,6 +653,7 @@ function,PyTuple_Size,3.2,, var,PyTuple_Type,3.2,, type,PyTypeObject,3.2,,opaque function,PyType_ClearCache,3.2,, +function,PyType_FromMetaclass,3.11,, function,PyType_FromModuleAndSpec,3.10,, function,PyType_FromSpec,3.2,, function,PyType_FromSpecWithBases,3.3,, diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 1f88d2557aa3d0..bf3edd24d582cc 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -1608,6 +1608,11 @@ New Features * Added the :c:member:`PyConfig.safe_path` member. (Contributed by Victor Stinner in :gh:`57684`.) +* Added the new limited C API function :c:func:`PyType_FromMetaclass`, + which generalizes the existing :c:func:`PyType_FromModuleAndSpec` using + an additional metaclass argument. + (Contributed by Wenzel Jakob in :gh:`93012`.) + Porting to Python 3.11 ---------------------- diff --git a/Include/object.h b/Include/object.h index f01b9fa86d0148..d4c89ba404ef90 100644 --- a/Include/object.h +++ b/Include/object.h @@ -256,6 +256,7 @@ PyAPI_FUNC(void *) PyType_GetModuleState(PyTypeObject *); #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030B0000 PyAPI_FUNC(PyObject *) PyType_GetName(PyTypeObject *); PyAPI_FUNC(PyObject *) PyType_GetQualName(PyTypeObject *); +PyAPI_FUNC(PyObject *) PyType_FromMetaclass(PyTypeObject*, PyObject*, PyType_Spec*, PyObject*); #endif /* Generic type check */ diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 904ae9bc47ecfe..e28248363b5dc4 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -608,6 +608,19 @@ def test_heaptype_with_setattro(self): del obj.value self.assertEqual(obj.pvalue, 0) + def test_heaptype_with_custom_metaclass(self): + self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclass, type)) + self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclassCustomNew, type)) + + t = _testcapi.pytype_fromspec_meta(_testcapi.HeapCTypeMetaclass) + self.assertIsInstance(t, type) + self.assertEqual(t.__name__, "HeapCTypeViaMetaclass") + self.assertIs(type(t), _testcapi.HeapCTypeMetaclass) + + msg = "Metaclasses with custom tp_new are not supported." + with self.assertRaisesRegex(TypeError, msg): + t = _testcapi.pytype_fromspec_meta(_testcapi.HeapCTypeMetaclassCustomNew) + def test_pynumber_tobase(self): from _testcapi import pynumber_tobase self.assertEqual(pynumber_tobase(123, 2), '0b1111011') diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index 18c85061ca0893..53e93ab6b9b4c9 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -660,6 +660,7 @@ def test_windows_feature_macros(self): "PyTuple_Size", "PyTuple_Type", "PyType_ClearCache", + "PyType_FromMetaclass", "PyType_FromModuleAndSpec", "PyType_FromSpec", "PyType_FromSpecWithBases", diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-05-20-13-32-24.gh-issue-93012.e9B-pv.rst b/Misc/NEWS.d/next/Core and Builtins/2022-05-20-13-32-24.gh-issue-93012.e9B-pv.rst new file mode 100644 index 00000000000000..8de0f000647dc8 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-05-20-13-32-24.gh-issue-93012.e9B-pv.rst @@ -0,0 +1,8 @@ +Added the new function :c:func:`PyType_FromMetaclass`, which generalizes the +existing :c:func:`PyType_FromModuleAndSpec` using an additional metaclass +argument. This is useful for language binding tools, where it can be used to +intercept type-related operations like subclassing or static attribute access +by specifying a metaclass with custom slots. + +Importantly, :c:func:`PyType_FromMetaclass` is available in the Limited API, +which provides a path towards migrating more binding tools onto the Stable ABI. diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index d848f18d68ff64..387f4cd513027b 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2275,3 +2275,5 @@ added = '3.11' [function.PyErr_SetHandledException] added = '3.11' +[function.PyType_FromMetaclass] + added = '3.11' diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 3bc776140aabaa..37f4ded8001c6f 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -308,6 +308,32 @@ test_dict_inner(int count) } } +static PyObject *pytype_fromspec_meta(PyObject* self, PyObject *meta) +{ + if (!PyType_Check(meta)) { + PyErr_SetString( + TestError, + "pytype_fromspec_meta: must be invoked with a type argument!"); + return NULL; + } + + PyType_Slot HeapCTypeViaMetaclass_slots[] = { + {0}, + }; + + PyType_Spec HeapCTypeViaMetaclass_spec = { + "_testcapi.HeapCTypeViaMetaclass", + sizeof(PyObject), + 0, + Py_TPFLAGS_DEFAULT, + HeapCTypeViaMetaclass_slots + }; + + return PyType_FromMetaclass( + (PyTypeObject *) meta, NULL, &HeapCTypeViaMetaclass_spec, NULL); +} + + static PyObject* test_dict_iteration(PyObject* self, PyObject *Py_UNUSED(ignored)) { @@ -5886,6 +5912,7 @@ static PyMethodDef TestMethods[] = { {"test_long_numbits", test_long_numbits, METH_NOARGS}, {"test_k_code", test_k_code, METH_NOARGS}, {"test_empty_argparse", test_empty_argparse, METH_NOARGS}, + {"pytype_fromspec_meta", pytype_fromspec_meta, METH_O}, {"parse_tuple_and_keywords", parse_tuple_and_keywords, METH_VARARGS}, {"pyobject_repr_from_null", pyobject_repr_from_null, METH_NOARGS}, {"pyobject_str_from_null", pyobject_str_from_null, METH_NOARGS}, @@ -7078,6 +7105,38 @@ static PyType_Spec HeapCTypeSubclassWithFinalizer_spec = { HeapCTypeSubclassWithFinalizer_slots }; +static PyType_Slot HeapCTypeMetaclass_slots[] = { + {0}, +}; + +static PyType_Spec HeapCTypeMetaclass_spec = { + "_testcapi.HeapCTypeMetaclass", + sizeof(PyHeapTypeObject), + sizeof(PyMemberDef), + Py_TPFLAGS_DEFAULT, + HeapCTypeMetaclass_slots +}; + +static PyObject * +heap_ctype_metaclass_custom_tp_new(PyTypeObject *tp, PyObject *args, PyObject *kwargs) +{ + return PyType_Type.tp_new(tp, args, kwargs); +} + +static PyType_Slot HeapCTypeMetaclassCustomNew_slots[] = { + { Py_tp_new, heap_ctype_metaclass_custom_tp_new }, + {0}, +}; + +static PyType_Spec HeapCTypeMetaclassCustomNew_spec = { + "_testcapi.HeapCTypeMetaclassCustomNew", + sizeof(PyHeapTypeObject), + sizeof(PyMemberDef), + Py_TPFLAGS_DEFAULT, + HeapCTypeMetaclassCustomNew_slots +}; + + typedef struct { PyObject_HEAD PyObject *dict; @@ -7591,6 +7650,20 @@ PyInit__testcapi(void) Py_DECREF(subclass_with_finalizer_bases); PyModule_AddObject(m, "HeapCTypeSubclassWithFinalizer", HeapCTypeSubclassWithFinalizer); + PyObject *HeapCTypeMetaclass = PyType_FromMetaclass( + &PyType_Type, m, &HeapCTypeMetaclass_spec, (PyObject *) &PyType_Type); + if (HeapCTypeMetaclass == NULL) { + return NULL; + } + PyModule_AddObject(m, "HeapCTypeMetaclass", HeapCTypeMetaclass); + + PyObject *HeapCTypeMetaclassCustomNew = PyType_FromMetaclass( + &PyType_Type, m, &HeapCTypeMetaclassCustomNew_spec, (PyObject *) &PyType_Type); + if (HeapCTypeMetaclassCustomNew == NULL) { + return NULL; + } + PyModule_AddObject(m, "HeapCTypeMetaclassCustomNew", HeapCTypeMetaclassCustomNew); + if (PyType_Ready(&ContainerNoGC_type) < 0) { return NULL; } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 1daf2b8d3b0ff8..3faea3fb72c56f 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3373,6 +3373,13 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) PyObject * PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) +{ + return PyType_FromMetaclass(&PyType_Type, module, spec, bases); +} + +PyObject * +PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, + PyType_Spec *spec, PyObject *bases) { PyHeapTypeObject *res; PyObject *modname; @@ -3384,6 +3391,12 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) char *res_start; short slot_offset, subslot_offset; + if (metaclass->tp_new != PyType_Type.tp_new) { + PyErr_SetString(PyExc_TypeError, + "Metaclasses with custom tp_new are not supported."); + return NULL; + } + nmembers = weaklistoffset = dictoffset = vectorcalloffset = 0; for (slot = spec->slots; slot->slot; slot++) { if (slot->slot == Py_tp_members) { @@ -3412,7 +3425,7 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) } } - res = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, nmembers); + res = (PyHeapTypeObject*)metaclass->tp_alloc(metaclass, nmembers); if (res == NULL) return NULL; res_start = (char*)res; diff --git a/PC/python3dll.c b/PC/python3dll.c index 50e7a9607bec95..024ec49d68d797 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -599,6 +599,7 @@ EXPORT_FUNC(PyTuple_Pack) EXPORT_FUNC(PyTuple_SetItem) EXPORT_FUNC(PyTuple_Size) EXPORT_FUNC(PyType_ClearCache) +EXPORT_FUNC(PyType_FromMetaclass) EXPORT_FUNC(PyType_FromModuleAndSpec) EXPORT_FUNC(PyType_FromSpec) EXPORT_FUNC(PyType_FromSpecWithBases) From 30e4e1bfd096697e63aaaeacdafbfd5a7d0254d3 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 24 May 2022 22:42:41 +0200 Subject: [PATCH 2/6] retarget to Python 3.12 --- Doc/c-api/type.rst | 2 +- Doc/data/stable_abi.dat | 2 +- Doc/whatsnew/3.11.rst | 5 ----- Doc/whatsnew/3.12.rst | 5 +++++ Misc/stable_abi.toml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index d02fddabe7f5e5..60a6fafb535411 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -228,7 +228,7 @@ The following functions and structs are used to create Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not supported. - .. versionadded:: 3.11 + .. versionadded:: 3.12 .. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index c0ee7ce7cbbd89..82cd5796efd27d 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -653,7 +653,7 @@ function,PyTuple_Size,3.2,, var,PyTuple_Type,3.2,, type,PyTypeObject,3.2,,opaque function,PyType_ClearCache,3.2,, -function,PyType_FromMetaclass,3.11,, +function,PyType_FromMetaclass,3.12,, function,PyType_FromModuleAndSpec,3.10,, function,PyType_FromSpec,3.2,, function,PyType_FromSpecWithBases,3.3,, diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index bf3edd24d582cc..1f88d2557aa3d0 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -1608,11 +1608,6 @@ New Features * Added the :c:member:`PyConfig.safe_path` member. (Contributed by Victor Stinner in :gh:`57684`.) -* Added the new limited C API function :c:func:`PyType_FromMetaclass`, - which generalizes the existing :c:func:`PyType_FromModuleAndSpec` using - an additional metaclass argument. - (Contributed by Wenzel Jakob in :gh:`93012`.) - Porting to Python 3.11 ---------------------- diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 033de1780b3d18..fd487848f09a34 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -151,6 +151,11 @@ C API Changes New Features ------------ +* Added the new limited C API function :c:func:`PyType_FromMetaclass`, + which generalizes the existing :c:func:`PyType_FromModuleAndSpec` using + an additional metaclass argument. + (Contributed by Wenzel Jakob in :gh:`93012`.) + Porting to Python 3.12 ---------------------- diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 387f4cd513027b..84bec827096050 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2276,4 +2276,4 @@ [function.PyErr_SetHandledException] added = '3.11' [function.PyType_FromMetaclass] - added = '3.11' + added = '3.12' From 0525d785f19e487cedac494a63c1f892f6a91450 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 25 May 2022 17:00:38 +0200 Subject: [PATCH 3/6] incorporated feedback --- Doc/c-api/type.rst | 31 +++++++++++++++---------------- Objects/typeobject.c | 5 ++++- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 60a6fafb535411..99b3845237d868 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -190,11 +190,16 @@ Creating Heap-Allocated Types The following functions and structs are used to create :ref:`heap types `. -.. c:function:: PyObject* PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) +.. c:function:: PyObject* PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) Create and return a :ref:`heap type ` from the *spec* (:const:`Py_TPFLAGS_HEAPTYPE`). + The metaclass *metaclass* is used to construct the resulting type object. + When *metaclass* is ``NULL``, the default :c:type:`PyType_Type` is used + instead. Note that metaclasses that override + :c:member:`~PyTypeObject.tp_new` are not supported. + The *bases* argument can be used to specify base classes; it can either be only one class or a tuple of classes. If *bases* is ``NULL``, the *Py_tp_bases* slot is used instead. @@ -208,9 +213,13 @@ The following functions and structs are used to create The associated module is not inherited by subclasses; it must be specified for each class individually. - This function calls :c:func:`PyType_Ready` on the new type. Its behavior is - equivalent to ``PyType_FromMetaclass(&PyType_Type, NULL, spec, - bases)``. + This function calls :c:func:`PyType_Ready` on the new type. + + .. versionadded:: 3.12 + +.. c:function:: PyObject* PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) + + Equivalent to ``PyType_FromMetaclass(NULL, module, spec, bases)``. .. versionadded:: 3.9 @@ -219,26 +228,16 @@ The following functions and structs are used to create The function now accepts a single class as the *bases* argument and ``NULL`` as the ``tp_doc`` slot. -.. c:function:: PyObject* PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) - - Create and return a :ref:`heap type ` from the *spec* - (:const:`Py_TPFLAGS_HEAPTYPE`). This function is a generalization of - :c:func:`PyType_FromModuleAndSpec`, with the main difference being that - the metaclass *metaclass* is used to construct the resulting type object. - - Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not supported. - - .. versionadded:: 3.12 .. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) - Equivalent to ``PyType_FromModuleAndSpec(NULL, spec, bases)``. + Equivalent to ``PyType_FromMetaclass(NULL, NULL, spec, bases)``. .. versionadded:: 3.3 .. c:function:: PyObject* PyType_FromSpec(PyType_Spec *spec) - Equivalent to ``PyType_FromSpecWithBases(spec, NULL)``. + Equivalent to ``PyType_FromMetaclass(NULL, NULL, spec, NULL)``. .. c:type:: PyType_Spec diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 3faea3fb72c56f..8f8ae1ba45968e 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3374,7 +3374,7 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) PyObject * PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) { - return PyType_FromMetaclass(&PyType_Type, module, spec, bases); + return PyType_FromMetaclass(NULL, module, spec, bases); } PyObject * @@ -3391,6 +3391,9 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, char *res_start; short slot_offset, subslot_offset; + if (!metaclass) + metaclass = &PyType_Type; + if (metaclass->tp_new != PyType_Type.tp_new) { PyErr_SetString(PyExc_TypeError, "Metaclasses with custom tp_new are not supported."); From db620804e45b8f757b26cb58c20ed02573abeac3 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 25 May 2022 17:03:28 +0200 Subject: [PATCH 4/6] .. also remove unnecessary indirection from implementation --- Objects/typeobject.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 8f8ae1ba45968e..a2181d205acae2 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3365,18 +3365,6 @@ static const PySlot_Offset pyslot_offsets[] = { #include "typeslots.inc" }; -PyObject * -PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) -{ - return PyType_FromModuleAndSpec(NULL, spec, bases); -} - -PyObject * -PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) -{ - return PyType_FromMetaclass(NULL, module, spec, bases); -} - PyObject * PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) @@ -3655,10 +3643,22 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, return NULL; } +PyObject * +PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) +{ + return PyType_FromMetaclass(NULL, module, spec, bases); +} + +PyObject * +PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) +{ + return PyType_FromMetaclass(NULL, NULL, spec, bases); +} + PyObject * PyType_FromSpec(PyType_Spec *spec) { - return PyType_FromSpecWithBases(spec, NULL); + return PyType_FromMetaclass(NULL, NULL, spec, NULL); } PyObject * From b4a478cda8d7d0e543011e0c84336d0a7c58aca9 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 25 May 2022 20:15:43 +0200 Subject: [PATCH 5/6] fix Py_LIMITED_API check in object.h --- Include/object.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Include/object.h b/Include/object.h index d4c89ba404ef90..a3c6bd4fa984d5 100644 --- a/Include/object.h +++ b/Include/object.h @@ -256,6 +256,8 @@ PyAPI_FUNC(void *) PyType_GetModuleState(PyTypeObject *); #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030B0000 PyAPI_FUNC(PyObject *) PyType_GetName(PyTypeObject *); PyAPI_FUNC(PyObject *) PyType_GetQualName(PyTypeObject *); +#endif +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030C0000 PyAPI_FUNC(PyObject *) PyType_FromMetaclass(PyTypeObject*, PyObject*, PyType_Spec*, PyObject*); #endif From 872249e70d7f5d274de22696e597a1133d869c71 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 26 May 2022 17:41:48 +0200 Subject: [PATCH 6/6] Style nitpick --- Objects/typeobject.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index a2181d205acae2..ff5196c904eefb 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3379,8 +3379,9 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, char *res_start; short slot_offset, subslot_offset; - if (!metaclass) + if (!metaclass) { metaclass = &PyType_Type; + } if (metaclass->tp_new != PyType_Type.tp_new) { PyErr_SetString(PyExc_TypeError,