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

Co-authored-by: Petr Viktorin <encukou@gmail.com>
Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
This commit is contained in:
Richard Hansen 2025-05-20 08:25:50 -04:00 committed by GitHub
parent 306f9e04e5
commit 3246ea514d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 1384 additions and 135 deletions

2
.gitattributes vendored
View File

@ -10,6 +10,7 @@
*.ico binary
*.jpg binary
*.pck binary
*.pdf binary
*.png binary
*.psd binary
*.tar binary
@ -67,6 +68,7 @@ PCbuild/readme.txt dos
**/clinic/*.cpp.h generated
**/clinic/*.h.h generated
*_db.h generated
Doc/c-api/lifecycle.dot.svg generated
Doc/data/stable_abi.dat generated
Doc/library/token-list.inc generated
Include/internal/pycore_ast.h generated

View File

@ -16,7 +16,20 @@ Allocating Objects on the Heap
Initialize a newly allocated object *op* with its type and initial
reference. Returns the initialized object. Other fields of the object are
not affected.
not initialized. Despite its name, this function is unrelated to the
object's :meth:`~object.__init__` method (:c:member:`~PyTypeObject.tp_init`
slot). Specifically, this function does **not** call the object's
:meth:`!__init__` method.
In general, consider this function to be a low-level routine. Use
:c:member:`~PyTypeObject.tp_alloc` where possible.
For implementing :c:member:`!tp_alloc` for your type, prefer
:c:func:`PyType_GenericAlloc` or :c:func:`PyObject_New`.
.. note::
This function only initializes the object's memory corresponding to the
initial :c:type:`PyObject` structure. It does not zero the rest.
.. c:function:: PyVarObject* PyObject_InitVar(PyVarObject *op, PyTypeObject *type, Py_ssize_t size)
@ -24,38 +37,107 @@ Allocating Objects on the Heap
This does everything :c:func:`PyObject_Init` does, and also initializes the
length information for a variable-size object.
.. note::
This function only initializes some of the object's memory. It does not
zero the rest.
.. c:macro:: PyObject_New(TYPE, typeobj)
Allocate a new Python object using the C structure type *TYPE*
and the Python type object *typeobj* (``PyTypeObject*``).
Fields not defined by the Python object header are not initialized.
The caller will own the only reference to the object
(i.e. its reference count will be one).
The size of the memory allocation is determined from the
:c:member:`~PyTypeObject.tp_basicsize` field of the type object.
Allocates a new Python object using the C structure type *TYPE* and the
Python type object *typeobj* (``PyTypeObject*``) by calling
:c:func:`PyObject_Malloc` to allocate memory and initializing it like
:c:func:`PyObject_Init`. The caller will own the only reference to the
object (i.e. its reference count will be one).
Note that this function is unsuitable if *typeobj* has
:c:macro:`Py_TPFLAGS_HAVE_GC` set. For such objects,
use :c:func:`PyObject_GC_New` instead.
Avoid calling this directly to allocate memory for an object; call the type's
:c:member:`~PyTypeObject.tp_alloc` slot instead.
When populating a type's :c:member:`~PyTypeObject.tp_alloc` slot,
:c:func:`PyType_GenericAlloc` is preferred over a custom function that
simply calls this macro.
This macro does not call :c:member:`~PyTypeObject.tp_alloc`,
:c:member:`~PyTypeObject.tp_new` (:meth:`~object.__new__`), or
:c:member:`~PyTypeObject.tp_init` (:meth:`~object.__init__`).
This cannot be used for objects with :c:macro:`Py_TPFLAGS_HAVE_GC` set in
:c:member:`~PyTypeObject.tp_flags`; use :c:macro:`PyObject_GC_New` instead.
Memory allocated by this macro must be freed with :c:func:`PyObject_Free`
(usually called via the object's :c:member:`~PyTypeObject.tp_free` slot).
.. note::
The returned memory is not guaranteed to have been completely zeroed
before it was initialized.
.. note::
This macro does not construct a fully initialized object of the given
type; it merely allocates memory and prepares it for further
initialization by :c:member:`~PyTypeObject.tp_init`. To construct a
fully initialized object, call *typeobj* instead. For example::
PyObject *foo = PyObject_CallNoArgs((PyObject *)&PyFoo_Type);
.. seealso::
* :c:func:`PyObject_Free`
* :c:macro:`PyObject_GC_New`
* :c:func:`PyType_GenericAlloc`
* :c:member:`~PyTypeObject.tp_alloc`
.. c:macro:: PyObject_NewVar(TYPE, typeobj, size)
Allocate a new Python object using the C structure type *TYPE* and the
Python type object *typeobj* (``PyTypeObject*``).
Fields not defined by the Python object header
are not initialized. The allocated memory allows for the *TYPE* structure
plus *size* (``Py_ssize_t``) fields of the size
given by the :c:member:`~PyTypeObject.tp_itemsize` field of
*typeobj*. This is useful for implementing objects like tuples, which are
able to determine their size at construction time. Embedding the array of
fields into the same allocation decreases the number of allocations,
improving the memory management efficiency.
Like :c:macro:`PyObject_New` except:
Note that this function is unsuitable if *typeobj* has
:c:macro:`Py_TPFLAGS_HAVE_GC` set. For such objects,
use :c:func:`PyObject_GC_NewVar` instead.
* It allocates enough memory for the *TYPE* structure plus *size*
(``Py_ssize_t``) fields of the size given by the
:c:member:`~PyTypeObject.tp_itemsize` field of *typeobj*.
* The memory is initialized like :c:func:`PyObject_InitVar`.
This is useful for implementing objects like tuples, which are able to
determine their size at construction time. Embedding the array of fields
into the same allocation decreases the number of allocations, improving the
memory management efficiency.
Avoid calling this directly to allocate memory for an object; call the type's
:c:member:`~PyTypeObject.tp_alloc` slot instead.
When populating a type's :c:member:`~PyTypeObject.tp_alloc` slot,
:c:func:`PyType_GenericAlloc` is preferred over a custom function that
simply calls this macro.
This cannot be used for objects with :c:macro:`Py_TPFLAGS_HAVE_GC` set in
:c:member:`~PyTypeObject.tp_flags`; use :c:macro:`PyObject_GC_NewVar`
instead.
Memory allocated by this function must be freed with :c:func:`PyObject_Free`
(usually called via the object's :c:member:`~PyTypeObject.tp_free` slot).
.. note::
The returned memory is not guaranteed to have been completely zeroed
before it was initialized.
.. note::
This macro does not construct a fully initialized object of the given
type; it merely allocates memory and prepares it for further
initialization by :c:member:`~PyTypeObject.tp_init`. To construct a
fully initialized object, call *typeobj* instead. For example::
PyObject *list_instance = PyObject_CallNoArgs((PyObject *)&PyList_Type);
.. seealso::
* :c:func:`PyObject_Free`
* :c:macro:`PyObject_GC_NewVar`
* :c:func:`PyType_GenericAlloc`
* :c:member:`~PyTypeObject.tp_alloc`
.. c:function:: void PyObject_Del(void *op)

View File

@ -57,11 +57,49 @@ rules:
Analogous to :c:macro:`PyObject_New` but for container objects with the
:c:macro:`Py_TPFLAGS_HAVE_GC` flag set.
Do not call this directly to allocate memory for an object; call the type's
:c:member:`~PyTypeObject.tp_alloc` slot instead.
When populating a type's :c:member:`~PyTypeObject.tp_alloc` slot,
:c:func:`PyType_GenericAlloc` is preferred over a custom function that
simply calls this macro.
Memory allocated by this macro must be freed with
:c:func:`PyObject_GC_Del` (usually called via the object's
:c:member:`~PyTypeObject.tp_free` slot).
.. seealso::
* :c:func:`PyObject_GC_Del`
* :c:macro:`PyObject_New`
* :c:func:`PyType_GenericAlloc`
* :c:member:`~PyTypeObject.tp_alloc`
.. c:macro:: PyObject_GC_NewVar(TYPE, typeobj, size)
Analogous to :c:macro:`PyObject_NewVar` but for container objects with the
:c:macro:`Py_TPFLAGS_HAVE_GC` flag set.
Do not call this directly to allocate memory for an object; call the type's
:c:member:`~PyTypeObject.tp_alloc` slot instead.
When populating a type's :c:member:`~PyTypeObject.tp_alloc` slot,
:c:func:`PyType_GenericAlloc` is preferred over a custom function that
simply calls this macro.
Memory allocated by this macro must be freed with
:c:func:`PyObject_GC_Del` (usually called via the object's
:c:member:`~PyTypeObject.tp_free` slot).
.. seealso::
* :c:func:`PyObject_GC_Del`
* :c:macro:`PyObject_NewVar`
* :c:func:`PyType_GenericAlloc`
* :c:member:`~PyTypeObject.tp_alloc`
.. c:function:: PyObject* PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *type, size_t extra_size)
Analogous to :c:macro:`PyObject_GC_New` but allocates *extra_size*
@ -73,6 +111,10 @@ rules:
The extra data will be deallocated with the object, but otherwise it is
not managed by Python.
Memory allocated by this function must be freed with
:c:func:`PyObject_GC_Del` (usually called via the object's
:c:member:`~PyTypeObject.tp_free` slot).
.. warning::
The function is marked as unstable because the final mechanism
for reserving extra data after an instance is not yet decided.
@ -136,6 +178,21 @@ rules:
Releases memory allocated to an object using :c:macro:`PyObject_GC_New` or
:c:macro:`PyObject_GC_NewVar`.
Do not call this directly to free an object's memory; call the type's
:c:member:`~PyTypeObject.tp_free` slot instead.
Do not use this for memory allocated by :c:macro:`PyObject_New`,
:c:macro:`PyObject_NewVar`, or related allocation functions; use
:c:func:`PyObject_Free` instead.
.. seealso::
* :c:func:`PyObject_Free` is the non-GC equivalent of this function.
* :c:macro:`PyObject_GC_New`
* :c:macro:`PyObject_GC_NewVar`
* :c:func:`PyType_GenericAlloc`
* :c:member:`~PyTypeObject.tp_free`
.. c:function:: void PyObject_GC_UnTrack(void *op)

156
Doc/c-api/lifecycle.dot Normal file
View File

@ -0,0 +1,156 @@
digraph "Life Events" {
graph [
fontnames="svg"
fontsize=12.0
id="life_events_graph"
layout="dot"
margin="0,0"
ranksep=0.25
stylesheet="lifecycle.dot.css"
]
node [
fontname="Courier"
fontsize=12.0
]
edge [
fontname="Times-Italic"
fontsize=12.0
]
"start" [fontname="Times-Italic" shape=plain label=< start > style=invis]
{
rank="same"
"tp_new" [href="typeobj.html#c.PyTypeObject.tp_new" target="_top"]
"tp_alloc" [href="typeobj.html#c.PyTypeObject.tp_alloc" target="_top"]
}
"tp_init" [href="typeobj.html#c.PyTypeObject.tp_init" target="_top"]
"reachable" [fontname="Times-Italic" shape=box]
"tp_traverse" [
href="typeobj.html#c.PyTypeObject.tp_traverse"
ordering="in"
target="_top"
]
"finalized?" [
fontname="Times-Italic"
label=<marked as<br/>finalized?>
ordering="in"
shape=diamond
tooltip="marked as finalized?"
]
"tp_finalize" [
href="typeobj.html#c.PyTypeObject.tp_finalize"
ordering="in"
target="_top"
]
"tp_clear" [href="typeobj.html#c.PyTypeObject.tp_clear" target="_top"]
"uncollectable" [
fontname="Times-Italic"
label=<uncollectable<br/>(leaked)>
shape=box
tooltip="uncollectable (leaked)"
]
"tp_dealloc" [
href="typeobj.html#c.PyTypeObject.tp_dealloc"
ordering="in"
target="_top"
]
"tp_free" [href="typeobj.html#c.PyTypeObject.tp_free" target="_top"]
"start" -> "tp_new" [
label=< type call >
]
"tp_new" -> "tp_alloc" [
label=< direct call > arrowhead=empty
labeltooltip="tp_new to tp_alloc: direct call"
tooltip="tp_new to tp_alloc: direct call"
]
"tp_new" -> "tp_init" [tooltip="tp_new to tp_init"]
"tp_init" -> "reachable" [tooltip="tp_init to reachable"]
"reachable" -> "tp_traverse" [
dir="back"
label=< not in a <br/> cyclic <br/> isolate >
labeltooltip="tp_traverse to reachable: not in a cyclic isolate"
tooltip="tp_traverse to reachable: not in a cyclic isolate"
]
"reachable" -> "tp_traverse" [
label=< periodic <br/> cyclic isolate <br/> detection >
labeltooltip="reachable to tp_traverse: periodic cyclic isolate detection"
tooltip="reachable to tp_traverse: periodic cyclic isolate detection"
]
"reachable" -> "tp_init" [tooltip="reachable to tp_init"]
"reachable" -> "tp_finalize" [
dir="back"
label=< resurrected <br/> (maybe remove <br/> finalized mark) >
labeltooltip="tp_finalize to reachable: resurrected (maybe remove finalized mark)"
tooltip="tp_finalize to reachable: resurrected (maybe remove finalized mark)"
]
"tp_traverse" -> "finalized?" [
label=< cyclic <br/> isolate >
labeltooltip="tp_traverse to finalized?: cyclic isolate"
tooltip="tp_traverse to finalized?: cyclic isolate"
]
"reachable" -> "finalized?" [
label=< no refs >
labeltooltip="reachable to finalized?: no refs"
tooltip="reachable to finalized?: no refs"
]
"finalized?" -> "tp_finalize" [
label=< no (mark <br/> as finalized) >
labeltooltip="finalized? to tp_finalize: no (mark as finalized)"
tooltip="finalized? to tp_finalize: no (mark as finalized)"
]
"finalized?" -> "tp_clear" [
label=< yes >
labeltooltip="finalized? to tp_clear: yes"
tooltip="finalized? to tp_clear: yes"
]
"tp_finalize" -> "tp_clear" [
label=< no refs or <br/> cyclic isolate >
labeltooltip="tp_finalize to tp_clear: no refs or cyclic isolate"
tooltip="tp_finalize to tp_clear: no refs or cyclic isolate"
]
"tp_finalize" -> "tp_dealloc" [
arrowtail=empty
dir="back"
href="lifecycle.html#c.PyObject_CallFinalizerFromDealloc"
style=dashed
label=< recommended<br/> call (see<br/> explanation)>
labeltooltip="tp_dealloc to tp_finalize: recommended call (see explanation)"
target="_top"
tooltip="tp_dealloc to tp_finalize: recommended call (see explanation)"
]
"tp_finalize" -> "tp_dealloc" [
label=< no refs >
labeltooltip="tp_finalize to tp_dealloc: no refs"
tooltip="tp_finalize to tp_dealloc: no refs"
]
"tp_clear" -> "tp_dealloc" [
label=< no refs >
labeltooltip="tp_clear to tp_dealloc: no refs"
tooltip="tp_clear to tp_dealloc: no refs"
]
"tp_clear" -> "uncollectable" [
label=< cyclic <br/> isolate >
labeltooltip="tp_clear to uncollectable: cyclic isolate"
tooltip="tp_clear to uncollectable: cyclic isolate"
]
"uncollectable" -> "tp_dealloc" [
style=invis
tooltip="uncollectable to tp_dealloc"
]
"reachable" -> "uncollectable" [
label=< cyclic <br/> isolate <br/> (no GC <br/> support) >
labeltooltip="reachable to uncollectable: cyclic isolate (no GC support)"
tooltip="reachable to uncollectable: cyclic isolate (no GC support)"
]
"reachable" -> "tp_dealloc" [
label=< no refs>
labeltooltip="reachable to tp_dealloc: no refs"
]
"tp_dealloc" -> "tp_free" [
arrowhead=empty
label=< direct call >
labeltooltip="tp_dealloc to tp_free: direct call"
tooltip="tp_dealloc to tp_free: direct call"
]
}

View File

@ -0,0 +1,21 @@
#life_events_graph {
--svg-fgcolor: currentcolor;
--svg-bgcolor: transparent;
}
#life_events_graph a {
color: inherit;
}
#life_events_graph [stroke="black"] {
stroke: var(--svg-fgcolor);
}
#life_events_graph text,
#life_events_graph [fill="black"] {
fill: var(--svg-fgcolor);
}
#life_events_graph [fill="white"] {
fill: var(--svg-bgcolor);
}
#life_events_graph [fill="none"] {
/* On links, setting fill will make the entire shape clickable */
fill: var(--svg-bgcolor);
}

