https://github.com/python/cpython/commit/fd6cd621e0cce6ba2e737103d2a62b5ade90f41f
commit: fd6cd621e0cce6ba2e737103d2a62b5ade90f41f
branch: main
author: Alyssa Coghlan <[email protected]>
committer: ncoghlan <[email protected]>
date: 2024-06-02T04:44:29Z
summary:

gh-118934: Fix PyEval_GetLocals docs (PEP 667) (#119932)

PEP 667's description of the planned changes to PyEval_GetLocals
was internally inconsistent when accepted, so the docs added for
gh-74929 didn't match either the current behaviour or the intended
behaviour once gh-118934 is fixed.

This PR updates the documentation and 3.13 What's New to match the
intended behaviour (once gh-118934 is fixed).

It also tidies up lingering references to `f_locals` always being a
dictionary (this hasn't been true since at least when custom
namespace support for class statement execution was added)

files:
M Doc/c-api/reflection.rst
M Doc/reference/datamodel.rst
M Doc/whatsnew/3.13.rst

diff --git a/Doc/c-api/reflection.rst b/Doc/c-api/reflection.rst
index af9a1a74ec137e..038e6977104560 100644
--- a/Doc/c-api/reflection.rst
+++ b/Doc/c-api/reflection.rst
@@ -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)
diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst
index 134385ed2f1860..9110060a6177e5 100644
--- a/Doc/reference/datamodel.rst
+++ b/Doc/reference/datamodel.rst
@@ -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
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index ab260bf2a2d740..66626ac06428b9 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -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
@@ -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
@@ -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
@@ -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
 ------------------------------
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to