It is now allowed to add new fields at the end of the PyTypeObject struct without having to allocate a dedicated compatibility flag in tp_flags.
This will reduce the risk of running out of bits in the 32-bit tp_flags value.
.. versionadded:: 3.4
+ .. deprecated:: 3.8
+ This flag isn't necessary anymore, as the interpreter assumes the
+ :c:member:`~PyTypeObject.tp_finalize` slot is always present in the
+ type structure.
+
.. c:member:: const char* PyTypeObject.tp_doc
(Contributed by Zackery Spytz in :issue:`33407`.)
+* The interpreter does not pretend to support binary compatibility of
+ extension types accross feature releases, anymore. A :c:type:`PyTypeObject`
+ exported by a third-party extension module is supposed to have all the
+ slots expected in the current Python version, including
+ :c:member:`~PyTypeObject.tp_finalize` (:const:`Py_TPFLAGS_HAVE_FINALIZE`
+ is not checked anymore before reading :c:member:`~PyTypeObject.tp_finalize`).
+
+ (Contributed by Antoine Pitrou in :issue:`32388`.)
+
CPython bytecode changes
------------------------
#define Py_PRINT_RAW 1 /* No string quotes etc. */
/*
-`Type flags (tp_flags)
+Type flags (tp_flags)
-These flags are used to extend the type structure in a backwards-compatible
-fashion. Extensions can use the flags to indicate (and test) when a given
-type structure contains a new feature. The Python core will use these when
-introducing new functionality between major revisions (to avoid mid-version
-changes in the PYTHON_API_VERSION).
+These flags are used to change expected features and behavior for a
+particular type.
Arbitration of the flag bit positions will need to be coordinated among
all extension writers who publicly release their extensions (this will
-be fewer than you might expect!)..
+be fewer than you might expect!).
Most flags were removed as of Python 3.0 to make room for new flags. (Some
flags are not for backwards compatibility but to indicate the presence of an
/* Set while the type is being 'readied', to prevent recursive ready calls */
#define Py_TPFLAGS_READYING (1UL << 13)
-/* Objects support garbage collection (see objimp.h) */
+/* Objects support garbage collection (see objimpl.h) */
#define Py_TPFLAGS_HAVE_GC (1UL << 14)
/* These two bits are preserved for Stackless Python, next after this is 17 */
/* NOTE: The following flags reuse lower bits (removed as part of the
* Python 3.0 transition). */
+/* The following flag is kept for compatibility. Starting with 3.8,
+ * binary compatibility of C extensions accross feature releases of
+ * Python is not supported anymore, except when using the stable ABI.
+ */
+
/* Type structure has tp_finalize member (3.4) */
#define Py_TPFLAGS_HAVE_FINALIZE (1UL << 0)
--- /dev/null
+Remove cross-version binary compatibility requirement in tp_flags.
.tp_dealloc = FutureObj_dealloc,
.tp_as_async = &FutureType_as_async,
.tp_repr = (reprfunc)FutureObj_repr,
- .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_FINALIZE,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE,
.tp_doc = _asyncio_Future___init____doc__,
.tp_traverse = (traverseproc)FutureObj_traverse,
.tp_clear = (inquiry)FutureObj_clear,
.tp_dealloc = TaskObj_dealloc,
.tp_as_async = &FutureType_as_async,
.tp_repr = (reprfunc)FutureObj_repr,
- .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_FINALIZE,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE,
.tp_doc = _asyncio_Task___init____doc__,
.tp_traverse = (traverseproc)TaskObj_traverse,
.tp_clear = (inquiry)TaskObj_clear,
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
bufferediobase_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
+ | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
_io_BufferedReader___init____doc__, /* tp_doc */
(traverseproc)buffered_traverse, /* tp_traverse */
(inquiry)buffered_clear, /* tp_clear */
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
+ | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
_io_BufferedWriter___init____doc__, /* tp_doc */
(traverseproc)buffered_traverse, /* tp_traverse */
(inquiry)buffered_clear, /* tp_clear */
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
+ | Py_TPFLAGS_HAVE_GC, /* tp_flags */
_io_BufferedRWPair___init____doc__, /* tp_doc */
(traverseproc)bufferedrwpair_traverse, /* tp_traverse */
(inquiry)bufferedrwpair_clear, /* tp_clear */
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
+ | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
_io_BufferedRandom___init____doc__, /* tp_doc */
(traverseproc)buffered_traverse, /* tp_traverse */
(inquiry)buffered_clear, /* tp_clear */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
+ | Py_TPFLAGS_HAVE_GC, /* tp_flags */
_io_FileIO___init____doc__, /* tp_doc */
(traverseproc)fileio_traverse, /* tp_traverse */
(inquiry)fileio_clear, /* tp_clear */
0, /* tp_richcompare */
- offsetof(fileio, weakreflist), /* tp_weaklistoffset */
+ offsetof(fileio, weakreflist), /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
fileio_methods, /* tp_methods */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
- offsetof(fileio, dict), /* tp_dictoffset */
+ offsetof(fileio, dict), /* tp_dictoffset */
_io_FileIO___init__, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
fileio_new, /* tp_new */
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
+ | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
iobase_doc, /* tp_doc */
(traverseproc)iobase_traverse, /* tp_traverse */
(inquiry)iobase_clear, /* tp_clear */
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
rawiobase_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
textiobase_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/
+ | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
_io_TextIOWrapper___init____doc__, /* tp_doc */
(traverseproc)textiowrapper_traverse, /* tp_traverse */
(inquiry)textiowrapper_clear, /* tp_clear */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
+ | Py_TPFLAGS_HAVE_GC, /* tp_flags */
_io__WindowsConsoleIO___init____doc__, /* tp_doc */
(traverseproc)winconsoleio_traverse, /* tp_traverse */
(inquiry)winconsoleio_clear, /* tp_clear */
"_testimportexec.Example",
sizeof(ExampleObject),
0,
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
Example_Type_slots
};
PyObject *op = FROM_GC(gc);
gc_list_move(gc, &seen);
if (!_PyGCHead_FINALIZED(gc) &&
- PyType_HasFeature(Py_TYPE(op), Py_TPFLAGS_HAVE_FINALIZE) &&
(finalize = Py_TYPE(op)->tp_finalize) != NULL) {
_PyGCHead_SET_FINALIZED(gc);
Py_INCREF(op);
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT
- | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
- | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
sock_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
"xxlimited.Xxo",
sizeof(XxoObject),
0,
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
Xxo_Type_slots
};
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
- Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc)gen_traverse, /* tp_traverse */
0, /* tp_clear */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
- Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc)gen_traverse, /* tp_traverse */
0, /* tp_clear */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
- Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc)async_gen_traverse, /* tp_traverse */
0, /* tp_clear */
{
PyTypeObject *tp = Py_TYPE(self);
- /* The former could happen on heaptypes created from the C API, e.g.
- PyType_FromSpec(). */
- if (!PyType_HasFeature(tp, Py_TPFLAGS_HAVE_FINALIZE) ||
- tp->tp_finalize == NULL)
+ if (tp->tp_finalize == NULL)
return;
/* tp_finalize should only be called once. */
if (PyType_IS_GC(tp) && _PyGC_FINALIZED(self))
Invariants:
- Py_TPFLAGS_VALID_VERSION_TAG is never set if
- Py_TPFLAGS_HAVE_VERSION_TAG is not set (e.g. on type
- objects coming from non-recompiled extension modules)
+ Py_TPFLAGS_HAVE_VERSION_TAG is not set (in case of a
+ bizarre MRO, see type_mro_modified()).
- before Py_TPFLAGS_VALID_VERSION_TAG can be set on a type,
it must first be set on all super types.
/* Initialize tp_flags */
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE |
- Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_FINALIZE;
+ Py_TPFLAGS_BASETYPE;
if (base->tp_flags & Py_TPFLAGS_HAVE_GC)
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
COPYSLOT(tp_init);
COPYSLOT(tp_alloc);
COPYSLOT(tp_is_gc);
- if ((type->tp_flags & Py_TPFLAGS_HAVE_FINALIZE) &&
- (base->tp_flags & Py_TPFLAGS_HAVE_FINALIZE)) {
- COPYSLOT(tp_finalize);
- }
+ COPYSLOT(tp_finalize);
if ((type->tp_flags & Py_TPFLAGS_HAVE_GC) ==
(base->tp_flags & Py_TPFLAGS_HAVE_GC)) {
/* They agree about gc. */