BIN
Doc/c-api/lifecycle.dot.pdf Normal file

Binary file not shown.

374
Doc/c-api/lifecycle.dot.svg generated Normal file
View File

@ -0,0 +1,374 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet href="lifecycle.dot.css" type="text/css"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 12.2.0 (0)
-->
<!-- Title: Life Events Pages: 1 -->
<svg width="465pt" height="845pt"
viewBox="0.00 0.00 465.30 845.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="life_events_graph" class="graph" transform="scale(1 1) rotate(0) translate(4 841)">
<title>Life Events</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-841 461.3,-841 461.3,4 -4,4"/>
<!-- start -->
<!-- tp_new -->
<g id="life_events_graph_node2" class="node">
<title>tp_new</title>
<g id="a_life_events_graph_node2"><a xlink:href="typeobj.html#c.PyTypeObject.tp_new" xlink:title="tp_new" target="_top">
<ellipse fill="none" stroke="black" cx="192.8" cy="-772.5" rx="38.8" ry="18"/>
<text text-anchor="middle" x="192.8" y="-768.23" font-family="monospace,monospace" font-size="12.00">tp_new</text>
</a>
</g>
</g>
<!-- start&#45;&gt;tp_new -->
<g id="life_events_graph_edge1" class="edge">
<title>start&#45;&gt;tp_new</title>
<g id="a_life_events_graph_edge1"><a xlink:title="start to tp_new: type call">
<path fill="none" stroke="black" d="M192.8,-822.95C192.8,-817.85 192.8,-810.09 192.8,-802.22"/>
<polygon fill="black" stroke="black" points="196.3,-802.42 192.8,-792.42 189.3,-802.42 196.3,-802.42"/>
</a>
</g>
<g id="a_life_events_graph_edge1&#45;label"><a xlink:title="start to tp_new: type call">
<text text-anchor="start" x="192.8" y="-802.35" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;&#160;&#160;type call &#160;</text>
</a>
</g>
</g>
<!-- tp_alloc -->
<g id="life_events_graph_node3" class="node">
<title>tp_alloc</title>
<g id="a_life_events_graph_node3"><a xlink:href="typeobj.html#c.PyTypeObject.tp_alloc" xlink:title="tp_alloc" target="_top">
<ellipse fill="none" stroke="black" cx="373.8" cy="-772.5" rx="48.34" ry="18"/>
<text text-anchor="middle" x="373.8" y="-768.23" font-family="monospace,monospace" font-size="12.00">tp_alloc</text>
</a>
</g>
</g>
<!-- tp_new&#45;&gt;tp_alloc -->
<g id="life_events_graph_edge2" class="edge">
<title>tp_new&#45;&gt;tp_alloc</title>
<g id="a_life_events_graph_edge2"><a xlink:title="tp_new to tp_alloc: direct call">
<path fill="none" stroke="black" d="M232.07,-772.5C256,-772.5 287.05,-772.5 313.98,-772.5"/>
<polygon fill="none" stroke="black" points="313.73,-776 323.73,-772.5 313.73,-769 313.73,-776"/>
</a>
</g>
<g id="a_life_events_graph_edge2&#45;label"><a xlink:title="tp_new to tp_alloc: direct call">
<text text-anchor="start" x="240.65" y="-778.35" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;direct call &#160;</text>
</a>
</g>
</g>
<!-- tp_init -->
<g id="life_events_graph_node4" class="node">
<title>tp_init</title>
<g id="a_life_events_graph_node4"><a xlink:href="typeobj.html#c.PyTypeObject.tp_init" xlink:title="tp_init" target="_top">
<ellipse fill="none" stroke="black" cx="192.8" cy="-717.5" rx="43.57" ry="18"/>
<text text-anchor="middle" x="192.8" y="-713.23" font-family="monospace,monospace" font-size="12.00">tp_init</text>
</a>
</g>
</g>
<!-- tp_new&#45;&gt;tp_init -->
<g id="life_events_graph_edge3" class="edge">
<title>tp_new&#45;&gt;tp_init</title>
<g id="a_life_events_graph_edge3"><a xlink:title="tp_new to tp_init">
<path fill="none" stroke="black" d="M192.8,-754.15C192.8,-751.83 192.8,-749.42 192.8,-746.98"/>
<polygon fill="black" stroke="black" points="196.3,-747.23 192.8,-737.23 189.3,-747.23 196.3,-747.23"/>
</a>
</g>
</g>
<!-- reachable -->
<g id="life_events_graph_node5" class="node">
<title>reachable</title>
<polygon fill="none" stroke="black" points="230.8,-680.5 154.8,-680.5 154.8,-644.5 230.8,-644.5 230.8,-680.5"/>
<text text-anchor="middle" x="192.8" y="-658.23" font-family="serif,serif" font-style="italic" font-size="12.00">reachable</text>
</g>
<!-- tp_init&#45;&gt;reachable -->
<g id="life_events_graph_edge4" class="edge">
<title>tp_init&#45;&gt;reachable</title>
<g id="a_life_events_graph_edge4"><a xlink:title="tp_init to reachable">
<path fill="none" stroke="black" d="M186.44,-699.44C186.24,-697.12 186.11,-694.69 186.07,-692.24"/>
<polygon fill="black" stroke="black" points="189.56,-692.51 186.37,-682.41 182.56,-692.29 189.56,-692.51"/>
</a>
</g>
</g>
<!-- reachable&#45;&gt;tp_init -->
<g id="life_events_graph_edge7" class="edge">
<title>reachable&#45;&gt;tp_init</title>
<g id="a_life_events_graph_edge7"><a xlink:title="reachable to tp_init">
<path fill="none" stroke="black" d="M199.18,-680.89C199.37,-683.22 199.49,-685.65 199.53,-688.11"/>
<polygon fill="black" stroke="black" points="196.04,-687.81 199.2,-697.93 203.04,-688.05 196.04,-687.81"/>
</a>
</g>
</g>
<!-- tp_traverse -->
<g id="life_events_graph_node6" class="node">
<title>tp_traverse</title>
<g id="a_life_events_graph_node6"><a xlink:href="typeobj.html#c.PyTypeObject.tp_traverse" xlink:title="tp_traverse" target="_top">
<ellipse fill="none" stroke="black" cx="136.8" cy="-565.75" rx="62.65" ry="18"/>
<text text-anchor="middle" x="136.8" y="-561.48" font-family="monospace,monospace" font-size="12.00">tp_traverse</text>
</a>
</g>
</g>
<!-- reachable&#45;&gt;tp_traverse -->
<g id="life_events_graph_edge5" class="edge">
<title>reachable&#45;&gt;tp_traverse</title>
<g id="a_life_events_graph_edge5"><a xlink:title="tp_traverse to reachable: not in a cyclic isolate">
<path fill="none" stroke="black" d="M143.43,-658.77C108.3,-655.68 65.38,-649.16 54.05,-635.5 41.91,-620.88 42.8,-608.07 54.05,-592.75 60.55,-583.89 70.07,-577.97 80.37,-574.03"/>
<polygon fill="black" stroke="black" points="142.76,-662.23 153.01,-659.54 143.32,-655.25 142.76,-662.23"/>
</a>
</g>
<g id="a_life_events_graph_edge5&#45;label"><a xlink:title="tp_traverse to reachable: not in a cyclic isolate">
<text text-anchor="start" x="54.05" y="-624.1" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;not in a &#160;</text>
<text text-anchor="start" x="59.67" y="-609.85" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;cyclic &#160;</text>
<text text-anchor="start" x="57.05" y="-595.6" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;isolate &#160;</text>
</a>
</g>
</g>
<!-- reachable&#45;&gt;tp_traverse -->
<g id="life_events_graph_edge6" class="edge">
<title>reachable&#45;&gt;tp_traverse</title>
<g id="a_life_events_graph_edge6"><a xlink:title="reachable to tp_traverse: periodic cyclic isolate detection">
<path fill="none" stroke="black" d="M154.41,-650.07C147.94,-646.44 142.04,-641.69 138.05,-635.5 130.52,-623.82 129.57,-608.56 130.79,-595.38"/>
<polygon fill="black" stroke="black" points="134.25,-595.91 132.17,-585.52 127.31,-594.94 134.25,-595.91"/>
</a>
</g>
<g id="a_life_events_graph_edge6&#45;label"><a xlink:title="reachable to tp_traverse: periodic cyclic isolate detection">
<text text-anchor="start" x="154.17" y="-624.1" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;periodic &#160;</text>
<text text-anchor="start" x="138.05" y="-609.85" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;cyclic isolate &#160;&#160;</text>
<text text-anchor="start" x="151.17" y="-595.6" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;detection &#160;</text>
</a>
</g>
</g>
<!-- finalized? -->
<g id="life_events_graph_node7" class="node">
<title>finalized?</title>
<g id="a_life_events_graph_node7"><a xlink:title="marked as finalized?">
<polygon fill="none" stroke="black" points="191.8,-487 112.05,-450.5 191.8,-414 271.55,-450.5 191.8,-487"/>
<text text-anchor="start" x="159.92" y="-453.35" font-family="serif,serif" font-style="italic" font-size="12.00">marked as</text>
<text text-anchor="start" x="162.92" y="-439.1" font-family="serif,serif" font-style="italic" font-size="12.00">finalized?</text>
</a>
</g>
</g>
<!-- reachable&#45;&gt;finalized? -->
<g id="life_events_graph_edge10" class="edge">
<title>reachable&#45;&gt;finalized?</title>
<g id="a_life_events_graph_edge10"><a xlink:title="reachable to finalized?: no refs">
<path fill="none" stroke="black" d="M227.72,-644.32C230.51,-641.73 232.96,-638.8 234.8,-635.5 244.04,-618.9 235.48,-611.74 234.8,-592.75 233.24,-549.67 243.64,-536.1 227.8,-496 226.37,-492.38 224.53,-488.82 222.45,-485.4"/>
<polygon fill="black" stroke="black" points="225.47,-483.62 216.91,-477.39 219.72,-487.61 225.47,-483.62"/>
</a>
</g>
<g id="a_life_events_graph_edge10&#45;label"><a xlink:title="reachable to finalized?: no refs">
<text text-anchor="start" x="236.45" y="-561.48" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;no refs &#160;</text>
</a>
</g>
</g>
<!-- tp_finalize -->
<g id="life_events_graph_node8" class="node">
<title>tp_finalize</title>
<g id="a_life_events_graph_node8"><a xlink:href="typeobj.html#c.PyTypeObject.tp_finalize" xlink:title="tp_finalize" target="_top">
<ellipse fill="none" stroke="black" cx="122.8" cy="-321" rx="62.65" ry="18"/>
<text text-anchor="middle" x="122.8" y="-316.73" font-family="monospace,monospace" font-size="12.00">tp_finalize</text>
</a>
</g>
</g>
<!-- reachable&#45;&gt;tp_finalize -->
<g id="life_events_graph_edge8" class="edge">
<title>reachable&#45;&gt;tp_finalize</title>
<g id="a_life_events_graph_edge8"><a xlink:title="tp_finalize to reachable: resurrected (maybe remove finalized mark)">
<path fill="none" stroke="black" d="M142.86,-659.6C103.8,-656.96 53.97,-650.63 40.8,-635.5 -37.32,-545.75 69.61,-390.31 109.14,-338.99"/>
<polygon fill="black" stroke="black" points="142.62,-663.09 152.82,-660.2 143.05,-656.11 142.62,-663.09"/>
</a>
</g>
<g id="a_life_events_graph_edge8&#45;label"><a xlink:title="tp_finalize to reachable: resurrected (maybe remove finalized mark)">
<text text-anchor="start" x="33.68" y="-527.35" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;resurrected &#160;</text>
<text text-anchor="start" x="22.43" y="-513.1" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;(maybe remove &#160;</text>
<text text-anchor="start" x="23.18" y="-498.85" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;finalized mark) &#160;</text>
</a>
</g>
</g>
<!-- uncollectable -->
<g id="life_events_graph_node10" class="node">
<title>uncollectable</title>
<g id="a_life_events_graph_node10"><a xlink:title="uncollectable (leaked)">
<polygon fill="none" stroke="black" points="371.92,-159.75 275.67,-159.75 275.67,-123.25 371.92,-123.25 371.92,-159.75"/>
<text text-anchor="start" x="283.67" y="-144.35" font-family="serif,serif" font-style="italic" font-size="12.00">uncollectable</text>
<text text-anchor="start" x="299.42" y="-130.1" font-family="serif,serif" font-style="italic" font-size="12.00">(leaked)</text>
</a>
</g>
</g>
<!-- reachable&#45;&gt;uncollectable -->
<g id="life_events_graph_edge19" class="edge">
<title>reachable&#45;&gt;uncollectable</title>
<g id="a_life_events_graph_edge19"><a xlink:title="reachable to uncollectable: cyclic isolate (no GC support)">
<path fill="none" stroke="black" d="M231.2,-652.03C270.79,-639.69 326.8,-613.9 326.8,-566.75 326.8,-566.75 326.8,-566.75 326.8,-237.5 326.8,-215.3 325.97,-190.2 325.18,-171.37"/>
<polygon fill="black" stroke="black" points="328.68,-171.35 324.75,-161.52 321.69,-171.66 328.68,-171.35"/>
</a>
</g>
<g id="a_life_events_graph_edge19&#45;label"><a xlink:title="reachable to uncollectable: cyclic isolate (no GC support)">
<text text-anchor="start" x="335.05" y="-393.6" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;cyclic &#160;</text>
<text text-anchor="start" x="332.42" y="-379.35" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;isolate &#160;</text>
<text text-anchor="start" x="331.3" y="-365.1" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;(no GC &#160;</text>
<text text-anchor="start" x="326.8" y="-350.85" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;support) &#160;</text>
</a>
</g>
</g>
<!-- tp_dealloc -->
<g id="life_events_graph_node11" class="node">
<title>tp_dealloc</title>
<g id="a_life_events_graph_node11"><a xlink:href="typeobj.html#c.PyTypeObject.tp_dealloc" xlink:title="tp_dealloc" target="_top">
<ellipse fill="none" stroke="black" cx="200.8" cy="-86.25" rx="57.88" ry="18"/>
<text text-anchor="middle" x="200.8" y="-81.97" font-family="monospace,monospace" font-size="12.00">tp_dealloc</text>
</a>
</g>
</g>
<!-- reachable&#45;&gt;tp_dealloc -->
<g id="life_events_graph_edge20" class="edge">
<title>reachable&#45;&gt;tp_dealloc</title>
<path fill="none" stroke="black" d="M231.23,-661.18C293.08,-658.43 407.8,-643.03 407.8,-566.75 407.8,-566.75 407.8,-566.75 407.8,-140.5 407.8,-111.22 329.12,-97.8 268.77,-91.82"/>
<polygon fill="black" stroke="black" points="269.15,-88.34 258.87,-90.89 268.5,-95.31 269.15,-88.34"/>
<g id="a_life_events_graph_edge20&#45;label"><a xlink:title="reachable to tp_dealloc: no refs">
<text text-anchor="start" x="407.8" y="-316.73" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;no refs</text>
</a>
</g>
</g>
<!-- tp_traverse&#45;&gt;finalized? -->
<g id="life_events_graph_edge9" class="edge">
<title>tp_traverse&#45;&gt;finalized?</title>
<g id="a_life_events_graph_edge9"><a xlink:title="tp_traverse to finalized?: cyclic isolate">
<path fill="none" stroke="black" d="M145.15,-547.55C152.4,-532.62 163.18,-510.43 172.55,-491.13"/>
<polygon fill="black" stroke="black" points="175.7,-492.66 176.92,-482.14 169.4,-489.61 175.7,-492.66"/>
</a>
</g>
<g id="a_life_events_graph_edge9&#45;label"><a xlink:title="tp_traverse to finalized?: cyclic isolate">
<text text-anchor="start" x="171.85" y="-520.23" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;cyclic &#160;</text>
<text text-anchor="start" x="169.22" y="-505.98" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;isolate &#160;</text>
</a>
</g>
</g>
<!-- finalized?&#45;&gt;tp_finalize -->
<g id="life_events_graph_edge11" class="edge">
<title>finalized?&#45;&gt;tp_finalize</title>
<g id="a_life_events_graph_edge11"><a xlink:title="finalized? to tp_finalize: no (mark as finalized)">
<path fill="none" stroke="black" d="M172.89,-422.6C169.14,-416.89 165.34,-410.82 162.05,-405 151.89,-387.08 141.99,-366.11 134.68,-349.73"/>
<polygon fill="black" stroke="black" points="137.89,-348.35 130.66,-340.61 131.48,-351.17 137.89,-348.35"/>
</a>
</g>
<g id="a_life_events_graph_edge11&#45;label"><a xlink:title="finalized? to tp_finalize: no (mark as finalized)">
<text text-anchor="start" x="170.67" y="-379.35" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;no (mark &#160;</text>
<text text-anchor="start" x="162.05" y="-365.1" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;as finalized) &#160;</text>
</a>
</g>
</g>
<!-- tp_clear -->
<g id="life_events_graph_node9" class="node">
<title>tp_clear</title>
<g id="a_life_events_graph_node9"><a xlink:href="typeobj.html#c.PyTypeObject.tp_clear" xlink:title="tp_clear" target="_top">
<ellipse fill="none" stroke="black" cx="222.8" cy="-238.5" rx="48.34" ry="18"/>
<text text-anchor="middle" x="222.8" y="-234.22" font-family="monospace,monospace" font-size="12.00">tp_clear</text>
</a>
</g>
</g>
<!-- finalized?&#45;&gt;tp_clear -->
<g id="life_events_graph_edge12" class="edge">
<title>finalized?&#45;&gt;tp_clear</title>
<g id="a_life_events_graph_edge12"><a xlink:title="finalized? to tp_clear: yes">
<path fill="none" stroke="black" d="M227.56,-430.1C236.46,-423.41 244.86,-415.02 249.8,-405 277.22,-349.39 274.06,-322.55 249.8,-265.5 249.7,-265.27 249.6,-265.04 249.49,-264.81"/>
<polygon fill="black" stroke="black" points="252.41,-262.88 243.93,-256.52 246.6,-266.78 252.41,-262.88"/>
</a>
</g>
<g id="a_life_events_graph_edge12&#45;label"><a xlink:title="finalized? to tp_clear: yes">
<text text-anchor="start" x="269.2" y="-316.73" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;yes &#160;</text>
</a>
</g>
</g>
<!-- tp_finalize&#45;&gt;tp_clear -->
<g id="life_events_graph_edge13" class="edge">
<title>tp_finalize&#45;&gt;tp_clear</title>
<g id="a_life_events_graph_edge13"><a xlink:title="tp_finalize to tp_clear: no refs or cyclic isolate">
<path fill="none" stroke="black" d="M130.02,-302.72C135.75,-290.85 144.8,-275.49 156.8,-265.5 161.95,-261.21 167.9,-257.57 174.07,-254.49"/>
<polygon fill="black" stroke="black" points="175.46,-257.71 183.18,-250.45 172.62,-251.31 175.46,-257.71"/>
</a>
</g>
<g id="a_life_events_graph_edge13&#45;label"><a xlink:title="tp_finalize to tp_clear: no refs or cyclic isolate">
<text text-anchor="start" x="164.3" y="-282.6" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;no refs or &#160;&#160;</text>
<text text-anchor="start" x="156.8" y="-268.35" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;cyclic isolate &#160;</text>
</a>
</g>
</g>
<!-- tp_finalize&#45;&gt;tp_dealloc -->
<g id="life_events_graph_edge14" class="edge">
<title>tp_finalize&#45;&gt;tp_dealloc</title>
<g id="a_life_events_graph_edge14"><a xlink:href="lifecycle.html#c.PyObject_CallFinalizerFromDealloc" xlink:title="tp_dealloc to tp_finalize: recommended call (see explanation)" target="_top">
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M85.85,-298.52C42.09,-270.18 -21.4,-218.11 7.8,-168.75 36.22,-120.7 99.95,-100.97 146.42,-92.87"/>
<polygon fill="none" stroke="black" points="83.78,-301.35 94.11,-303.72 87.52,-295.43 83.78,-301.35"/>
</a>
</g>
<g id="a_life_events_graph_edge14&#45;label"><a xlink:href="lifecycle.html#c.PyObject_CallFinalizerFromDealloc" xlink:title="tp_dealloc to tp_finalize: recommended call (see explanation)" target="_top">
<text text-anchor="start" x="7.8" y="-200.1" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;recommended</text>
<text text-anchor="start" x="25.8" y="-185.85" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;call (see</text>
<text text-anchor="start" x="13.05" y="-171.6" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;explanation)</text>
</a>
</g>
</g>
<!-- tp_finalize&#45;&gt;tp_dealloc -->
<g id="life_events_graph_edge15" class="edge">
<title>tp_finalize&#45;&gt;tp_dealloc</title>
<g id="a_life_events_graph_edge15"><a xlink:title="tp_finalize to tp_dealloc: no refs">
<path fill="none" stroke="black" d="M123.03,-302.58C123.95,-273.77 128.08,-214.78 146.05,-168.75 153.95,-148.5 167.56,-128.2 179.24,-112.92"/>
<polygon fill="black" stroke="black" points="181.81,-115.32 185.25,-105.3 176.31,-110.98 181.81,-115.32"/>
</a>
</g>
<g id="a_life_events_graph_edge15&#45;label"><a xlink:title="tp_finalize to tp_dealloc: no refs">
<text text-anchor="start" x="146.05" y="-185.85" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;&#160;no refs &#160;</text>
</a>
</g>
</g>
<!-- tp_clear&#45;&gt;uncollectable -->
<g id="life_events_graph_edge17" class="edge">
<title>tp_clear&#45;&gt;uncollectable</title>
<g id="a_life_events_graph_edge17"><a xlink:title="tp_clear to uncollectable: cyclic isolate">
<path fill="none" stroke="black" d="M227.75,-220.38C232.99,-205 242.67,-182.74 258.05,-168.75 260.43,-166.58 263.02,-164.58 265.74,-162.73"/>
<polygon fill="black" stroke="black" points="267.27,-165.89 274.12,-157.81 263.73,-159.86 267.27,-165.89"/>
</a>
</g>
<g id="a_life_events_graph_edge17&#45;label"><a xlink:title="tp_clear to uncollectable: cyclic isolate">
<text text-anchor="start" x="260.67" y="-192.97" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;cyclic &#160;</text>
<text text-anchor="start" x="258.05" y="-178.72" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;isolate &#160;</text>
</a>
</g>
</g>
<!-- tp_clear&#45;&gt;tp_dealloc -->
<g id="life_events_graph_edge16" class="edge">
<title>tp_clear&#45;&gt;tp_dealloc</title>
<g id="a_life_events_graph_edge16"><a xlink:title="tp_clear to tp_dealloc: no refs">
<path fill="none" stroke="black" d="M219.7,-220.24C216.92,-204.51 212.83,-180.61 209.8,-159.75 207.7,-145.34 205.67,-129.26 204.07,-115.92"/>
<polygon fill="black" stroke="black" points="207.56,-115.59 202.91,-106.07 200.61,-116.41 207.56,-115.59"/>
</a>
</g>
<g id="a_life_events_graph_edge16&#45;label"><a xlink:title="tp_clear to tp_dealloc: no refs">
<text text-anchor="start" x="209.8" y="-137.22" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;no refs &#160;</text>
</a>
</g>
</g>
<!-- uncollectable&#45;&gt;tp_dealloc -->
<!-- tp_free -->
<g id="life_events_graph_node12" class="node">
<title>tp_free</title>
<g id="a_life_events_graph_node12"><a xlink:href="typeobj.html#c.PyTypeObject.tp_free" xlink:title="tp_free" target="_top">
<ellipse fill="none" stroke="black" cx="200.8" cy="-18" rx="43.57" ry="18"/>
<text text-anchor="middle" x="200.8" y="-13.72" font-family="monospace,monospace" font-size="12.00">tp_free</text>
</a>
</g>
</g>
<!-- tp_dealloc&#45;&gt;tp_free -->
<g id="life_events_graph_edge21" class="edge">
<title>tp_dealloc&#45;&gt;tp_free</title>
<g id="a_life_events_graph_edge21"><a xlink:title="tp_dealloc to tp_free: direct call">
<path fill="none" stroke="black" d="M200.8,-67.84C200.8,-61.63 200.8,-54.46 200.8,-47.56"/>
<polygon fill="none" stroke="black" points="204.3,-47.57 200.8,-37.57 197.3,-47.57 204.3,-47.57"/>
</a>
</g>
<g id="a_life_events_graph_edge21&#45;label"><a xlink:title="tp_dealloc to tp_free: direct call">
<text text-anchor="start" x="200.8" y="-47.85" font-family="serif,serif" font-style="italic" font-size="12.00"> &#160;&#160;&#160;direct call &#160;</text>
</a>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

