-
-
Notifications
You must be signed in to change notification settings - Fork 31.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gh-75459: Doc: C API: Improve object life cycle documentation #125962
base: main
Are you sure you want to change the base?
Changes from all commits
bc32398
361eaca
b27bcca
b42b58d
7e571ae
97dc30c
85e535a
ef979e6
f3863c4
6a114f9
182c977
7cd0cb5
e34e224
16a29ab
018a3c4
442b7f2
5ed484a
b7774ae
bb1a94f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -16,46 +16,123 @@ 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. | ||||||
.. warning:: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer downgrading these to
Suggested change
(And similar for all the warnings in this PR.) |
||||||
This function only initializes some of the object's memory. It does not | ||||||
zero the rest. | ||||||
.. c:function:: PyVarObject* PyObject_InitVar(PyVarObject *op, PyTypeObject *type, Py_ssize_t size) | ||||||
This does everything :c:func:`PyObject_Init` does, and also initializes the | ||||||
length information for a variable-size object. | ||||||
.. warning:: | ||||||
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). | ||||||
Do not call this directly to allocate memory for an object; call the type's | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, "do not" is a strong phrase. Nothing bad will happen if you do it, it's just not recommended. |
||||||
: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 function must be freed with :c:func:`PyObject_Free` | ||||||
(usually called via the object's :c:member:`~PyTypeObject.tp_free` slot). | ||||||
.. warning:: | ||||||
The returned memory is not guaranteed to have been completely zeroed | ||||||
before it was initialized. | ||||||
.. warning:: | ||||||
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); | ||||||
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. | ||||||
.. 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. | ||||||
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. | ||||||
Like :c:macro:`PyObject_New` except: | ||||||
|
||||||
* 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. | ||||||
|
||||||
Do not call this directly to allocate memory for an object; call the type's | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. (This occurs several times, so just deal with all of them.) |
||||||
: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). | ||||||
|
||||||
.. warning:: | ||||||
|
||||||
The returned memory is not guaranteed to have been completely zeroed | ||||||
before it was initialized. | ||||||
|
||||||
.. warning:: | ||||||
|
||||||
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); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's worth using an example of a real type here, primarily because not all types are available via
Suggested change
|
||||||
|
||||||
.. 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) | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 function 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 function 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I'm not a fan of "do not" here again. This "do not" is not nearly the severity of the case below, in which you would get a segfault. |
||
: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) | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,158 @@ | ||||||
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 > | ||||||
labeltooltip="start to tp_new: type call" | ||||||
tooltip="start to tp_new: type call" | ||||||
Comment on lines
+61
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove
Suggested change
|
||||||
] | ||||||
"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" | ||||||
] | ||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,41 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#life_events_graph { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Unfortunately these colors don't seem to be exposed in any of the theme's | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* variables, so they are manually copied here. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-light-fgcolor: black; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-light-bgcolor: white; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-dark-fgcolor: rgba(255, 255, 255, 0.87); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-dark-bgcolor: #222; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-fgcolor: var(--svg-light-fgcolor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-bgcolor: var(--svg-light-bgcolor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@media (prefers-color-scheme: dark) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#life_events_graph { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-fgcolor: var(--svg-dark-fgcolor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-bgcolor: var(--svg-dark-bgcolor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@media (prefers-color-scheme: light) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#life_events_graph { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-fgcolor: var(--svg-light-fgcolor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-bgcolor: var(--svg-light-bgcolor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
:root:has(#pydoctheme_dark_css[media="not all"]) #life_events_graph { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-fgcolor: var(--svg-light-fgcolor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-bgcolor: var(--svg-light-bgcolor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
:root:has(#pydoctheme_dark_css[media="all"]) #life_events_graph { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-fgcolor: var(--svg-dark-fgcolor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--svg-bgcolor: var(--svg-dark-bgcolor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#life_events_graph [stroke="black"] { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
stroke: var(--svg-fgcolor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#life_events_graph :is(text, [fill="black"]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fill: var(--svg-fgcolor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#life_events_graph :is([fill="white"]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fill: var(--svg-bgcolor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be simplified using
Suggested change
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,268 @@ | ||||||||
.. 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. | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This also needs an arrow in the diagram. |
||||||||
* After :c:member:`!tp_init` completes, the object is ready to use. | ||||||||
* After the last reference to an object is removed: | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
#. 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 currently does | ||||||||
not finalize an object when the last reference to it is deleted, but | ||||||||
this may change in the future. | ||||||||
Comment on lines
+89
to
+91
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should add something like "(unless |
||||||||
#. If the object is marked as finalized, | ||||||||
:c:member:`~PyTypeObject.tp_clear` might be called to clear references | ||||||||
held by the object. Python currently does not clear an object in | ||||||||
response to the deletion of the last reference, but this may change in | ||||||||
the future. | ||||||||
#. :c:member:`~PyTypeObject.tp_dealloc` is called to destroy the object. | ||||||||
rhansen marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we want to say that it's good practice to just have |
||||||||
#. 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 | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is "resurrected" italicized? |
||||||||
:c:member:`!tp_finalize` is allowed to resurrect an object; | ||||||||
:c:member:`~PyTypeObject.tp_clear` and | ||||||||
:c:member:`~PyTypeObject.tp_dealloc` cannot.) Resurrecting an object may | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well,
Comment on lines
+105
to
+108
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would make the text contradictory: if AFAIK, any of these may resurrect the object. |
||||||||
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. | ||||||||
Comment on lines
+115
to
+121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAIK: For heap types (ones defined in Python code, and ones defined via For these types, we should, strongly recommend to not implement your own
However, it's not that important to call in on types you control, where you know users can't provide Note that all static (i.e. non-heap) types are immutable. So, the recommendation to call If you're looking to write |
||||||||
* 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 | ||||||||
rhansen marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
: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 | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like I've already asked this, but why is this italicized? |
||||||||
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, but with the :term:`GIL` held) 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 bug if a cyclic isolate progresses through all of these stages; it should | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "memory leak" instead of "bug" is more informative:
Suggested change
|
||||||||
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 contemporaneously related to destruction---it runs just | ||||||||
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. | ||||||||
rhansen marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
.. seealso:: | ||||||||
:c:member:`~PyTypeObject.tp_dealloc` for example code. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ object types. | |
.. toctree:: | ||
|
||
allocation.rst | ||
lifecycle.rst | ||
structures.rst | ||
typeobj.rst | ||
gcsupport.rst |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -333,6 +333,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 garbage collector is to identify these groups and break the reference | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
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 | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably worth mentioning that you should just use
tp_alloc
and not touch this (say it's a "low-level routine").