From 82bd88974457d2b378add1b1880298f7fdbd1c24 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 4 Nov 2020 19:36:26 +0100 Subject: [PATCH] bpo-40170: Add PyType_SetBase() function Add PyType_SetBase() and PyType_SetBaseStatic() functions to set the base of a type. PyType_Ready() no longer increment the reference count of the object type on static types which have no base type and use a borrowed reference to their base type. --- Doc/c-api/type.rst | 22 ++++++++++++ Doc/includes/sublist.c | 2 +- Doc/whatsnew/3.10.rst | 4 +++ Include/object.h | 3 ++ .../2020-11-04-20-43-15.bpo-40170.rvRWUD.rst | 2 ++ Modules/_collectionsmodule.c | 2 +- Modules/_ctypes/_ctypes.c | 28 +++++++-------- Modules/_datetimemodule.c | 6 ++-- Modules/_decimal/_decimal.c | 8 ++--- Modules/_io/_iomodule.c | 18 +++++----- Modules/_testcapimodule.c | 10 +++--- Modules/_zoneinfo.c | 2 +- Modules/xxmodule.c | 4 +-- Modules/xxsubtype.c | 4 +-- Objects/structseq.c | 2 +- Objects/typeobject.c | 35 ++++++++++++++----- PC/python3dll.c | 2 ++ 17 files changed, 103 insertions(+), 51 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2020-11-04-20-43-15.bpo-40170.rvRWUD.rst diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index fcd92e38e2428e..0fe5eb8f38b133 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -140,6 +140,28 @@ Type Objects .. versionadded:: 3.9 +.. c:function:: void PyType_SetBase(PyTypeObject *type, PyTypeObject *base) + + Set the base type of the given type. The *type* type stores a strong + reference to *base*. + + If the *type* type already has a base type, decrement the reference count of + the old base type. + + The *type* type must be a heap type. + + .. versionadded:: 3.10 + +.. c:function:: void PyType_SetBaseStatic(PyTypeObject *type, PyTypeObject *base) + + Similar to :c:func:`PyType_SetBase`, but the *type* type stores a borrowed + reference to *base* and leaves the reference count of the old base type + unchanged. + + The *type* type must be a static type. + + .. versionadded:: 3.10 + Creating Heap-Allocated Types ............................. diff --git a/Doc/includes/sublist.c b/Doc/includes/sublist.c index b2c26e73ebaf7e..dd01a20b2900fb 100644 --- a/Doc/includes/sublist.c +++ b/Doc/includes/sublist.c @@ -50,7 +50,7 @@ PyMODINIT_FUNC PyInit_sublist(void) { PyObject *m; - SubListType.tp_base = &PyList_Type; + PyType_SetBaseStatic(&SubListType, &PyList_Type); if (PyType_Ready(&SubListType) < 0) return NULL; diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index a735bf235435ca..b6bb54fd2f71f1 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -429,6 +429,10 @@ New Features slot. (Contributed by Hai Shi in :issue:`41832`.) +* Added :c:func:`PyType_SetBase` and :c:func:`PyType_SetBaseStatic` + functions to set the base type of a type. + (Contributed by Victor Stinner in :issue:`40170`.) + Porting to Python 3.10 ---------------------- diff --git a/Include/object.h b/Include/object.h index eab3228f3abe41..0f180acd68188c 100644 --- a/Include/object.h +++ b/Include/object.h @@ -686,6 +686,9 @@ static inline int _PyType_CheckExact(PyObject *op) { } #define PyType_CheckExact(op) _PyType_CheckExact(_PyObject_CAST(op)) +PyAPI_FUNC(void) PyType_SetBase(PyTypeObject *type, PyTypeObject *base); +PyAPI_FUNC(void) PyType_SetBaseStatic(PyTypeObject *type, PyTypeObject *base); + #ifdef __cplusplus } #endif diff --git a/Misc/NEWS.d/next/C API/2020-11-04-20-43-15.bpo-40170.rvRWUD.rst b/Misc/NEWS.d/next/C API/2020-11-04-20-43-15.bpo-40170.rvRWUD.rst new file mode 100644 index 00000000000000..fa8c668f27da76 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-11-04-20-43-15.bpo-40170.rvRWUD.rst @@ -0,0 +1,2 @@ +Added :c:func:`PyType_SetBase` and :c:func:`PyType_SetBaseStatic` functions +to set the base type of a type. Patch by Victor Stinner. diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 157875067635ac..65eaf1e157c122 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -2565,7 +2565,7 @@ collections_exec(PyObject *module) { &tuplegetter_type }; - defdict_type.tp_base = &PyDict_Type; + PyType_SetBaseStatic(&defdict_type, &PyDict_Type); for (size_t i = 0; i < Py_ARRAY_LENGTH(typelist); i++) { if (PyModule_AddType(module, typelist[i]) < 0) { diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 8d5594c62c4170..e4a103ca3aa248 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5560,7 +5560,7 @@ static PyTypeObject PyComError_Type = { static int create_comerror(void) { - PyComError_Type.tp_base = (PyTypeObject*)PyExc_Exception; + PyType_SetBaseStatic(&PyComError_Type, (PyTypeObject*)PyExc_Exception); if (PyType_Ready(&PyComError_Type) < 0) return -1; Py_INCREF(&PyComError_Type); @@ -5719,7 +5719,7 @@ PyInit__ctypes(void) return NULL; /* StgDict is derived from PyDict_Type */ - PyCStgDict_Type.tp_base = &PyDict_Type; + PyType_SetBaseStatic(&PyCStgDict_Type, &PyDict_Type); if (PyType_Ready(&PyCStgDict_Type) < 0) return NULL; @@ -5728,27 +5728,27 @@ PyInit__ctypes(void) * Metaclasses */ - PyCStructType_Type.tp_base = &PyType_Type; + PyType_SetBaseStatic(&PyCStructType_Type, &PyType_Type); if (PyType_Ready(&PyCStructType_Type) < 0) return NULL; - UnionType_Type.tp_base = &PyType_Type; + PyType_SetBaseStatic(&UnionType_Type, &PyType_Type); if (PyType_Ready(&UnionType_Type) < 0) return NULL; - PyCPointerType_Type.tp_base = &PyType_Type; + PyType_SetBaseStatic(&PyCPointerType_Type, &PyType_Type); if (PyType_Ready(&PyCPointerType_Type) < 0) return NULL; - PyCArrayType_Type.tp_base = &PyType_Type; + PyType_SetBaseStatic(&PyCArrayType_Type, &PyType_Type); if (PyType_Ready(&PyCArrayType_Type) < 0) return NULL; - PyCSimpleType_Type.tp_base = &PyType_Type; + PyType_SetBaseStatic(&PyCSimpleType_Type, &PyType_Type); if (PyType_Ready(&PyCSimpleType_Type) < 0) return NULL; - PyCFuncPtrType_Type.tp_base = &PyType_Type; + PyType_SetBaseStatic(&PyCFuncPtrType_Type, &PyType_Type); if (PyType_Ready(&PyCFuncPtrType_Type) < 0) return NULL; @@ -5761,42 +5761,42 @@ PyInit__ctypes(void) return NULL; Py_SET_TYPE(&Struct_Type, &PyCStructType_Type); - Struct_Type.tp_base = &PyCData_Type; + PyType_SetBaseStatic(&Struct_Type, &PyCData_Type); if (PyType_Ready(&Struct_Type) < 0) return NULL; Py_INCREF(&Struct_Type); PyModule_AddObject(m, "Structure", (PyObject *)&Struct_Type); Py_SET_TYPE(&Union_Type, &UnionType_Type); - Union_Type.tp_base = &PyCData_Type; + PyType_SetBaseStatic(&Union_Type, &PyCData_Type); if (PyType_Ready(&Union_Type) < 0) return NULL; Py_INCREF(&Union_Type); PyModule_AddObject(m, "Union", (PyObject *)&Union_Type); Py_SET_TYPE(&PyCPointer_Type, &PyCPointerType_Type); - PyCPointer_Type.tp_base = &PyCData_Type; + PyType_SetBaseStatic(&PyCPointer_Type, &PyCData_Type); if (PyType_Ready(&PyCPointer_Type) < 0) return NULL; Py_INCREF(&PyCPointer_Type); PyModule_AddObject(m, "_Pointer", (PyObject *)&PyCPointer_Type); Py_SET_TYPE(&PyCArray_Type, &PyCArrayType_Type); - PyCArray_Type.tp_base = &PyCData_Type; + PyType_SetBaseStatic(&PyCArray_Type, &PyCData_Type); if (PyType_Ready(&PyCArray_Type) < 0) return NULL; Py_INCREF(&PyCArray_Type); PyModule_AddObject(m, "Array", (PyObject *)&PyCArray_Type); Py_SET_TYPE(&Simple_Type, &PyCSimpleType_Type); - Simple_Type.tp_base = &PyCData_Type; + PyType_SetBaseStatic(&Simple_Type, &PyCData_Type); if (PyType_Ready(&Simple_Type) < 0) return NULL; Py_INCREF(&Simple_Type); PyModule_AddObject(m, "_SimpleCData", (PyObject *)&Simple_Type); Py_SET_TYPE(&PyCFuncPtr_Type, &PyCFuncPtrType_Type); - PyCFuncPtr_Type.tp_base = &PyCData_Type; + PyType_SetBaseStatic(&PyCFuncPtr_Type, &PyCData_Type); if (PyType_Ready(&PyCFuncPtr_Type) < 0) return NULL; Py_INCREF(&PyCFuncPtr_Type); diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index e59f89b3d10fb0..53448d5a71a63b 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -6523,9 +6523,9 @@ PyInit__datetime(void) // `&...` is not a constant expression according to a strict reading // of C standards. Fill tp_base at run-time rather than statically. // See https://bugs.python.org/issue40777 - PyDateTime_IsoCalendarDateType.tp_base = &PyTuple_Type; - PyDateTime_TimeZoneType.tp_base = &PyDateTime_TZInfoType; - PyDateTime_DateTimeType.tp_base = &PyDateTime_DateType; + PyType_SetBaseStatic(&PyDateTime_IsoCalendarDateType, &PyTuple_Type); + PyType_SetBaseStatic(&PyDateTime_TimeZoneType, &PyDateTime_TZInfoType); + PyType_SetBaseStatic(&PyDateTime_DateTimeType, &PyDateTime_DateType); PyTypeObject *types[] = { &PyDateTime_DateType, diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index ea16c5a6cd9cdc..6cb4e6c9f28105 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -5867,10 +5867,10 @@ PyInit__decimal(void) /* Init types */ - PyDec_Type.tp_base = &PyBaseObject_Type; - PyDecContext_Type.tp_base = &PyBaseObject_Type; - PyDecContextManager_Type.tp_base = &PyBaseObject_Type; - PyDecSignalDictMixin_Type.tp_base = &PyBaseObject_Type; + PyType_SetBaseStatic(&PyDec_Type, &PyBaseObject_Type); + PyType_SetBaseStatic(&PyDecContext_Type, &PyBaseObject_Type); + PyType_SetBaseStatic(&PyDecContextManager_Type, &PyBaseObject_Type); + PyType_SetBaseStatic(&PyDecSignalDictMixin_Type, &PyBaseObject_Type); CHECK_INT(PyType_Ready(&PyDec_Type)); CHECK_INT(PyType_Ready(&PyDecContext_Type)); diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index 9147648b243bed..9b6256cc1d9344 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -691,43 +691,43 @@ PyInit__io(void) /* Implementation of concrete IO objects. */ /* FileIO */ - PyFileIO_Type.tp_base = &PyRawIOBase_Type; + PyType_SetBaseStatic(&PyFileIO_Type, &PyRawIOBase_Type); ADD_TYPE(&PyFileIO_Type); /* BytesIO */ - PyBytesIO_Type.tp_base = &PyBufferedIOBase_Type; + PyType_SetBaseStatic(&PyBytesIO_Type, &PyBufferedIOBase_Type); ADD_TYPE(&PyBytesIO_Type); if (PyType_Ready(&_PyBytesIOBuffer_Type) < 0) goto fail; /* StringIO */ - PyStringIO_Type.tp_base = &PyTextIOBase_Type; + PyType_SetBaseStatic(&PyStringIO_Type, &PyTextIOBase_Type); ADD_TYPE(&PyStringIO_Type); #ifdef MS_WINDOWS /* WindowsConsoleIO */ - PyWindowsConsoleIO_Type.tp_base = &PyRawIOBase_Type; + PyType_SetBaseStatic(&PyWindowsConsoleIO_Type, &PyRawIOBase_Type); ADD_TYPE(&PyWindowsConsoleIO_Type); #endif /* BufferedReader */ - PyBufferedReader_Type.tp_base = &PyBufferedIOBase_Type; + PyType_SetBaseStatic(&PyBufferedReader_Type, &PyBufferedIOBase_Type); ADD_TYPE(&PyBufferedReader_Type); /* BufferedWriter */ - PyBufferedWriter_Type.tp_base = &PyBufferedIOBase_Type; + PyType_SetBaseStatic(&PyBufferedWriter_Type, &PyBufferedIOBase_Type); ADD_TYPE(&PyBufferedWriter_Type); /* BufferedRWPair */ - PyBufferedRWPair_Type.tp_base = &PyBufferedIOBase_Type; + PyType_SetBaseStatic(&PyBufferedRWPair_Type, &PyBufferedIOBase_Type); ADD_TYPE(&PyBufferedRWPair_Type); /* BufferedRandom */ - PyBufferedRandom_Type.tp_base = &PyBufferedIOBase_Type; + PyType_SetBaseStatic(&PyBufferedRandom_Type, &PyBufferedIOBase_Type); ADD_TYPE(&PyBufferedRandom_Type); /* TextIOWrapper */ - PyTextIOWrapper_Type.tp_base = &PyTextIOBase_Type; + PyType_SetBaseStatic(&PyTextIOWrapper_Type, &PyTextIOBase_Type); ADD_TYPE(&PyTextIOWrapper_Type); /* IncrementalNewlineDecoder */ diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 22d20d220d4089..9157acb7e29187 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -7093,7 +7093,7 @@ PyInit__testcapi(void) Py_INCREF(&awaitType); PyModule_AddObject(m, "awaitType", (PyObject *)&awaitType); - MyList_Type.tp_base = &PyList_Type; + PyType_SetBaseStatic(&MyList_Type, &PyList_Type); if (PyType_Ready(&MyList_Type) < 0) return NULL; Py_INCREF(&MyList_Type); @@ -7104,19 +7104,19 @@ PyInit__testcapi(void) Py_INCREF(&MethodDescriptorBase_Type); PyModule_AddObject(m, "MethodDescriptorBase", (PyObject *)&MethodDescriptorBase_Type); - MethodDescriptorDerived_Type.tp_base = &MethodDescriptorBase_Type; + PyType_SetBaseStatic(&MethodDescriptorDerived_Type, &MethodDescriptorBase_Type); if (PyType_Ready(&MethodDescriptorDerived_Type) < 0) return NULL; Py_INCREF(&MethodDescriptorDerived_Type); PyModule_AddObject(m, "MethodDescriptorDerived", (PyObject *)&MethodDescriptorDerived_Type); - MethodDescriptorNopGet_Type.tp_base = &MethodDescriptorBase_Type; + PyType_SetBaseStatic(&MethodDescriptorNopGet_Type, &MethodDescriptorBase_Type); if (PyType_Ready(&MethodDescriptorNopGet_Type) < 0) return NULL; Py_INCREF(&MethodDescriptorNopGet_Type); PyModule_AddObject(m, "MethodDescriptorNopGet", (PyObject *)&MethodDescriptorNopGet_Type); - MethodDescriptor2_Type.tp_base = &MethodDescriptorBase_Type; + PyType_SetBaseStatic(&MethodDescriptor2_Type, &MethodDescriptorBase_Type); if (PyType_Ready(&MethodDescriptor2_Type) < 0) return NULL; Py_INCREF(&MethodDescriptor2_Type); @@ -7147,7 +7147,7 @@ PyInit__testcapi(void) Py_INCREF(&MethStatic_Type); PyModule_AddObject(m, "MethStatic", (PyObject *)&MethStatic_Type); - PyRecursingInfinitelyError_Type.tp_base = (PyTypeObject *)PyExc_Exception; + PyType_SetBaseStatic(&PyRecursingInfinitelyError_Type, (PyTypeObject *)PyExc_Exception); if (PyType_Ready(&PyRecursingInfinitelyError_Type) < 0) { return NULL; } diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 7888cf86de0a5c..19a8076793365d 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -2619,7 +2619,7 @@ static int zoneinfomodule_exec(PyObject *m) { PyDateTime_IMPORT; - PyZoneInfo_ZoneInfoType.tp_base = PyDateTimeAPI->TZInfoType; + PyType_SetBaseStatic(&PyZoneInfo_ZoneInfoType, PyDateTimeAPI->TZInfoType); if (PyType_Ready(&PyZoneInfo_ZoneInfoType) < 0) { goto error; } diff --git a/Modules/xxmodule.c b/Modules/xxmodule.c index 17b049c4b9a375..92d1a02beab60d 100644 --- a/Modules/xxmodule.c +++ b/Modules/xxmodule.c @@ -353,8 +353,8 @@ xx_exec(PyObject *m) Both compilers are strictly standard conforming in this particular behavior. */ - Null_Type.tp_base = &PyBaseObject_Type; - Str_Type.tp_base = &PyUnicode_Type; + PyType_SetBaseStatic(&Null_Type, &PyBaseObject_Type); + PyType_SetBaseStatic(&Str_Type, &PyUnicode_Type); /* Finalize the type object including setting type of the new type * object; doing it here is required for portability, too. */ diff --git a/Modules/xxsubtype.c b/Modules/xxsubtype.c index 7200337724e080..5f17e49238a4e8 100644 --- a/Modules/xxsubtype.c +++ b/Modules/xxsubtype.c @@ -264,11 +264,11 @@ xxsubtype_exec(PyObject* m) PyType_Ready() is called. Note that PyType_Ready() automatically initializes the ob.ob_type field to &PyType_Type if it's NULL, so it's not necessary to fill in ob_type first. */ - spamdict_type.tp_base = &PyDict_Type; + PyType_SetBaseStatic(&spamdict_type, &PyDict_Type); if (PyType_Ready(&spamdict_type) < 0) return -1; - spamlist_type.tp_base = &PyList_Type; + PyType_SetBaseStatic(&spamlist_type, &PyList_Type); if (PyType_Ready(&spamlist_type) < 0) return -1; diff --git a/Objects/structseq.c b/Objects/structseq.c index 5caa3bd52e4d4f..a5f7ee7053eee7 100644 --- a/Objects/structseq.c +++ b/Objects/structseq.c @@ -451,7 +451,7 @@ PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc) type->tp_dealloc = (destructor)structseq_dealloc; type->tp_repr = (reprfunc)structseq_repr; type->tp_doc = desc->doc; - type->tp_base = &PyTuple_Type; + PyType_SetBaseStatic(type, &PyTuple_Type); type->tp_methods = structseq_methods; type->tp_new = structseq_new; type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 55bf9b3f389273..3d0d81e638995f 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2633,11 +2633,10 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) goto error; } - /* Set tp_base and tp_bases */ + /* Set tp_bases and tp_base */ type->tp_bases = bases; bases = NULL; - Py_INCREF(base); - type->tp_base = base; + PyType_SetBase(type, base); /* Initialize tp_dict from passed-in dict */ Py_INCREF(dict); @@ -2990,11 +2989,10 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) type->tp_as_sequence = &res->as_sequence; type->tp_as_mapping = &res->as_mapping; type->tp_as_buffer = &res->as_buffer; - /* Set tp_base and tp_bases */ + /* Set tp_bases and tp_base */ type->tp_bases = bases; bases = NULL; - Py_INCREF(base); - type->tp_base = base; + PyType_SetBase(type, base); type->tp_basicsize = spec->basicsize; type->tp_itemsize = spec->itemsize; @@ -3200,6 +3198,22 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def) } +void +PyType_SetBase(PyTypeObject *type, PyTypeObject *base) +{ + _PyObject_ASSERT((PyObject *)type, (type->tp_flags & Py_TPFLAGS_HEAPTYPE)); + Py_INCREF(base); + Py_XSETREF(type->tp_base, base); +} + +void +PyType_SetBaseStatic(PyTypeObject *type, PyTypeObject *base) +{ + _PyObject_ASSERT((PyObject *)type, !(type->tp_flags & Py_TPFLAGS_HEAPTYPE)); + type->tp_base = base; +} + + /* Internal API to look for a name through the MRO, bypassing the method cache. This returns a borrowed reference, and might set an exception. 'error' is set to: -1: error with exception; 1: error without exception; 0: ok */ @@ -5448,8 +5462,13 @@ PyType_Ready(PyTypeObject *type) /* Initialize tp_base (defaults to BaseObject unless that's us) */ base = type->tp_base; if (base == NULL && type != &PyBaseObject_Type) { - base = type->tp_base = &PyBaseObject_Type; - Py_INCREF(base); + base = &PyBaseObject_Type; + if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { + PyType_SetBase(type, base); + } + else { + PyType_SetBaseStatic(type, base); + } } /* Now the only way base can still be NULL is if type is diff --git a/PC/python3dll.c b/PC/python3dll.c index d1fdd0ac54ca8d..75a98c8584f7bb 100644 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -554,6 +554,8 @@ EXPORT_FUNC(PyType_GetSlot) EXPORT_FUNC(PyType_IsSubtype) EXPORT_FUNC(PyType_Modified) EXPORT_FUNC(PyType_Ready) +EXPORT_FUNC(PyType_SetBase) +EXPORT_FUNC(PyType_SetBaseStatic) EXPORT_FUNC(PyUnicode_Append) EXPORT_FUNC(PyUnicode_AppendAndDel) EXPORT_FUNC(PyUnicode_AsASCIIString)