Skip to content

gh-135913: Document ob_refcnt, ob_type, ob_size #135914

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

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
75 changes: 59 additions & 16 deletions Doc/c-api/structures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,52 @@ under :ref:`reference counting <countingrefs>`.
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`.
Comment on lines +36 to +40
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is accurate anymore:

cpython/Include/object.h

Lines 128 to 146 in a88b49c

union {
#if SIZEOF_VOID_P > 4
PY_INT64_T ob_refcnt_full; /* This field is needed for efficient initialization with Clang on ARM */
struct {
# if PY_BIG_ENDIAN
uint16_t ob_flags;
uint16_t ob_overflow;
uint32_t ob_refcnt;
# else
uint32_t ob_refcnt;
uint16_t ob_overflow;
uint16_t ob_flags;
# endif
};
#else
Py_ssize_t ob_refcnt;
#endif
_Py_ALIGNED_DEF(_PyObject_MIN_ALIGNMENT, char) _aligner;
};

Copy link
Member Author

Choose a reason for hiding this comment

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

Hm, yes, the type isn't Py_ssize_t any more.
That's an issue in the current documentation too; guess it's time to reopen #125174 for a docs update.
Similarly, a lot of the docs talk about PyObject_HEAD_INIT setting the refcount to 1. I can't fix everything in this PR...

Copy link
Member Author

Choose a reason for hiding this comment

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

Sphinx needs a type for .. c:member:. I don't think I can do better than the note below.


The field type may be different from ``Py_ssize_t``, depending on
build configuration and platform.

.. 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
Expand Down Expand Up @@ -103,9 +137,8 @@ under :ref:`reference counting <countingrefs>`.

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 <borrowed reference>` 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.
Expand All @@ -122,16 +155,26 @@ under :ref:`reference counting <countingrefs>`.

.. 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 <heap-types>`, 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.
Expand All @@ -140,7 +183,7 @@ under :ref:`reference counting <countingrefs>`.

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

Expand Down
11 changes: 5 additions & 6 deletions Doc/c-api/typeobj.rst
Original file line number Diff line number Diff line change
Expand Up @@ -492,9 +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
: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 <static-types>`, the type's instances (objects whose :c:member:`~PyObject.ob_type`
points back to the type) do *not* count as references. But for
Expand All @@ -506,7 +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
: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
Expand All @@ -532,14 +532,13 @@ type objects) *must* have the :c:member:`~PyVarObject.ob_size` field.
PyVarObject Slots
-----------------

.. c:member:: Py_ssize_t PyVarObject.ob_size
:c:member:`PyVarObject.ob_size`

For :ref:`statically allocated type objects <static-types>`, this should be
initialized to zero. For :ref:`dynamically allocated type objects
<heap-types>`, 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:**

Expand Down
Loading