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-118934: Fix PyEval_GetLocals docs (PEP 667) #119932

Merged
merged 4 commits into from
Jun 2, 2024
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
27 changes: 16 additions & 11 deletions Doc/c-api/reflection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,28 @@ Reflection

.. deprecated:: 3.13

To avoid creating a reference cycle in :term:`optimized scopes <optimized scope>`,
use either :c:func:`PyEval_GetFrameLocals` to obtain the same behaviour as calling
Use either :c:func:`PyEval_GetFrameLocals` to obtain the same behaviour as calling
:func:`locals` in Python code, or else call :c:func:`PyFrame_GetLocals` on the result
of :c:func:`PyEval_GetFrame` to get the same result as this function without having to
cache the proxy instance on the underlying frame.
of :c:func:`PyEval_GetFrame` to access the :attr:`~frame.f_locals` attribute of the
currently executing frame.

Return the :attr:`~frame.f_locals` attribute of the currently executing frame,
Return a mapping providing access to the local variables in the current execution frame,
or ``NULL`` if no frame is currently executing.

If the frame refers to an :term:`optimized scope`, this returns a
write-through proxy object that allows modifying the locals.
In all other cases (classes, modules, :func:`exec`, :func:`eval`) it returns
the mapping representing the frame locals directly (as described for
:func:`locals`).
Refer to :func:`locals` for details of the mapping returned at different scopes.

As this function returns a :term:`borrowed reference`, the dictionary returned for
:term:`optimized scopes <optimized scope>` is cached on the frame object and will remain
alive as long as the frame object does. Unlike :c:func:`PyEval_GetFrameLocals` and
:func:`locals`, subsequent calls to this function in the same frame will update the
contents of the cached dictionary to reflect changes in the state of the local variables
rather than returning a new snapshot.

.. versionchanged:: 3.13
As part of :pep:`667`, return a proxy object for optimized scopes.
As part of :pep:`667`, :c:func:`PyFrame_GetLocals`, :func:`locals`, and
:attr:`FrameType.f_locals <frame.f_locals>` no longer make use of the shared cache
dictionary. Refer to the :ref:`What's New entry <whatsnew313-locals-semantics>` for
additional details.


.. c:function:: PyObject* PyEval_GetGlobals(void)
Expand Down
4 changes: 2 additions & 2 deletions Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1347,13 +1347,13 @@ Special read-only attributes
``object.__getattr__`` with arguments ``obj`` and ``"f_code"``.

* - .. attribute:: frame.f_locals
- The dictionary used by the frame to look up
- The mapping used by the frame to look up
:ref:`local variables <naming>`.
If the frame refers to an :term:`optimized scope`,
this may return a write-through proxy object.

.. versionchanged:: 3.13
Return a proxy for functions and comprehensions.
Return a proxy for optimized scopes.

* - .. attribute:: frame.f_globals
- The dictionary used by the frame to look up
Expand Down
36 changes: 28 additions & 8 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,9 @@ returns a write-through proxy to the frame's local and locally referenced
nonlocal variables in these scopes, rather than returning an inconsistently
updated shared ``dict`` instance with undefined runtime semantics.

See :pep:`667` for more details, including related C API changes and
deprecations.
See :pep:`667` for more details, including related C API changes and deprecations. Porting
notes are also provided below for the affected :ref:`Python APIs <pep667-porting-notes-py>`
and :ref:`C APIs <pep667-porting-notes-c>`.

(PEP and implementation contributed by Mark Shannon and Tian Gao in
:gh:`74929`. Documentation updates provided by Guido van Rossum and
Expand Down Expand Up @@ -2246,6 +2247,8 @@ Changes in the Python API
returned by :meth:`zipfile.ZipFile.open` was changed from ``'r'`` to ``'rb'``.
(Contributed by Serhiy Storchaka in :gh:`115961`.)

.. _pep667-porting-notes-py:

* Calling :func:`locals` in an :term:`optimized scope` now produces an
independent snapshot on each call, and hence no longer implicitly updates
previously returned references. Obtaining the legacy CPython behaviour now
Expand Down Expand Up @@ -2341,15 +2344,27 @@ Changes in the C API
to :c:func:`PyUnstable_Code_GetFirstFree`.
(Contributed by Bogdan Romanyuk in :gh:`115781`.)

* Calling :c:func:`PyFrame_GetLocals` or :c:func:`PyEval_GetLocals` in an
:term:`optimized scope` now returns a write-through proxy rather than a
snapshot that gets updated at ill-specified times. If a snapshot is desired,
it must be created explicitly (e.g. with :c:func:`PyDict_Copy`) or by calling
the new :c:func:`PyEval_GetFrameLocals` API. (Changed as part of :pep:`667`.)
.. _pep667-porting-notes-c:

* The effects of mutating the dictionary returned from :c:func:`PyEval_GetLocals` in an
:term:`optimized scope` have changed. New dict entries added this way will now *only* be
visible to subsequent :c:func:`PyEval_GetLocals` calls in that frame, as
:c:func:`PyFrame_GetLocals`, :func:`locals`, and
:attr:`FrameType.f_locals <frame.f_locals>` no longer access the same underlying cached
dictionary. Changes made to entries for actual variable names and names added via the
write-through proxy interfaces will be overwritten on subsequent calls to
:c:func:`PyEval_GetLocals` in that frame. The recommended code update depends on how the
function was being used, so refer to the deprecation notice on the function for details.
(Changed as part of :pep:`667`.)

* Calling :c:func:`PyFrame_GetLocals` in an :term:`optimized scope` now returns a
write-through proxy rather than a snapshot that gets updated at ill-specified times.
If a snapshot is desired, it must be created explicitly (e.g. with :c:func:`PyDict_Copy`)
or by calling the new :c:func:`PyEval_GetFrameLocals` API. (Changed as part of :pep:`667`.)

* :c:func:`!PyFrame_FastToLocals` and :c:func:`!PyFrame_FastToLocalsWithError`
no longer have any effect. Calling these functions has been redundant since
Python 3.11, when :c:func:`PyFrame_GetLocals` was first introduced.
Python 3.11, when :c:func:`PyFrame_GetLocals` was first introduced.
(Changed as part of :pep:`667`.)

* :c:func:`!PyFrame_LocalsToFast` no longer has any effect. Calling this function
Expand Down Expand Up @@ -2509,6 +2524,11 @@ Deprecated C APIs
:c:func:`PyWeakref_GetRef` on Python 3.12 and older.
(Contributed by Victor Stinner in :gh:`105927`.)

* Deprecate the :c:func:`PyEval_GetBuiltins`, :c:func:`PyEval_GetGlobals`, and
:c:func:`PyEval_GetLocals` functions, which return a :term:`borrowed reference`.
Refer to the deprecation notices on each function for their recommended replacements.
(Soft deprecated as part of :pep:`667`.)

Pending Removal in Python 3.14
------------------------------

Expand Down
Loading