273
Doc/c-api/lifecycle.rst Normal file
View File

@ -0,0 +1,273 @@
.. highlight:: c
.. _life-cycle:
Object Life Cycle
=================
This section explains how a type's slots relate to each other throughout the
life of an object. It is not intended to be a complete canonical reference for
the slots; instead, refer to the slot-specific documentation in
:ref:`type-structs` for details about a particular slot.
Life Events
-----------
The figure below illustrates the order of events that can occur throughout an
object's life. An arrow from *A* to *B* indicates that event *B* can occur
after event *A* has occurred, with the arrow's label indicating the condition
that must be true for *B* to occur after *A*.
.. only:: html and not epub
.. raw:: html
<style type="text/css">
.. raw:: html
:file: lifecycle.dot.css
.. raw:: html
</style>
.. raw:: html
:file: lifecycle.dot.svg
.. raw:: html
<script>
(() => {
const g = document.getElementById('life_events_graph');
const title = g.querySelector(':scope > title');
title.id = 'life-events-graph-title';
const svg = g.closest('svg');
svg.role = 'img';
svg.setAttribute('aria-describedby',
'life-events-graph-description');
svg.setAttribute('aria-labelledby', 'life-events-graph-title');
})();
</script>
.. only:: epub or not (html or latex)
.. image:: lifecycle.dot.svg
:align: center
:class: invert-in-dark-mode
:alt: Diagram showing events in an object's life. Explained in detail
below.
.. only:: latex
.. image:: lifecycle.dot.pdf
:align: center
:class: invert-in-dark-mode
:alt: Diagram showing events in an object's life. Explained in detail
below.
.. container::
:name: life-events-graph-description
Explanation:
* When a new object is constructed by calling its type:
#. :c:member:`~PyTypeObject.tp_new` is called to create a new object.
#. :c:member:`~PyTypeObject.tp_alloc` is directly called by
:c:member:`~PyTypeObject.tp_new` to allocate the memory for the new
object.
#. :c:member:`~PyTypeObject.tp_init` initializes the newly created object.
:c:member:`!tp_init` can be called again to re-initialize an object, if
desired. The :c:member:`!tp_init` call can also be skipped entirely,
for example by Python code calling :py:meth:`~object.__new__`.
* After :c:member:`!tp_init` completes, the object is ready to use.
* Some time after the last reference to an object is removed:
#. If an object is not marked as *finalized*, it might be finalized by
marking it as *finalized* and calling its
:c:member:`~PyTypeObject.tp_finalize` function. Python does
*not* finalize an object when the last reference to it is deleted; use
:c:func:`PyObject_CallFinalizerFromDealloc` to ensure that
:c:member:`~PyTypeObject.tp_finalize` is always called.
#. If the object is marked as finalized,
:c:member:`~PyTypeObject.tp_clear` might be called by the garbage collector
to clear references held by the object. It is *not* called when the
object's reference count reaches zero.
#. :c:member:`~PyTypeObject.tp_dealloc` is called to destroy the object.
To avoid code duplication, :c:member:`~PyTypeObject.tp_dealloc` typically
calls into :c:member:`~PyTypeObject.tp_clear` to free up the object's
references.
#. When :c:member:`~PyTypeObject.tp_dealloc` finishes object destruction,
it directly calls :c:member:`~PyTypeObject.tp_free` (usually set to
:c:func:`PyObject_Free` or :c:func:`PyObject_GC_Del` automatically as
appropriate for the type) to deallocate the memory.
* The :c:member:`~PyTypeObject.tp_finalize` function is permitted to add a
reference to the object if desired. If it does, the object is
*resurrected*, preventing its pending destruction. (Only
:c:member:`!tp_finalize` is allowed to resurrect an object;
:c:member:`~PyTypeObject.tp_clear` and
:c:member:`~PyTypeObject.tp_dealloc` cannot without calling into
:c:member:`!tp_finalize`.) Resurrecting an object may
or may not cause the object's *finalized* mark to be removed. Currently,
Python does not remove the *finalized* mark from a resurrected object if
it supports garbage collection (i.e., the :c:macro:`Py_TPFLAGS_HAVE_GC`
flag is set) but does remove the mark if the object does not support
garbage collection; either or both of these behaviors may change in the
future.
* :c:member:`~PyTypeObject.tp_dealloc` can optionally call
:c:member:`~PyTypeObject.tp_finalize` via
:c:func:`PyObject_CallFinalizerFromDealloc` if it wishes to reuse that
code to help with object destruction. This is recommended because it
guarantees that :c:member:`!tp_finalize` is always called before
destruction. See the :c:member:`~PyTypeObject.tp_dealloc` documentation
for example code.
* If the object is a member of a :term:`cyclic isolate` and either
:c:member:`~PyTypeObject.tp_clear` fails to break the reference cycle or
the cyclic isolate is not detected (perhaps :func:`gc.disable` was called,
or the :c:macro:`Py_TPFLAGS_HAVE_GC` flag was erroneously omitted in one
of the involved types), the objects remain indefinitely uncollectable
(they "leak"). See :data:`gc.garbage`.
If the object is marked as supporting garbage collection (the
:c:macro:`Py_TPFLAGS_HAVE_GC` flag is set in
:c:member:`~PyTypeObject.tp_flags`), the following events are also possible:
* The garbage collector occasionally calls
:c:member:`~PyTypeObject.tp_traverse` to identify :term:`cyclic isolates
<cyclic isolate>`.
* When the garbage collector discovers a :term:`cyclic isolate`, it
finalizes one of the objects in the group by marking it as *finalized* and
calling its :c:member:`~PyTypeObject.tp_finalize` function, if it has one.
This repeats until the cyclic isolate doesn't exist or all of the objects
have been finalized.
* :c:member:`~PyTypeObject.tp_finalize` is permitted to resurrect the object
by adding a reference from outside the :term:`cyclic isolate`. The new
reference causes the group of objects to no longer form a cyclic isolate
(the reference cycle may still exist, but if it does the objects are no
longer isolated).
* When the garbage collector discovers a :term:`cyclic isolate` and all of
the objects in the group have already been marked as *finalized*, the
garbage collector clears one or more of the uncleared objects in the group
(possibly concurrently) by calling each's
:c:member:`~PyTypeObject.tp_clear` function. This repeats as long as the
cyclic isolate still exists and not all of the objects have been cleared.
Cyclic Isolate Destruction
--------------------------
Listed below are the stages of life of a hypothetical :term:`cyclic isolate`
that continues to exist after each member object is finalized or cleared. It
is a memory leak if a cyclic isolate progresses through all of these stages; it should
vanish once all objects are cleared, if not sooner. A cyclic isolate can
vanish either because the reference cycle is broken or because the objects are
no longer isolated due to finalizer resurrection (see
:c:member:`~PyTypeObject.tp_finalize`).
0. **Reachable** (not yet a cyclic isolate): All objects are in their normal,
reachable state. A reference cycle could exist, but an external reference
means the objects are not yet isolated.
#. **Unreachable but consistent:** The final reference from outside the cyclic
group of objects has been removed, causing the objects to become isolated
(thus a cyclic isolate is born). None of the group's objects have been
finalized or cleared yet. The cyclic isolate remains at this stage until
some future run of the garbage collector (not necessarily the next run
because the next run might not scan every object).
#. **Mix of finalized and not finalized:** Objects in a cyclic isolate are
finalized one at a time, which means that there is a period of time when the
cyclic isolate is composed of a mix of finalized and non-finalized objects.
Finalization order is unspecified, so it can appear random. A finalized
object must behave in a sane manner when non-finalized objects interact with
it, and a non-finalized object must be able to tolerate the finalization of
an arbitrary subset of its referents.
#. **All finalized:** All objects in a cyclic isolate are finalized before any
of them are cleared.
#. **Mix of finalized and cleared:** The objects can be cleared serially or
concurrently (but with the :term:`GIL` held); either way, some will finish
before others. A finalized object must be able to tolerate the clearing of
a subset of its referents. :pep:`442` calls this stage "cyclic trash".
#. **Leaked:** If a cyclic isolate still exists after all objects in the group
have been finalized and cleared, then the objects remain indefinitely
uncollectable (see :data:`gc.garbage`). It is a bug if a cyclic isolate
reaches this stage---it means the :c:member:`~PyTypeObject.tp_clear` methods
of the participating objects have failed to break the reference cycle as
required.
If :c:member:`~PyTypeObject.tp_clear` did not exist, then Python would have no
way to safely break a reference cycle. Simply destroying an object in a cyclic
isolate would result in a dangling pointer, triggering undefined behavior when
an object referencing the destroyed object is itself destroyed. The clearing
step makes object destruction a two-phase process: first
:c:member:`~PyTypeObject.tp_clear` is called to partially destroy the objects
enough to detangle them from each other, then
:c:member:`~PyTypeObject.tp_dealloc` is called to complete the destruction.
Unlike clearing, finalization is not a phase of destruction. A finalized
object must still behave properly by continuing to fulfill its design
contracts. An object's finalizer is allowed to execute arbitrary Python code,
and is even allowed to prevent the impending destruction by adding a reference.
The finalizer is only related to destruction by call order---if it runs, it runs
before destruction, which starts with :c:member:`~PyTypeObject.tp_clear` (if
called) and concludes with :c:member:`~PyTypeObject.tp_dealloc`.
The finalization step is not necessary to safely reclaim the objects in a
cyclic isolate, but its existence makes it easier to design types that behave
in a sane manner when objects are cleared. Clearing an object might
necessarily leave it in a broken, partially destroyed state---it might be
unsafe to call any of the cleared object's methods or access any of its
attributes. With finalization, only finalized objects can possibly interact
with cleared objects; non-finalized objects are guaranteed to interact with
only non-cleared (but potentially finalized) objects.
To summarize the possible interactions:
* A non-finalized object might have references to or from non-finalized and
finalized objects, but not to or from cleared objects.
* A finalized object might have references to or from non-finalized, finalized,
and cleared objects.
* A cleared object might have references to or from finalized and cleared
objects, but not to or from non-finalized objects.
Without any reference cycles, an object can be simply destroyed once its last
reference is deleted; the finalization and clearing steps are not necessary to
safely reclaim unused objects. However, it can be useful to automatically call
:c:member:`~PyTypeObject.tp_finalize` and :c:member:`~PyTypeObject.tp_clear`
before destruction anyway because type design is simplified when all objects
always experience the same series of events regardless of whether they
participated in a cyclic isolate. Python currently only calls
:c:member:`~PyTypeObject.tp_finalize` and :c:member:`~PyTypeObject.tp_clear` as
needed to destroy a cyclic isolate; this may change in a future version.
Functions
---------
To allocate and free memory, see :ref:`allocating-objects`.
.. c:function:: void PyObject_CallFinalizer(PyObject *op)
Finalizes the object as described in :c:member:`~PyTypeObject.tp_finalize`.
Call this function (or :c:func:`PyObject_CallFinalizerFromDealloc`) instead
of calling :c:member:`~PyTypeObject.tp_finalize` directly because this
function may deduplicate multiple calls to :c:member:`!tp_finalize`.
Currently, calls are only deduplicated if the type supports garbage
collection (i.e., the :c:macro:`Py_TPFLAGS_HAVE_GC` flag is set); this may
change in the future.
.. c:function:: int PyObject_CallFinalizerFromDealloc(PyObject *op)
Same as :c:func:`PyObject_CallFinalizer` but meant to be called at the
beginning of the object's destructor (:c:member:`~PyTypeObject.tp_dealloc`).
There must not be any references to the object. If the object's finalizer
resurrects the object, this function returns -1; no further destruction
should happen. Otherwise, this function returns 0 and destruction can
continue normally.
.. seealso::
:c:member:`~PyTypeObject.tp_dealloc` for example code.

