Skip to content

Commit

Permalink
pythongh-118934: Fix PyEval_GetLocals docs (PEP 667)
Browse files Browse the repository at this point in the history
PEP 667's description of the planned changes to PyEval_GetLocals
was internally inconsistent when accepted, so the docs added for
pythongh-74929 didn't match either the current behaviour or the intended
behaviour once pythongh-118934 is fixed.

This PR updates the documentation and 3.13 What's New to match the
intended behaviour (once pythongh-118934 is fixed).
  • Loading branch information
ncoghlan committed Jun 2, 2024
1 parent c618f7d commit 4906559
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 21 deletions.
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
33 changes: 25 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,24 @@ 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. Changes made 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. 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 +2521,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

0 comments on commit 4906559

Please sign in to comment.