Skip to content
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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
54 changes: 36 additions & 18 deletions Doc/c-api/allocation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ Allocating Objects on the Heap
reference. Returns the initialized object. If *type* indicates that the
object participates in the cyclic garbage detector, it is added to the
detector's set of observed objects. Other fields of the object are not
affected.
initialized. Specifically, this function does **not** call the object's
:meth:`~object.__init__` method (:c:member:`~PyTypeObject.tp_init` slot).
.. c:function:: PyVarObject* PyObject_InitVar(PyVarObject *op, PyTypeObject *type, Py_ssize_t size)
Expand All @@ -29,27 +30,44 @@ Allocating Objects on the Heap
.. 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.
Calls :c:func:`PyObject_Malloc` to allocate memory for a new Python object
using the C structure type *TYPE* and the Python type object *typeobj*
(``PyTypeObject*``), then initializes the memory 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.
Comment on lines +33 to +39
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the original sentence did a better job at explaining what the macro does, whereas the new one focuses more on the "how".

Perhaps this is better?

Suggested change
Calls :c:func:`PyObject_Malloc` to allocate memory for a new Python object
using the C structure type *TYPE* and the Python type object *typeobj*
(``PyTypeObject*``), then initializes the memory 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.
Allocate 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.

This 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 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`.
.. 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`.
.. c:function:: void PyObject_Del(void *op)
Expand Down
9 changes: 9 additions & 0 deletions Doc/c-api/gcsupport.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,17 @@ 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`.

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

.. 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 +79,9 @@ 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`.
.. warning::
The function is marked as unstable because the final mechanism
for reserving extra data after an instance is not yet decided.
Expand Down
93 changes: 93 additions & 0 deletions Doc/c-api/lifecycle.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
digraph G {
graph [
fontname="svg"
fontsize=10.0
layout="dot"
ranksep=0.25
]
node [
fontname="Courier"
fontsize=10.0
]
edge [
fontname="Times-Italic"
fontsize=10.0
]

"start" [fontname="Times-Italic" shape=plain label=< start > style=invis]
"tp_alloc" [href="typeobj.html#c.PyTypeObject.tp_alloc" target="_top"]
"tp_new" [href="typeobj.html#c.PyTypeObject.tp_new" target="_top"]
"tp_init" [href="typeobj.html#c.PyTypeObject.tp_init" target="_top"]
{
rank="same"
"alive" [
fontname="Times-Italic"
label=<alive, ref count &gt; 0>
shape=box
]
"tp_traverse" [
href="typeobj.html#c.PyTypeObject.tp_traverse"
shape=octagon
target="_top"
]
}
"tp_finalize" [
href="typeobj.html#c.PyTypeObject.tp_finalize"
shape=octagon
target="_top"
]
"tp_clear" [
href="typeobj.html#c.PyTypeObject.tp_clear"
shape=octagon
target="_top"
]
"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_alloc"
"tp_alloc" -> "tp_new"
"tp_new" -> "tp_init"
"tp_init" -> "alive"
"tp_traverse" -> "alive"
"alive" -> "tp_traverse"
"alive" -> "tp_clear" [label=< cyclic <br/>isolate >]
"alive" -> "tp_finalize" [
dir="back"
label=< resurrected >
]
"alive" -> "tp_finalize" [label=< cyclic <br/>isolate >]
"tp_finalize" -> "tp_clear"
"tp_finalize" -> "tp_dealloc" [
dir="back"
href="lifecycle.html#c.PyObject_CallFinalizerFromDealloc"
label=<
<table border="0" cellborder="0" cellpadding="0" cellspacing="0">
<tr>
<td rowspan="4"> </td>
<td align="left">optional call to</td>
<td rowspan="4"> </td>
</tr>
<tr>
<td align="left"><font face="Courier">PyObject_Call</font></td>
</tr>
<tr>
<td align="left"><font face="Courier">FinalizerFrom</font></td>
</tr>
<tr><td align="left"><font face="Courier">Dealloc</font></td></tr>
</table>
>
target="_top"
]
"tp_finalize" -> "tp_dealloc" [label=< ref count <br/> == 0 >]
"tp_clear" -> "tp_dealloc" [label=< ref count <br/> == 0 >]
"tp_clear" -> "tp_dealloc" [
dir="back"
label=< optional<br/>direct call >
]
"alive" -> "tp_dealloc" [label=< ref count <br/> == 0 >]
"tp_dealloc" -> "tp_free" [label=< directly calls >]
}
Loading
Loading