From 5ae4df1939d0cc0a645d140b8a5cbc06d977b561 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 25 Jun 2025 10:09:00 +0200 Subject: [PATCH 1/4] gh-135913: Document ob_refcnt, ob_type, ob_size --- Doc/c-api/structures.rst | 72 +++++++++++++++++++++++++++++++--------- Doc/c-api/typeobj.rst | 3 ++ 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 987bc167c68d6c..3d5d86cb277961 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -28,18 +28,49 @@ under :ref:`reference counting `. object. In a normal "release" build, it contains only the object's reference count and a pointer to the corresponding type object. Nothing is actually declared to be a :c:type:`PyObject`, but every pointer - to a Python object can be cast to a :c:expr:`PyObject*`. Access to the - members must be done by using the macros :c:macro:`Py_REFCNT` and - :c:macro:`Py_TYPE`. + to a Python object can be cast to a :c:expr:`PyObject*`. + + The members must not be accessed directly; instead use macros such as + :c:macro:`Py_REFCNT` and :c:macro:`Py_TYPE`. + + .. c:member:: Py_ssize_t ob_refcnt + + The object's reference count, as returned by :c:macro:`Py_REFCNT`. + Do not use this field directly; instead use functions and macros such as + :c:macro:`!Py_REFCNT`, :c:func:`Py_INCREF` and :c:func:`Py_DecRef`. + + .. c:member:: PyTypeObject* ob_type + + The object's type. + Do not use this field directly; use :c:macro:`Py_TYPE` and + :c:func:`Py_SET_TYPE` instead. .. c:type:: PyVarObject - This is an extension of :c:type:`PyObject` that adds the :c:member:`~PyVarObject.ob_size` - field. This is only used for objects that have some notion of *length*. - This type does not often appear in the Python/C API. - Access to the members must be done by using the macros - :c:macro:`Py_REFCNT`, :c:macro:`Py_TYPE`, and :c:macro:`Py_SIZE`. + An extension of :c:type:`PyObject` that adds the + :c:member:`~PyVarObject.ob_size` field. + This is intended for objects that have some notion of *length*. + + As with :c:type:`!PyObject`, the members must not be accessed directly; + instead use macros such as :c:macro:`Py_SIZE`, :c:macro:`Py_REFCNT` and + :c:macro:`Py_TYPE`. + + .. c:member:: Py_ssize_t ob_size + + A size field, whose contents should be considered an object's internal + implementation detail. + + Do not use this field directly; use :c:macro:`Py_SIZE` instead. + + Object creation functions such as :c:func:`PyObject_NewVar` will + generally set this field to the requested size (number of items). + After creation, arbitrary values can be stored in :c:member:`!ob_size` + using :c:macro:`Py_SET_SIZE`. + + To get an object's publicly exposed length, as returned by + the Python function :py:func:`len`, use :c:func:`PyObject_Length` + instead. .. c:macro:: PyObject_HEAD @@ -103,9 +134,8 @@ under :ref:`reference counting `. Get the type of the Python object *o*. - Return a :term:`borrowed reference`. - - Use the :c:func:`Py_SET_TYPE` function to set an object type. + The returned reference is :term:`borrowed ` from *o*. + Do not release it with :c:func:`Py_DECREF` or similar. .. versionchanged:: 3.11 :c:func:`Py_TYPE()` is changed to an inline static function. @@ -122,16 +152,26 @@ under :ref:`reference counting `. .. c:function:: void Py_SET_TYPE(PyObject *o, PyTypeObject *type) - Set the object *o* type to *type*. + Set the type of object *o* to *type*, without any checking or reference + counting. + + This is a very low-level operation. + Consider instead setting the Python attribute :attr:`~object.__class__` + using :c:func:`PyObject_SetAttrString` or similar. + + Note that assigning an incompatible type can lead to undefined behavior. + + If *type* is a :ref:`heap type `, the caller must create a + new reference to it. + Similarly, if the old type of *o* is a heap type, the caller must release + a reference to that type. .. versionadded:: 3.9 .. c:function:: Py_ssize_t Py_SIZE(PyVarObject *o) - Get the size of the Python object *o*. - - Use the :c:func:`Py_SET_SIZE` function to set an object size. + Get the :c:member:`~PyVarObject.ob_size` field of *o*. .. versionchanged:: 3.11 :c:func:`Py_SIZE()` is changed to an inline static function. @@ -140,7 +180,7 @@ under :ref:`reference counting `. .. c:function:: void Py_SET_SIZE(PyVarObject *o, Py_ssize_t size) - Set the object *o* size to *size*. + Set the :c:member:`~PyVarObject.ob_size` field of *o* to *size*. .. versionadded:: 3.9 diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index af2bead3bb5004..912e30eb56e236 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -493,6 +493,7 @@ type objects) *must* have the :c:member:`~PyVarObject.ob_size` field. .. c:member:: Py_ssize_t PyObject.ob_refcnt + :noindex: This is the type object's reference count, initialized to ``1`` by the ``PyObject_HEAD_INIT`` macro. Note that for :ref:`statically allocated type @@ -507,6 +508,7 @@ type objects) *must* have the :c:member:`~PyVarObject.ob_size` field. .. c:member:: PyTypeObject* PyObject.ob_type + :noindex: This is the type's type, in other words its metatype. It is initialized by the argument to the ``PyObject_HEAD_INIT`` macro, and its value should normally be @@ -533,6 +535,7 @@ PyVarObject Slots ----------------- .. c:member:: Py_ssize_t PyVarObject.ob_size + :noindex: For :ref:`statically allocated type objects `, this should be initialized to zero. For :ref:`dynamically allocated type objects From ea8dab012110966acd6181f6fdeb3187d9322661 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 26 Jun 2025 10:12:57 +0200 Subject: [PATCH 2/4] Work around `c:member` not having the `noindex` option See ref. https://www.sphinx-doc.org/en/master/usage/domains/index.html#basic-markup --- Doc/c-api/structures.rst | 2 ++ Doc/c-api/typeobj.rst | 14 +++++--------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 3d5d86cb277961..50f8f7b4f3a595 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -39,6 +39,8 @@ under :ref:`reference counting `. Do not use this field directly; instead use functions and macros such as :c:macro:`!Py_REFCNT`, :c:func:`Py_INCREF` and :c:func:`Py_DecRef`. + The type may be different from `Py_ssize_t` on platforms. + .. c:member:: PyTypeObject* ob_type The object's type. diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 912e30eb56e236..060d6f60174b41 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -492,10 +492,9 @@ metatype) initializes :c:member:`~PyTypeObject.tp_itemsize`, which means that it type objects) *must* have the :c:member:`~PyVarObject.ob_size` field. -.. c:member:: Py_ssize_t PyObject.ob_refcnt - :noindex: +:c:member:`PyObject.ob_refcnt` - This is the type object's reference count, initialized to ``1`` by the + The type object's reference count is initialized to ``1`` by the ``PyObject_HEAD_INIT`` macro. Note that for :ref:`statically allocated type objects `, the type's instances (objects whose :c:member:`~PyObject.ob_type` points back to the type) do *not* count as references. But for @@ -507,8 +506,7 @@ type objects) *must* have the :c:member:`~PyVarObject.ob_size` field. This field is not inherited by subtypes. -.. c:member:: PyTypeObject* PyObject.ob_type - :noindex: +:c:member:`PyObject.ob_type` This is the type's type, in other words its metatype. It is initialized by the argument to the ``PyObject_HEAD_INIT`` macro, and its value should normally be @@ -534,15 +532,13 @@ type objects) *must* have the :c:member:`~PyVarObject.ob_size` field. PyVarObject Slots ----------------- -.. c:member:: Py_ssize_t PyVarObject.ob_size - :noindex: +:c:member:`PyVarObject.ob_size` For :ref:`statically allocated type objects `, this should be initialized to zero. For :ref:`dynamically allocated type objects `, this field has a special internal meaning. - This field should be accessed using the :c:func:`Py_SIZE()` and - :c:func:`Py_SET_SIZE()` macros. + This field should be accessed using the :c:func:`Py_SIZE()` macro. **Inheritance:** From aca3310cd067cc603cece6c2d5315afee6b27d3d Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 2 Jul 2025 15:33:33 +0200 Subject: [PATCH 3/4] Fix note about type being different --- Doc/c-api/structures.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 50f8f7b4f3a595..a36caa7b6f65a9 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -39,7 +39,8 @@ under :ref:`reference counting `. Do not use this field directly; instead use functions and macros such as :c:macro:`!Py_REFCNT`, :c:func:`Py_INCREF` and :c:func:`Py_DecRef`. - The type may be different from `Py_ssize_t` on platforms. + The field type may be different from `Py_ssize_t`, depending on + build configuration and platform. .. c:member:: PyTypeObject* ob_type From e4161c854b8d5707856c68caf9166b4023e5fc10 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 2 Jul 2025 15:35:58 +0200 Subject: [PATCH 4/4] Fix ReST syntax --- Doc/c-api/structures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index a36caa7b6f65a9..58dd915e04f619 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -39,7 +39,7 @@ under :ref:`reference counting `. Do not use this field directly; instead use functions and macros such as :c:macro:`!Py_REFCNT`, :c:func:`Py_INCREF` and :c:func:`Py_DecRef`. - The field type may be different from `Py_ssize_t`, depending on + The field type may be different from ``Py_ssize_t``, depending on build configuration and platform. .. c:member:: PyTypeObject* ob_type