[3.14] gh-75459: Doc: C API: Improve object life cycle documentation (GH-125962) (GH-134344)

gh-75459: Doc: C API: Improve object life cycle documentation (GH-125962)

  * Add "cyclic isolate" to the glossary.
  * Add a new "Object Life Cycle" page.
  * Improve docs for related API, with special focus on cross-references and warnings
(cherry picked from commit 3246ea514d)

Co-authored-by: Richard Hansen <rhansen@rhansen.org>
Co-authored-by: Petr Viktorin <encukou@gmail.com>
Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
This commit is contained in:
Miss Islington (bot) 2025-05-20 19:11:01 +02:00 committed by GitHub
parent 6d4b56d2f3
commit 6074e1e6c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 1384 additions and 135 deletions

View file

@ -676,77 +676,122 @@ and :c:data:`PyType_Type` effectively act as defaults.)
.. c:member:: destructor PyTypeObject.tp_dealloc
A pointer to the instance destructor function. This function must be defined
unless the type guarantees that its instances will never be deallocated (as is
the case for the singletons ``None`` and ``Ellipsis``). The function signature is::
A pointer to the instance destructor function. The function signature is::
void tp_dealloc(PyObject *self);
The destructor function is called by the :c:func:`Py_DECREF` and
:c:func:`Py_XDECREF` macros when the new reference count is zero. At this point,
the instance is still in existence, but there are no references to it. The
destructor function should free all references which the instance owns, free all
memory buffers owned by the instance (using the freeing function corresponding
to the allocation function used to allocate the buffer), and call the type's
:c:member:`~PyTypeObject.tp_free` function. If the type is not subtypable
(doesn't have the :c:macro:`Py_TPFLAGS_BASETYPE` flag bit set), it is
permissible to call the object deallocator directly instead of via
:c:member:`~PyTypeObject.tp_free`. The object deallocator should be the one used to allocate the
instance; this is normally :c:func:`PyObject_Free` if the instance was allocated
using :c:macro:`PyObject_New` or :c:macro:`PyObject_NewVar`, or
:c:func:`PyObject_GC_Del` if the instance was allocated using
:c:macro:`PyObject_GC_New` or :c:macro:`PyObject_GC_NewVar`.
The destructor function should remove all references which the instance owns
(e.g., call :c:func:`Py_CLEAR`), free all memory buffers owned by the
instance, and call the type's :c:member:`~PyTypeObject.tp_free` function to
free the object itself.
If the type supports garbage collection (has the :c:macro:`Py_TPFLAGS_HAVE_GC`
flag bit set), the destructor should call :c:func:`PyObject_GC_UnTrack`
No guarantees are made about when an object is destroyed, except:
* Python will destroy an object immediately or some time after the final
reference to the object is deleted, unless its finalizer
(:c:member:`~PyTypeObject.tp_finalize`) subsequently resurrects the
object.
* An object will not be destroyed while it is being automatically finalized
(:c:member:`~PyTypeObject.tp_finalize`) or automatically cleared
(:c:member:`~PyTypeObject.tp_clear`).
CPython currently destroys an object immediately from :c:func:`Py_DECREF`
when the new reference count is zero, but this may change in a future
version.
It is recommended to call :c:func:`PyObject_CallFinalizerFromDealloc` at the
beginning of :c:member:`!tp_dealloc` to guarantee that the object is always
finalized before destruction.
If the type supports garbage collection (the :c:macro:`Py_TPFLAGS_HAVE_GC`
flag is set), the destructor should call :c:func:`PyObject_GC_UnTrack`
before clearing any member fields.
.. code-block:: c
It is permissible to call :c:member:`~PyTypeObject.tp_clear` from
:c:member:`!tp_dealloc` to reduce code duplication and to guarantee that the
object is always cleared before destruction. Beware that
:c:member:`!tp_clear` might have already been called.
static void
foo_dealloc(PyObject *op)
{
If the type is heap allocated (:c:macro:`Py_TPFLAGS_HEAPTYPE`), the
deallocator should release the owned reference to its type object (via
:c:func:`Py_DECREF`) after calling the type deallocator. See the example
code below.::
static void
foo_dealloc(PyObject *op)
{
foo_object *self = (foo_object *) op;
PyObject_GC_UnTrack(self);
Py_CLEAR(self->ref);
Py_TYPE(self)->tp_free(self);
}
}
Finally, if the type is heap allocated (:c:macro:`Py_TPFLAGS_HEAPTYPE`), the
deallocator should release the owned reference to its type object
(via :c:func:`Py_DECREF`) after
calling the type deallocator. In order to avoid dangling pointers, the
recommended way to achieve this is:
:c:member:`!tp_dealloc` must leave the exception status unchanged. If it
needs to call something that might raise an exception, the exception state
must be backed up first and restored later (after logging any exceptions
with :c:func:`PyErr_WriteUnraisable`).
.. code-block:: c
Example::
static void
foo_dealloc(PyObject *op)
{
PyTypeObject *tp = Py_TYPE(op);
// free references and buffers here
tp->tp_free(op);
Py_DECREF(tp);
}
static void
foo_dealloc(PyObject *self)
{
PyObject *exc = PyErr_GetRaisedException();
.. warning::
if (PyObject_CallFinalizerFromDealloc(self) < 0) {
// self was resurrected.
goto done;
}
In a garbage collected Python, :c:member:`!tp_dealloc` may be called from
any Python thread, not just the thread which created the object (if the
object becomes part of a refcount cycle, that cycle might be collected by
a garbage collection on any thread). This is not a problem for Python
API calls, since the thread on which :c:member:`!tp_dealloc` is called
with an :term:`attached thread state`. However, if the object being
destroyed in turn destroys objects from some other C or C++ library, care
should be taken to ensure that destroying those objects on the thread
which called :c:member:`!tp_dealloc` will not violate any assumptions of
the library.
PyTypeObject *tp = Py_TYPE(self);
if (tp->tp_flags & Py_TPFLAGS_HAVE_GC) {
PyObject_GC_UnTrack(self);
}
// Optional, but convenient to avoid code duplication.
if (tp->tp_clear && tp->tp_clear(self) < 0) {
PyErr_WriteUnraisable(self);
}
// Any additional destruction goes here.
tp->tp_free(self);
self = NULL; // In case PyErr_WriteUnraisable() is called below.
if (tp->tp_flags & Py_TPFLAGS_HEAPTYPE) {
Py_CLEAR(tp);
}
done:
// Optional, if something was called that might have raised an
// exception.
if (PyErr_Occurred()) {
PyErr_WriteUnraisable(self);
}
PyErr_SetRaisedException(exc);
}
:c:member:`!tp_dealloc` may be called from
any Python thread, not just the thread which created the object (if the
object becomes part of a refcount cycle, that cycle might be collected by
a garbage collection on any thread). This is not a problem for Python
API calls, since the thread on which :c:member:`!tp_dealloc` is called
with an :term:`attached thread state`. However, if the object being
destroyed in turn destroys objects from some other C library, care
should be taken to ensure that destroying those objects on the thread
which called :c:member:`!tp_dealloc` will not violate any assumptions of
the library.
**Inheritance:**
This field is inherited by subtypes.
.. seealso::
:ref:`life-cycle` for details about how this slot relates to other slots.
.. c:member:: Py_ssize_t PyTypeObject.tp_vectorcall_offset
@ -1137,11 +1182,11 @@ and :c:data:`PyType_Type` effectively act as defaults.)
.. c:macro:: Py_TPFLAGS_HAVE_GC
This bit is set when the object supports garbage collection. If this bit
is set, instances must be created using :c:macro:`PyObject_GC_New` and
destroyed using :c:func:`PyObject_GC_Del`. More information in section
:ref:`supporting-cycle-detection`. This bit also implies that the
GC-related fields :c:member:`~PyTypeObject.tp_traverse` and :c:member:`~PyTypeObject.tp_clear` are present in
the type object.
is set, memory for new instances (see :c:member:`~PyTypeObject.tp_alloc`)
must be allocated using :c:macro:`PyObject_GC_New` or
:c:func:`PyType_GenericAlloc` and deallocated (see
:c:member:`~PyTypeObject.tp_free`) using :c:func:`PyObject_GC_Del`. More
information in section :ref:`supporting-cycle-detection`.
**Inheritance:**
@ -1478,6 +1523,11 @@ and :c:data:`PyType_Type` effectively act as defaults.)
heap-allocated superclass).
If they do not, the type object may not be garbage-collected.
.. note::
The :c:member:`~PyTypeObject.tp_traverse` function can be called from any
thread.
.. versionchanged:: 3.9
Heap-allocated types are expected to visit ``Py_TYPE(self)`` in
@ -1497,20 +1547,101 @@ and :c:data:`PyType_Type` effectively act as defaults.)
.. c:member:: inquiry PyTypeObject.tp_clear
An optional pointer to a clear function for the garbage collector. This is only
used if the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is set. The signature is::
An optional pointer to a clear function. The signature is::
int tp_clear(PyObject *);
The :c:member:`~PyTypeObject.tp_clear` member function is used to break reference cycles in cyclic
garbage detected by the garbage collector. Taken together, all :c:member:`~PyTypeObject.tp_clear`
functions in the system must combine to break all reference cycles. This is
subtle, and if in any doubt supply a :c:member:`~PyTypeObject.tp_clear` function. For example,
the tuple type does not implement a :c:member:`~PyTypeObject.tp_clear` function, because it's
possible to prove that no reference cycle can be composed entirely of tuples.
Therefore the :c:member:`~PyTypeObject.tp_clear` functions of other types must be sufficient to
break any cycle containing a tuple. This isn't immediately obvious, and there's
rarely a good reason to avoid implementing :c:member:`~PyTypeObject.tp_clear`.
The purpose of this function is to break reference cycles that are causing a
:term:`cyclic isolate` so that the objects can be safely destroyed. A
cleared object is a partially destroyed object; the object is not obligated
to satisfy design invariants held during normal use.
:c:member:`!tp_clear` does not need to delete references to objects that
can't participate in reference cycles, such as Python strings or Python
integers. However, it may be convenient to clear all references, and write
the type's :c:member:`~PyTypeObject.tp_dealloc` function to invoke
:c:member:`!tp_clear` to avoid code duplication. (Beware that
:c:member:`!tp_clear` might have already been called. Prefer calling
idempotent functions like :c:func:`Py_CLEAR`.)
Any non-trivial cleanup should be performed in
:c:member:`~PyTypeObject.tp_finalize` instead of :c:member:`!tp_clear`.
.. note::
If :c:member:`!tp_clear` fails to break a reference cycle then the
objects in the :term:`cyclic isolate` may remain indefinitely
uncollectable ("leak"). See :data:`gc.garbage`.
.. note::
Referents (direct and indirect) might have already been cleared; they are
not guaranteed to be in a consistent state.
.. note::
The :c:member:`~PyTypeObject.tp_clear` function can be called from any
thread.
.. note::
An object is not guaranteed to be automatically cleared before its
destructor (:c:member:`~PyTypeObject.tp_dealloc`) is called.
This function differs from the destructor
(:c:member:`~PyTypeObject.tp_dealloc`) in the following ways:
* The purpose of clearing an object is to remove references to other objects
that might participate in a reference cycle. The purpose of the
destructor, on the other hand, is a superset: it must release *all*
resources it owns, including references to objects that cannot participate
in a reference cycle (e.g., integers) as well as the object's own memory
(by calling :c:member:`~PyTypeObject.tp_free`).
* When :c:member:`!tp_clear` is called, other objects might still hold
references to the object being cleared. Because of this,
:c:member:`!tp_clear` must not deallocate the object's own memory
(:c:member:`~PyTypeObject.tp_free`). The destructor, on the other hand,
is only called when no (strong) references exist, and as such, must
safely destroy the object itself by deallocating it.
* :c:member:`!tp_clear` might never be automatically called. An object's
destructor, on the other hand, will be automatically called some time
after the object becomes unreachable (i.e., either there are no references
to the object or the object is a member of a :term:`cyclic isolate`).
No guarantees are made about when, if, or how often Python automatically
clears an object, except:
* Python will not automatically clear an object if it is reachable, i.e.,
there is a reference to it and it is not a member of a :term:`cyclic
isolate`.
* Python will not automatically clear an object if it has not been
automatically finalized (see :c:member:`~PyTypeObject.tp_finalize`). (If
the finalizer resurrected the object, the object may or may not be
automatically finalized again before it is cleared.)
* If an object is a member of a :term:`cyclic isolate`, Python will not
automatically clear it if any member of the cyclic isolate has not yet
been automatically finalized (:c:member:`~PyTypeObject.tp_finalize`).
* Python will not destroy an object until after any automatic calls to its
:c:member:`!tp_clear` function have returned. This ensures that the act
of breaking a reference cycle does not invalidate the ``self`` pointer
while :c:member:`!tp_clear` is still executing.
* Python will not automatically call :c:member:`!tp_clear` multiple times
concurrently.
CPython currently only automatically clears objects as needed to break
reference cycles in a :term:`cyclic isolate`, but future versions might
clear objects regularly before their destruction.
Taken together, all :c:member:`~PyTypeObject.tp_clear` functions in the
system must combine to break all reference cycles. This is subtle, and if
in any doubt supply a :c:member:`~PyTypeObject.tp_clear` function. For
example, the tuple type does not implement a
:c:member:`~PyTypeObject.tp_clear` function, because it's possible to prove
that no reference cycle can be composed entirely of tuples. Therefore the
:c:member:`~PyTypeObject.tp_clear` functions of other types are responsible
for breaking any cycle containing a tuple. This isn't immediately obvious,
and there's rarely a good reason to avoid implementing
:c:member:`~PyTypeObject.tp_clear`.
Implementations of :c:member:`~PyTypeObject.tp_clear` should drop the instance's references to
those of its members that may be Python objects, and set its pointers to those
@ -1545,18 +1676,6 @@ and :c:data:`PyType_Type` effectively act as defaults.)
PyObject_ClearManagedDict((PyObject*)self);
Note that :c:member:`~PyTypeObject.tp_clear` is not *always* called
before an instance is deallocated. For example, when reference counting
is enough to determine that an object is no longer used, the cyclic garbage
collector is not involved and :c:member:`~PyTypeObject.tp_dealloc` is
called directly.
Because the goal of :c:member:`~PyTypeObject.tp_clear` functions is to break reference cycles,
it's not necessary to clear contained objects like Python strings or Python
integers, which can't participate in reference cycles. On the other hand, it may
be convenient to clear all contained Python objects, and write the type's
:c:member:`~PyTypeObject.tp_dealloc` function to invoke :c:member:`~PyTypeObject.tp_clear`.
More information about Python's garbage collection scheme can be found in
section :ref:`supporting-cycle-detection`.
@ -1569,6 +1688,10 @@ and :c:data:`PyType_Type` effectively act as defaults.)
:c:member:`~PyTypeObject.tp_clear` are all inherited from the base type if they are all zero in
the subtype.
.. seealso::
:ref:`life-cycle` for details about how this slot relates to other slots.
.. c:member:: richcmpfunc PyTypeObject.tp_richcompare
@ -1945,18 +2068,17 @@ and :c:data:`PyType_Type` effectively act as defaults.)
**Inheritance:**
This field is inherited by static subtypes, but not by dynamic
subtypes (subtypes created by a class statement).
Static subtypes inherit this slot, which will be
:c:func:`PyType_GenericAlloc` if inherited from :class:`object`.
:ref:`Heap subtypes <heap-types>` do not inherit this slot.
**Default:**
For dynamic subtypes, this field is always set to
:c:func:`PyType_GenericAlloc`, to force a standard heap
allocation strategy.
For heap subtypes, this field is always set to
:c:func:`PyType_GenericAlloc`.
For static subtypes, :c:data:`PyBaseObject_Type` uses
:c:func:`PyType_GenericAlloc`. That is the recommended value
for all statically defined types.
For static subtypes, this slot is inherited (see above).
.. c:member:: newfunc PyTypeObject.tp_new
@ -2004,20 +2126,27 @@ and :c:data:`PyType_Type` effectively act as defaults.)
void tp_free(void *self);
An initializer that is compatible with this signature is :c:func:`PyObject_Free`.
This function must free the memory allocated by
:c:member:`~PyTypeObject.tp_alloc`.
**Inheritance:**
This field is inherited by static subtypes, but not by dynamic
subtypes (subtypes created by a class statement)
Static subtypes inherit this slot, which will be :c:func:`PyObject_Free` if
inherited from :class:`object`. Exception: If the type supports garbage
collection (i.e., the :c:macro:`Py_TPFLAGS_HAVE_GC` flag is set in
:c:member:`~PyTypeObject.tp_flags`) and it would inherit
:c:func:`PyObject_Free`, then this slot is not inherited but instead defaults
to :c:func:`PyObject_GC_Del`.
:ref:`Heap subtypes <heap-types>` do not inherit this slot.
**Default:**
In dynamic subtypes, this field is set to a deallocator suitable to
match :c:func:`PyType_GenericAlloc` and the value of the
:c:macro:`Py_TPFLAGS_HAVE_GC` flag bit.
For :ref:`heap subtypes <heap-types>`, this slot defaults to a deallocator suitable to match
:c:func:`PyType_GenericAlloc` and the value of the
:c:macro:`Py_TPFLAGS_HAVE_GC` flag.
For static subtypes, :c:data:`PyBaseObject_Type` uses :c:func:`PyObject_Free`.
For static subtypes, this slot is inherited (see above).
.. c:member:: inquiry PyTypeObject.tp_is_gc
@ -2144,29 +2273,138 @@ and :c:data:`PyType_Type` effectively act as defaults.)
.. c:member:: destructor PyTypeObject.tp_finalize
An optional pointer to an instance finalization function. Its signature is::
An optional pointer to an instance finalization function. This is the C
implementation of the :meth:`~object.__del__` special method. Its signature
is::
void tp_finalize(PyObject *self);
If :c:member:`~PyTypeObject.tp_finalize` is set, the interpreter calls it once when
finalizing an instance. It is called either from the garbage
collector (if the instance is part of an isolated reference cycle) or
just before the object is deallocated. Either way, it is guaranteed
to be called before attempting to break reference cycles, ensuring
that it finds the object in a sane state.
The primary purpose of finalization is to perform any non-trivial cleanup
that must be performed before the object is destroyed, while the object and
any other objects it directly or indirectly references are still in a
consistent state. The finalizer is allowed to execute
arbitrary Python code.
:c:member:`~PyTypeObject.tp_finalize` should not mutate the current exception status;
therefore, a recommended way to write a non-trivial finalizer is::
Before Python automatically finalizes an object, some of the object's direct
or indirect referents might have themselves been automatically finalized.
However, none of the referents will have been automatically cleared
(:c:member:`~PyTypeObject.tp_clear`) yet.
Other non-finalized objects might still be using a finalized object, so the
finalizer must leave the object in a sane state (e.g., invariants are still
met).
.. note::
After Python automatically finalizes an object, Python might start
automatically clearing (:c:member:`~PyTypeObject.tp_clear`) the object
and its referents (direct and indirect). Cleared objects are not
guaranteed to be in a consistent state; a finalized object must be able
to tolerate cleared referents.
.. note::
An object is not guaranteed to be automatically finalized before its
destructor (:c:member:`~PyTypeObject.tp_dealloc`) is called. It is
recommended to call :c:func:`PyObject_CallFinalizerFromDealloc` at the
beginning of :c:member:`!tp_dealloc` to guarantee that the object is
always finalized before destruction.
.. note::
The :c:member:`~PyTypeObject.tp_finalize` function can be called from any
thread, although the :term:`GIL` will be held.
.. note::
The :c:member:`!tp_finalize` function can be called during shutdown,
after some global variables have been deleted. See the documentation of
the :meth:`~object.__del__` method for details.
When Python finalizes an object, it behaves like the following algorithm:
#. Python might mark the object as *finalized*. Currently, Python always
marks objects whose type supports garbage collection (i.e., the
:c:macro:`Py_TPFLAGS_HAVE_GC` flag is set in
:c:member:`~PyTypeObject.tp_flags`) and never marks other types of
objects; this might change in a future version.
#. If the object is not marked as *finalized* and its
:c:member:`!tp_finalize` finalizer function is non-``NULL``, the
finalizer function is called.
#. If the finalizer function was called and the finalizer made the object
reachable (i.e., there is a reference to the object and it is not a
member of a :term:`cyclic isolate`), then the finalizer is said to have
*resurrected* the object. It is unspecified whether the finalizer can
also resurrect the object by adding a new reference to the object that
does not make it reachable, i.e., the object is (still) a member of a
cyclic isolate.
#. If the finalizer resurrected the object, the object's pending destruction
is canceled and the object's *finalized* mark might be removed if
present. Currently, Python never removes the *finalized* mark; this
might change in a future version.
*Automatic finalization* refers to any finalization performed by Python
except via calls to :c:func:`PyObject_CallFinalizer` or
:c:func:`PyObject_CallFinalizerFromDealloc`. No guarantees are made about
when, if, or how often an object is automatically finalized, except:
* Python will not automatically finalize an object if it is reachable, i.e.,
there is a reference to it and it is not a member of a :term:`cyclic
isolate`.
* Python will not automatically finalize an object if finalizing it would
not mark the object as *finalized*. Currently, this applies to objects
whose type does not support garbage collection, i.e., the
:c:macro:`Py_TPFLAGS_HAVE_GC` flag is not set. Such objects can still be
manually finalized by calling :c:func:`PyObject_CallFinalizer` or
:c:func:`PyObject_CallFinalizerFromDealloc`.
* Python will not automatically finalize any two members of a :term:`cyclic
isolate` concurrently.
* Python will not automatically finalize an object after it has
automatically cleared (:c:member:`~PyTypeObject.tp_clear`) the object.
* If an object is a member of a :term:`cyclic isolate`, Python will not
automatically finalize it after automatically clearing (see
:c:member:`~PyTypeObject.tp_clear`) any other member.
* Python will automatically finalize every member of a :term:`cyclic
isolate` before it automatically clears (see
:c:member:`~PyTypeObject.tp_clear`) any of them.
* If Python is going to automatically clear an object
(:c:member:`~PyTypeObject.tp_clear`), it will automatically finalize the
object first.
Python currently only automatically finalizes objects that are members of a
:term:`cyclic isolate`, but future versions might finalize objects regularly
before their destruction.
To manually finalize an object, do not call this function directly; call
:c:func:`PyObject_CallFinalizer` or
:c:func:`PyObject_CallFinalizerFromDealloc` instead.
:c:member:`~PyTypeObject.tp_finalize` should leave the current exception
status unchanged. The recommended way to write a non-trivial finalizer is
to back up the exception at the beginning by calling
:c:func:`PyErr_GetRaisedException` and restore the exception at the end by
calling :c:func:`PyErr_SetRaisedException`. If an exception is encountered
in the middle of the finalizer, log and clear it with
:c:func:`PyErr_WriteUnraisable` or :c:func:`PyErr_FormatUnraisable`. For
example::
static void
local_finalize(PyObject *self)
foo_finalize(PyObject *self)
{
/* Save the current exception, if any. */
// Save the current exception, if any.
PyObject *exc = PyErr_GetRaisedException();
/* ... */
// ...
/* Restore the saved exception. */
if (do_something_that_might_raise() != success_indicator) {
PyErr_WriteUnraisable(self);
goto done;
}
done:
// Restore the saved exception. This silently discards any exception
// raised above, so be sure to call PyErr_WriteUnraisable first if
// necessary.
PyErr_SetRaisedException(exc);
}
@ -2182,7 +2420,13 @@ and :c:data:`PyType_Type` effectively act as defaults.)
:c:macro:`Py_TPFLAGS_HAVE_FINALIZE` flags bit in order for this field to be
used. This is no longer required.
.. seealso:: "Safe object finalization" (:pep:`442`)
.. seealso::
* :pep:`442`: "Safe object finalization"
* :ref:`life-cycle` for details about how this slot relates to other
slots.
* :c:func:`PyObject_CallFinalizer`
* :c:func:`PyObject_CallFinalizerFromDealloc`
.. c:member:: vectorcallfunc PyTypeObject.tp_vectorcall