Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
bc32398
gh-75459: Doc: C API: Improve object life cycle documentation
rhansen Oct 19, 2024
361eaca
gh-75459: Doc: Tell sphinx to run graphviz
rhansen Oct 25, 2024
b27bcca
add blurb
rhansen Oct 25, 2024
b42b58d
Revert "gh-75459: Doc: Tell sphinx to run graphviz"
rhansen Oct 25, 2024
7e571ae
delete "ref count == 0" node, link directly to `tp_dealloc`
rhansen Oct 26, 2024
97dc30c
Merge branch 'main' into docs
rhansen Nov 7, 2024
85e535a
significant rewrite to address review comments
rhansen Nov 7, 2024
ef979e6
fix warnings
rhansen Nov 7, 2024
f3863c4
tweak cyclic isolate definition
rhansen Nov 7, 2024
6a114f9
apply css to the svg to support dark theme
rhansen Nov 8, 2024
182c977
increase font size
rhansen Nov 8, 2024
7cd0cb5
attempt to make the svg accessible
rhansen Nov 8, 2024
e34e224
wrap at 79 chars
rhansen Nov 8, 2024
16a29ab
address review feedback, and other tweaks
rhansen Nov 9, 2024
018a3c4
be more precise about the finalized mark
rhansen Nov 21, 2024
442b7f2
add pdf support, improve epub support
rhansen Nov 21, 2024
5ed484a
address feedback, and other improvements
rhansen Nov 27, 2024
b7774ae
Merge in the main branch
encukou Mar 11, 2025
bb1a94f
Remove redundant notes added in GH-129850
encukou Mar 11, 2025
fd38fd4
Merge branch 'main' into docs
ZeroIntensity May 5, 2025
3573c79
Apply suggestions from code review
ZeroIntensity May 5, 2025
2633594
Update Doc/c-api/lifecycle.rst
ZeroIntensity May 5, 2025
c57574a
Mention tp_alloc()
ZeroIntensity May 5, 2025
878fd27
Avoid using 'do not'
ZeroIntensity May 5, 2025
1b96fad
Mention PyObject_CallFinalizerFromDealloc()
ZeroIntensity May 5, 2025
e8c6852
Add some clarifications about tp_finalize()
ZeroIntensity May 5, 2025
3408919
Say that tp_dealloc() must free.
ZeroIntensity May 5, 2025
b6023a3
Fix doctest.
ZeroIntensity May 5, 2025
da3593c
Apply suggestions from code review
ZeroIntensity May 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 80 additions & 18 deletions Doc/c-api/allocation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,100 @@ 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. Specifically, this function does **not** call the object's
:meth:`~object.__init__` method (:c:member:`~PyTypeObject.tp_init` slot).

.. warning::

This function does not guarantee that the memory will be completely
zeroed before it is initialized.


.. 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 does not guarantee that the memory will be completely
zeroed before it is initialized.


.. 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). The size of the memory
allocation is determined from the :c:member:`~PyTypeObject.tp_basicsize`
field of the type object.

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 macro should not 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);


.. 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:

* 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.

This should not 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);


.. c:function:: void PyObject_Del(void *op)
Expand Down
12 changes: 12 additions & 0 deletions Doc/c-api/gcsupport.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,19 @@ rules:
Analogous to :c:macro:`PyObject_New` but for container objects with the
:c:macro:`Py_TPFLAGS_HAVE_GC` flag set.

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).

.. 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.

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).

.. c:function:: PyObject* PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *type, size_t extra_size)

Analogous to :c:macro:`PyObject_GC_New` but allocates *extra_size*
Expand All @@ -73,6 +81,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.
Expand Down
86 changes: 86 additions & 0 deletions Doc/c-api/lifecycle.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
digraph "Life Events" {
graph [
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"
target="_top"
]
"finalized" [
fontname="Times-Italic"
label=<marked as<br/>finalized?>
shape=diamond
]
"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
]
"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]
"tp_new" -> "tp_init"
"tp_init" -> "reachable"
"reachable" -> "tp_traverse" [
label=< periodic <br/> cyclic isolate <br/> detection >
]
"reachable" -> "tp_init"
"reachable" -> "tp_finalize" [
dir="back"
label=< resurrected <br/> (maybe remove <br/> finalized mark) >
]
"tp_traverse" -> "reachable" [label=< not in a <br/> cyclic isolate >]
"tp_traverse" -> "finalized" [label=< cyclic <br/> isolate >]
"reachable" -> "finalized" [label=< no refs >]
"finalized" -> "tp_finalize" [label=< no (mark <br/> as finalized) >]
"finalized" -> "tp_clear" [label=< yes >]
"tp_finalize" -> "tp_clear" [label=< no refs or <br/> 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)>
target="_top"
]
"tp_finalize" -> "tp_dealloc" [label=< no refs >]
"tp_clear" -> "tp_dealloc" [label=< no refs >]
"tp_clear" -> "uncollectable" [label=< cyclic <br/> isolate >]
"uncollectable" -> "tp_dealloc" [style=invis]
"reachable" -> "tp_dealloc" [label=< no refs>]
"tp_dealloc" -> "tp_free" [label=< direct call > arrowhead=empty]
}
41 changes: 41 additions & 0 deletions Doc/c-api/lifecycle.dot.css
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);
}
Binary file added Doc/c-api/lifecycle.dot.pdf
Binary file not shown.
Loading
Loading