gh-129675: Update documentation for tp_basicsize & tp_itemsize (#129850)

* Update documentation for tp_basicsize & tp_itemsize

- Add alignment requirement
- Mention that ob_size is unreliable if you don't control it
- Add some links for context
- basicsize should include the base type in generaly not just PyObject

This adds a “by-the-way” link to `PyObject_New`, which shouldn't be
used for GC types. In order to be comfortable linking to it, I also
add a link to `PyObject_GC_New` from its docs. And the same for
`*Var` variants, while I'm here.

* Strongly suggest Py_SIZE & Py_SET_SIZE
This commit is contained in:
Petr Viktorin 2025-03-11 11:21:18 +01:00 committed by GitHub
parent faadb446d9
commit ad0f618ab3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 81 additions and 28 deletions

View File

@ -35,6 +35,10 @@ Allocating Objects on the Heap
The size of the memory allocation is determined from the The size of the memory allocation is determined from the
:c:member:`~PyTypeObject.tp_basicsize` field of the type object. :c:member:`~PyTypeObject.tp_basicsize` field of the type object.
Note that this function is unsuitable if *typeobj* has
:c:macro:`Py_TPFLAGS_HAVE_GC` set. For such objects,
use :c:func:`PyObject_GC_New` instead.
.. c:macro:: PyObject_NewVar(TYPE, typeobj, size) .. c:macro:: PyObject_NewVar(TYPE, typeobj, size)
@ -49,6 +53,10 @@ Allocating Objects on the Heap
fields into the same allocation decreases the number of allocations, fields into the same allocation decreases the number of allocations,
improving the memory management efficiency. improving the memory management efficiency.
Note that this function is unsuitable if *typeobj* has
:c:macro:`Py_TPFLAGS_HAVE_GC` set. For such objects,
use :c:func:`PyObject_GC_NewVar` instead.
.. c:function:: void PyObject_Del(void *op) .. c:function:: void PyObject_Del(void *op)

View File

@ -456,6 +456,9 @@ The following functions and structs are used to create
class need *in addition* to the superclass. class need *in addition* to the superclass.
Use :c:func:`PyObject_GetTypeData` to get a pointer to subclass-specific Use :c:func:`PyObject_GetTypeData` to get a pointer to subclass-specific
memory reserved this way. memory reserved this way.
For negative :c:member:`!basicsize`, Python will insert padding when
needed to meet :c:member:`~PyTypeObject.tp_basicsize`'s alignment
requirements.
.. versionchanged:: 3.12 .. versionchanged:: 3.12

View File

@ -537,6 +537,9 @@ PyVarObject Slots
initialized to zero. For :ref:`dynamically allocated type objects initialized to zero. For :ref:`dynamically allocated type objects
<heap-types>`, this field has a special internal meaning. <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.
**Inheritance:** **Inheritance:**
This field is not inherited by subtypes. This field is not inherited by subtypes.
@ -587,47 +590,86 @@ and :c:data:`PyType_Type` effectively act as defaults.)
.. c:member:: Py_ssize_t PyTypeObject.tp_basicsize .. c:member:: Py_ssize_t PyTypeObject.tp_basicsize
Py_ssize_t PyTypeObject.tp_itemsize Py_ssize_t PyTypeObject.tp_itemsize
These fields allow calculating the size in bytes of instances of the type. These fields allow calculating the size in bytes of instances of the type.
There are two kinds of types: types with fixed-length instances have a zero There are two kinds of types: types with fixed-length instances have a zero
:c:member:`~PyTypeObject.tp_itemsize` field, types with variable-length instances have a non-zero :c:member:`!tp_itemsize` field, types with variable-length instances have a non-zero
:c:member:`~PyTypeObject.tp_itemsize` field. For a type with fixed-length instances, all :c:member:`!tp_itemsize` field. For a type with fixed-length instances, all
instances have the same size, given in :c:member:`~PyTypeObject.tp_basicsize`. instances have the same size, given in :c:member:`!tp_basicsize`.
(Exceptions to this rule can be made using
:c:func:`PyUnstable_Object_GC_NewWithExtraData`.)
For a type with variable-length instances, the instances must have an For a type with variable-length instances, the instances must have an
:c:member:`~PyVarObject.ob_size` field, and the instance size is :c:member:`~PyTypeObject.tp_basicsize` plus N :c:member:`~PyVarObject.ob_size` field, and the instance size is
times :c:member:`~PyTypeObject.tp_itemsize`, where N is the "length" of the object. The value of :c:member:`!tp_basicsize` plus N times :c:member:`!tp_itemsize`,
N is typically stored in the instance's :c:member:`~PyVarObject.ob_size` field. There are where N is the "length" of the object.
exceptions: for example, ints use a negative :c:member:`~PyVarObject.ob_size` to indicate a
negative number, and N is ``abs(ob_size)`` there. Also, the presence of an
:c:member:`~PyVarObject.ob_size` field in the instance layout doesn't mean that the instance
structure is variable-length (for example, the structure for the list type has
fixed-length instances, yet those instances have a meaningful :c:member:`~PyVarObject.ob_size`
field).
The basic size includes the fields in the instance declared by the macro Functions like :c:func:`PyObject_NewVar` will take the value of N as an
:c:macro:`PyObject_HEAD` or :c:macro:`PyObject_VAR_HEAD` (whichever is used to argument, and store in the instance's :c:member:`~PyVarObject.ob_size` field.
declare the instance struct) and this in turn includes the :c:member:`~PyObject._ob_prev` and Note that the :c:member:`~PyVarObject.ob_size` field may later be used for
:c:member:`~PyObject._ob_next` fields if they are present. This means that the only correct other purposes. For example, :py:type:`int` instances use the bits of
way to get an initializer for the :c:member:`~PyTypeObject.tp_basicsize` is to use the :c:member:`~PyVarObject.ob_size` in an implementation-defined
way; the underlying storage and its size should be acessed using
:c:func:`PyLong_Export`.
.. note::
The :c:member:`~PyVarObject.ob_size` field should be accessed using
the :c:func:`Py_SIZE()` and :c:func:`Py_SET_SIZE()` macros.
Also, the presence of an :c:member:`~PyVarObject.ob_size` field in the
instance layout doesn't mean that the instance structure is variable-length.
For example, the :py:type:`list` type has fixed-length instances, yet those
instances have a :c:member:`~PyVarObject.ob_size` field.
(As with :py:type:`int`, avoid reading lists' :c:member:`!ob_size` directly.
Call :c:func:`PyList_Size` instead.)
The :c:member:`!tp_basicsize` includes size needed for data of the type's
:c:member:`~PyTypeObject.tp_base`, plus any extra data needed
by each instance.
The correct way to set :c:member:`!tp_basicsize` is to use the
``sizeof`` operator on the struct used to declare the instance layout. ``sizeof`` operator on the struct used to declare the instance layout.
The basic size does not include the GC header size. This struct must include the struct used to declare the base type.
In other words, :c:member:`!tp_basicsize` must be greater than or equal
to the base's :c:member:`!tp_basicsize`.
A note about alignment: if the variable items require a particular alignment, Since every type is a subtype of :py:type:`object`, this struct must
this should be taken care of by the value of :c:member:`~PyTypeObject.tp_basicsize`. Example: include :c:type:`PyObject` or :c:type:`PyVarObject` (depending on
suppose a type implements an array of ``double``. :c:member:`~PyTypeObject.tp_itemsize` is whether :c:member:`~PyVarObject.ob_size` should be included). These are
``sizeof(double)``. It is the programmer's responsibility that usually defined by the macro :c:macro:`PyObject_HEAD` or
:c:member:`~PyTypeObject.tp_basicsize` is a multiple of ``sizeof(double)`` (assuming this is the :c:macro:`PyObject_VAR_HEAD`, respectively.
alignment requirement for ``double``).
For any type with variable-length instances, this field must not be ``NULL``. The basic size does not include the GC header size, as that header is not
part of :c:macro:`PyObject_HEAD`.
For cases where struct used to declare the base type is unknown,
see :c:member:`PyType_Spec.basicsize` and :c:func:`PyType_FromMetaclass`.
Notes about alignment:
- :c:member:`!tp_basicsize` must be a multiple of ``_Alignof(PyObject)``.
When using ``sizeof`` on a ``struct`` that includes
:c:macro:`PyObject_HEAD`, as recommended, the compiler ensures this.
When not using a C ``struct``, or when using compiler
extensions like ``__attribute__((packed))``, it is up to you.
- If the variable items require a particular alignment,
:c:member:`!tp_basicsize` and :c:member:`!tp_itemsize` must each be a
multiple of that alignment.
For example, if a type's variable part stores a ``double``, it is
your responsibility that both fields are a multiple of
``_Alignof(double)``.
**Inheritance:** **Inheritance:**
These fields are inherited separately by subtypes. If the base type has a These fields are inherited separately by subtypes.
non-zero :c:member:`~PyTypeObject.tp_itemsize`, it is generally not safe to set (That is, if the field is set to zero, :c:func:`PyType_Ready` will copy
the value from the base type, indicating that the instances do not
need additional storage.)
If the base type has a non-zero :c:member:`~PyTypeObject.tp_itemsize`, it is generally not safe to set
:c:member:`~PyTypeObject.tp_itemsize` to a different non-zero value in a subtype (though this :c:member:`~PyTypeObject.tp_itemsize` to a different non-zero value in a subtype (though this
depends on the implementation of the base type). depends on the implementation of the base type).