Skip to content

Commit bc32398

Browse files
committed
gh-75459: Doc: C API: Improve object life cycle documentation
* Add "cyclic isolate" to the glossary. * Add a new "Object Life Cycle" page. * Illustrate the order of life cycle functions. * Document `PyObject_CallFinalizer` and `PyObject_CallFinalizerFromDealloc`. * `PyObject_Init` does not call `tp_init`. * `PyObject_New`: * also initializes the memory * does not call `tp_alloc`, `tp_new`, or `tp_init` * should not be used for GC-enabled objects * memory must be freed by `PyObject_Free` * `PyObject_GC_New` memory must be freed by `PyObject_GC_Del`. * Warn that garbage collector functions can be called from any thread. * `tp_finalize` and `tp_clear`: * Only called when there's a cyclic isolate. * Only one object in the cyclic isolate is finalized/cleared at a time. * Clearly warn that they might not be called. * They can optionally be manually called from `tp_dealloc` (via `PyObject_CallFinalizerFromDealloc` in the case of `tp_finalize`). * `tp_finalize`: * Reference `object.__del__`. * The finalizer can resurrect the object. * Suggest `PyErr_GetRaisedException` and `PyErr_SetRaisedException` instead of the deprecated `PyErr_Fetch` and `PyErr_Restore` functions. * Add links to `PyErr_GetRaisedException` and `PyErr_SetRaisedException`. * Suggest using `PyErr_WriteUnraisable` if an exception is raised during finalization. * Rename the example function from `local_finalize` to `foo_finalize` for consistency with the `tp_dealloc` documentation and as a hint that the name isn't special. * Minor wording and sylistic tweaks. * Warn that `tp_finalize` can be called during shutdown.
1 parent ebcc578 commit bc32398

File tree

8 files changed

+574
-44
lines changed

8 files changed

+574
-44
lines changed

Doc/c-api/allocation.rst

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ Allocating Objects on the Heap
1818
reference. Returns the initialized object. If *type* indicates that the
1919
object participates in the cyclic garbage detector, it is added to the
2020
detector's set of observed objects. Other fields of the object are not
21-
affected.
21+
initialized. Specifically, this function does **not** call the object's
22+
:meth:`~object.__init__` method (:c:member:`~PyTypeObject.tp_init` slot).
2223
2324
2425
.. c:function:: PyVarObject* PyObject_InitVar(PyVarObject *op, PyTypeObject *type, Py_ssize_t size)
@@ -29,27 +30,44 @@ Allocating Objects on the Heap
2930
3031
.. c:macro:: PyObject_New(TYPE, typeobj)
3132
32-
Allocate a new Python object using the C structure type *TYPE*
33-
and the Python type object *typeobj* (``PyTypeObject*``).
34-
Fields not defined by the Python object header are not initialized.
35-
The caller will own the only reference to the object
36-
(i.e. its reference count will be one).
37-
The size of the memory allocation is determined from the
38-
:c:member:`~PyTypeObject.tp_basicsize` field of the type object.
33+
Calls :c:func:`PyObject_Malloc` to allocate memory for a new Python object
34+
using the C structure type *TYPE* and the Python type object *typeobj*
35+
(``PyTypeObject*``), then initializes the memory like
36+
:c:func:`PyObject_Init`. The caller will own the only reference to the
37+
object (i.e. its reference count will be one). The size of the memory
38+
allocation is determined from the :c:member:`~PyTypeObject.tp_basicsize`
39+
field of the type object.
40+
41+
This does not call :c:member:`~PyTypeObject.tp_alloc`,
42+
:c:member:`~PyTypeObject.tp_new` (:meth:`~object.__new__`), or
43+
:c:member:`~PyTypeObject.tp_init` (:meth:`~object.__init__`).
44+
45+
This should not be used for objects with :c:macro:`Py_TPFLAGS_HAVE_GC` set
46+
in :c:member:`~PyTypeObject.tp_flags`; use :c:macro:`PyObject_GC_New`
47+
instead.
48+
49+
Memory allocated by this function must be freed with :c:func:`PyObject_Free`.
3950
4051
4152
.. c:macro:: PyObject_NewVar(TYPE, typeobj, size)
4253
43-
Allocate a new Python object using the C structure type *TYPE* and the
44-
Python type object *typeobj* (``PyTypeObject*``).
45-
Fields not defined by the Python object header
46-
are not initialized. The allocated memory allows for the *TYPE* structure
47-
plus *size* (``Py_ssize_t``) fields of the size
48-
given by the :c:member:`~PyTypeObject.tp_itemsize` field of
49-
*typeobj*. This is useful for implementing objects like tuples, which are
50-
able to determine their size at construction time. Embedding the array of
51-
fields into the same allocation decreases the number of allocations,
52-
improving the memory management efficiency.
54+
Like :c:macro:`PyObject_New` except:
55+
56+
* It allocates enough memory for the *TYPE* structure plus *size*
57+
(``Py_ssize_t``) fields of the size given by the
58+
:c:member:`~PyTypeObject.tp_itemsize` field of *typeobj*.
59+
* The memory is initialized like :c:func:`PyObject_InitVar`.
60+
61+
This is useful for implementing objects like tuples, which are able to
62+
determine their size at construction time. Embedding the array of fields
63+
into the same allocation decreases the number of allocations, improving the
64+
memory management efficiency.
65+
66+
This should not be used for objects with :c:macro:`Py_TPFLAGS_HAVE_GC` set
67+
in :c:member:`~PyTypeObject.tp_flags`; use :c:macro:`PyObject_GC_NewVar`
68+
instead.
69+
70+
Memory allocated by this function must be freed with :c:func:`PyObject_Free`.
5371
5472
5573
.. c:function:: void PyObject_Del(void *op)

