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-92678: Document that you shouldn't be doing your own dictionary offset calculations. #95598

Merged
merged 5 commits into from
Aug 9, 2022
Merged
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
18 changes: 18 additions & 0 deletions Doc/c-api/object.rst
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@ Object Protocol
A generic implementation for the getter of a ``__dict__`` descriptor. It
creates the dictionary if necessary.

This function may also be called to get the :py:attr:`~object.__dict__`
of the object *o*. Pass ``NULL`` for *context* when calling it.
Since this function may need to allocate memory for the
dictionary, it may be more efficient to call :c:func:`PyObject_GetAttr`
when accessing an attribute on the object.

On failure, returns ``NULL`` with an exception set.

.. versionadded:: 3.3


Expand All @@ -137,6 +145,16 @@ Object Protocol
.. versionadded:: 3.3


.. c:function:: PyObject** _PyObject_GetDictPtr(PyObject *obj)

Return a pointer to :py:attr:`~object.__dict__` of the object *obj*.
If there is no ``__dict__``, return ``NULL`` without setting an exception.

This function may need to allocate memory for the
dictionary, so it may be more efficient to call :c:func:`PyObject_GetAttr`
when accessing an attribute on the object.


.. c:function:: PyObject* PyObject_RichCompare(PyObject *o1, PyObject *o2, int opid)

Compare the values of *o1* and *o2* using the operation specified by *opid*,
Expand Down
17 changes: 5 additions & 12 deletions Doc/c-api/typeobj.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1709,18 +1709,11 @@ and :c:type:`PyType_Type` effectively act as defaults.)
:c:member:`~PyTypeObject.tp_dictoffset` should be set to ``-4`` to indicate that the dictionary is
at the very end of the structure.

The real dictionary offset in an instance can be computed from a negative
:c:member:`~PyTypeObject.tp_dictoffset` as follows::

dictoffset = tp_basicsize + abs(ob_size)*tp_itemsize + tp_dictoffset
if dictoffset is not aligned on sizeof(void*):
round up to sizeof(void*)

where :c:member:`~PyTypeObject.tp_basicsize`, :c:member:`~PyTypeObject.tp_itemsize` and :c:member:`~PyTypeObject.tp_dictoffset` are
taken from the type object, and :attr:`ob_size` is taken from the instance. The
absolute value is taken because ints use the sign of :attr:`ob_size` to
store the sign of the number. (There's never a need to do this calculation
yourself; it is done for you by :c:func:`_PyObject_GetDictPtr`.)
The :c:member:`~PyTypeObject.tp_dictoffset` should be regarded as write-only.
To get the pointer to the dictionary call :c:func:`PyObject_GenericGetDict`.
Calling :c:func:`PyObject_GenericGetDict` may need to allocate memory for the
dictionary, so it is may be more efficient to call :c:func:`PyObject_GetAttr`
when accessing an attribute on the object.

**Inheritance:**

Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1498,6 +1498,10 @@ Changes in the Python API
:func:`compile` and other related functions. If invalid positions are detected,
a :exc:`ValueError` will be raised. (Contributed by Pablo Galindo in :gh:`93351`)

* :c:member:`~PyTypeObject.tp_dictoffset` should be treated as write-only.
It can be set to describe C extension clases to the VM, but should be regarded
as meaningless when read. To get the pointer to the object's dictionary call
:c:func:`PyObject_GenericGetDict` instead.

Build Changes
=============
Expand Down
6 changes: 5 additions & 1 deletion Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -1079,7 +1079,11 @@ _PyObject_ComputedDictPointer(PyObject *obj)

/* Helper to get a pointer to an object's __dict__ slot, if any.
* Creates the dict from inline attributes if necessary.
* Does not set an exception. */
* Does not set an exception.
*
* Note that the tp_dictoffset docs used to recommend this function,
* so it should be treated as part of the public API.
*/
PyObject **
_PyObject_GetDictPtr(PyObject *obj)
{
Expand Down