View File

@ -376,6 +376,24 @@ The :ref:`default object allocator <default-memory-allocators>` uses the
If *p* is ``NULL``, no operation is performed.
Do not call this directly to free an object's memory; call the type's
:c:member:`~PyTypeObject.tp_free` slot instead.
Do not use this for memory allocated by :c:macro:`PyObject_GC_New` or
:c:macro:`PyObject_GC_NewVar`; use :c:func:`PyObject_GC_Del` instead.
.. seealso::
* :c:func:`PyObject_GC_Del` is the equivalent of this function for memory
allocated by types that support garbage collection.
* :c:func:`PyObject_Malloc`
* :c:func:`PyObject_Realloc`
* :c:func:`PyObject_Calloc`
* :c:macro:`PyObject_New`
* :c:macro:`PyObject_NewVar`
* :c:func:`PyType_GenericAlloc`
* :c:member:`~PyTypeObject.tp_free`
.. _default-memory-allocators:

View File

@ -12,6 +12,7 @@ object types.
.. toctree::
allocation.rst
lifecycle.rst
structures.rst
typeobj.rst
gcsupport.rst

View File

@ -151,14 +151,29 @@ Type Objects
.. c:function:: PyObject* PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
Generic handler for the :c:member:`~PyTypeObject.tp_alloc` slot of a type object. Use
Python's default memory allocation mechanism to allocate a new instance and
initialize all its contents to ``NULL``.
Generic handler for the :c:member:`~PyTypeObject.tp_alloc` slot of a type
object. Uses Python's default memory allocation mechanism to allocate memory
for a new instance, zeros the memory, then initializes the memory as if by
calling :c:func:`PyObject_Init` or :c:func:`PyObject_InitVar`.
Do not call this directly to allocate memory for an object; call the type's
:c:member:`~PyTypeObject.tp_alloc` slot instead.
For types that support garbage collection (i.e., the
:c:macro:`Py_TPFLAGS_HAVE_GC` flag is set), this function behaves like
:c:macro:`PyObject_GC_New` or :c:macro:`PyObject_GC_NewVar` (except the
memory is guaranteed to be zeroed before initialization), and should be
paired with :c:func:`PyObject_GC_Del` in :c:member:`~PyTypeObject.tp_free`.
Otherwise, it behaves like :c:macro:`PyObject_New` or
:c:macro:`PyObject_NewVar` (except the memory is guaranteed to be zeroed
before initialization) and should be paired with :c:func:`PyObject_Free` in
:c:member:`~PyTypeObject.tp_free`.
.. c:function:: PyObject* PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds)
Generic handler for the :c:member:`~PyTypeObject.tp_new` slot of a type object. Create a
new instance using the type's :c:member:`~PyTypeObject.tp_alloc` slot.
Generic handler for the :c:member:`~PyTypeObject.tp_new` slot of a type
object. Creates a new instance using the type's
:c:member:`~PyTypeObject.tp_alloc` slot and returns the resulting object.
.. c:function:: int PyType_Ready(PyTypeObject *type)

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

View File

@ -355,6 +355,12 @@ Glossary
tasks (see :mod:`asyncio`) associate each task with a context which
becomes the current context whenever the task starts or resumes execution.
cyclic isolate
A subgroup of one or more objects that reference each other in a reference
cycle, but are not referenced by objects outside the group. The goal of
the :term:`cyclic garbage collector <garbage collection>` is to identify these groups and break the reference
cycles so that the memory can be reclaimed.
decorator
A function returning another function, usually applied as a function
transformation using the ``@wrapper`` syntax. Common examples for