Doc/c-api/gcsupport.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,17 @@ rules:
5757
Analogous to :c:macro:`PyObject_New` but for container objects with the
5858
:c:macro:`Py_TPFLAGS_HAVE_GC` flag set.
5959

60+
Memory allocated by this function must be freed with
61+
:c:func:`PyObject_GC_Del`.
62+
6063
.. c:macro:: PyObject_GC_NewVar(TYPE, typeobj, size)
6164
6265
Analogous to :c:macro:`PyObject_NewVar` but for container objects with the
6366
:c:macro:`Py_TPFLAGS_HAVE_GC` flag set.
6467

68+
Memory allocated by this function must be freed with
69+
:c:func:`PyObject_GC_Del`.
70+
6571
.. c:function:: PyObject* PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *type, size_t extra_size)
6672
6773
Analogous to :c:macro:`PyObject_GC_New` but allocates *extra_size*
@@ -73,6 +79,9 @@ rules:
7379
The extra data will be deallocated with the object, but otherwise it is
7480
not managed by Python.
7581
82+
Memory allocated by this function must be freed with
83+
:c:func:`PyObject_GC_Del`.
84+
7685
.. warning::
7786
The function is marked as unstable because the final mechanism
7887
for reserving extra data after an instance is not yet decided.

Doc/c-api/lifecycle.dot

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
digraph G {
2+
graph [
3+
fontname="svg"
4+
fontsize=10.0
5+
layout="dot"
6+
ranksep=0.25
7+
]
8+
node [
9+
fontname="Courier"
10+
fontsize=10.0
11+
]
12+
edge [
13+
fontname="Times-Italic"
14+
fontsize=10.0
15+
]
16+
17+
"start" [fontname="Times-Italic" shape=plain label=< start > style=invis]
18+
"tp_alloc" [href="typeobj.html#c.PyTypeObject.tp_alloc" target="_top"]
19+
"tp_new" [href="typeobj.html#c.PyTypeObject.tp_new" target="_top"]
20+
"tp_init" [href="typeobj.html#c.PyTypeObject.tp_init" target="_top"]
21+
{
22+
rank="same"
23+
"alive" [
24+
fontname="Times-Italic"
25+
label=<alive, ref count &gt; 0>
26+
shape=box
27+
]
28+
"tp_traverse" [
29+
href="typeobj.html#c.PyTypeObject.tp_traverse"
30+
shape=octagon
31+
target="_top"
32+
]
33+
}
34+
"tp_finalize" [
35+
href="typeobj.html#c.PyTypeObject.tp_finalize"
36+
shape=octagon
37+
target="_top"
38+
]
39+
"tp_clear" [
40+
href="typeobj.html#c.PyTypeObject.tp_clear"
41+
shape=octagon
42+
target="_top"
43+
]
44+
"ref0" [
45+
fontname="Times-Italic"
46+
label=<ref count == 0>
47+
ordering="in"
48+
shape=box
49+
]
50+
"tp_dealloc" [href="typeobj.html#c.PyTypeObject.tp_dealloc" target="_top"]
51+
"tp_free" [href="typeobj.html#c.PyTypeObject.tp_free" target="_top"]
52+
53+
"start" -> "tp_alloc"
54+
"tp_alloc" -> "tp_new"
55+
"tp_new" -> "tp_init"
56+
"tp_init" -> "alive"
57+
"tp_traverse" -> "alive"
58+
"alive" -> "tp_traverse"
59+
"alive" -> "tp_clear" [label=< cyclic <br/>isolate >]
60+
"alive" -> "tp_finalize" [
61+
dir="back"
62+
label=< resurrected >
63+
]
64+
"alive" -> "tp_finalize" [label=< cyclic <br/>isolate >]
65+
"tp_finalize" -> "tp_clear"
66+
"tp_finalize" -> "ref0"
67+
"tp_clear" -> "ref0"
68+
"tp_clear" -> "tp_dealloc" [
69+
dir="back"
70+
label=< optional<br/>direct call >
71+
]
72+
"alive" -> "ref0"
73+
"ref0" -> "tp_dealloc"
74+
"tp_finalize" -> "tp_dealloc" [
75+
dir="back"
76+
href="lifecycle.html#c.PyObject_CallFinalizerFromDealloc"
77+
label=<
78+
<table border="0" cellborder="0" cellpadding="0" cellspacing="0">
79+
<tr>
80+
<td rowspan="4"> </td>
81+
<td align="left">optional call to</td>
82+
<td rowspan="4"> </td>
83+
</tr>
84+
<tr>
85+
<td align="left"><font face="Courier">PyObject_Call</font></td>
86+
</tr>
87+
<tr>
88+
<td align="left"><font face="Courier">FinalizerFrom</font></td>
89+
</tr>
90+
<tr><td align="left"><font face="Courier">Dealloc</font></td></tr>
91+
</table>
92+
>
93+
target="_top"
94+
]
95+
"tp_dealloc" -> "tp_free" [label=< directly calls >]
96+
}

0 commit comments

Comments
 (0)