https://github.com/python/cpython/commit/36ca00f44d0136384f3f89b7fe2632bc802ec64d
commit: 36ca00f44d0136384f3f89b7fe2632bc802ec64d
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: ncoghlan <[email protected]>
date: 2024-06-02T15:41:25+10:00
summary:

[3.13] gh-118888: Further PEP 667 docs updates (gh-119894)

* Clarify impact on default behaviour of exec, eval, etc
* Update documentation for changes to PyEval_GetLocals (gh-74929)

Closes gh-118888
(cherry picked from commit 2180991ea3d50f56595edae241cc92dd4e7de642)

Co-authored-by: Alyssa Coghlan <[email protected]>

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

diff --git a/Doc/c-api/reflection.rst b/Doc/c-api/reflection.rst
index 5dcfe40c2ce92b..af9a1a74ec137e 100644
--- a/Doc/c-api/reflection.rst
+++ b/Doc/c-api/reflection.rst
@@ -19,11 +19,24 @@ Reflection
 
    .. deprecated:: 3.13
 
-      Use :c:func:`PyEval_GetFrameLocals` instead.
+      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
+      :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.
 
-   Return a dictionary of the local variables in the current execution frame,
+   Return the :attr:`~frame.f_locals` attribute of the currently executing 
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`).
+
+   .. versionchanged:: 3.13
+      As part of :pep:`667`, return a proxy object for optimized scopes.
+
 
 .. c:function:: PyObject* PyEval_GetGlobals(void)
 
@@ -57,6 +70,10 @@ Reflection
    or ``NULL`` if no frame is currently executing. Equivalent to calling
    :func:`locals` in Python code.
 
+   To access :attr:`~frame.f_locals` on the current frame without making an 
independent
+   snapshot in :term:`optimized scopes <optimized scope>`, call 
:c:func:`PyFrame_GetLocals`
+   on the result of :c:func:`PyEval_GetFrame`.
+
    .. versionadded:: 3.13
 
 
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index 09dc5375186993..974ecda13f6ab2 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -266,6 +266,21 @@ comprehensions, and generator expressions) to explicitly 
return independent
 snapshots of the currently assigned local variables, including locally
 referenced nonlocal variables captured in closures.
 
+This change to the semantics of :func:`locals` in optimized scopes also 
affects the default
+behaviour of code execution functions that implicitly target ``locals()`` if 
no explicit
+namespace is provided (such as :func:`exec` and :func:`eval`). In previous 
versions, whether
+or not changes could be accessed by calling ``locals()`` after calling the 
code execution
+function was implementation dependent. In CPython specifically, such code 
would typically
+appear to work as desired, but could sometimes fail in optimized scopes based 
on other code
+(including debuggers and code execution tracing tools) potentially resetting 
the shared
+snapshot in that scope. Now, the code will always run against an independent 
snapshot of the
+local variables in optimized scopes, and hence the changes will never be 
visible in
+subsequent calls to ``locals()``. To access the changes made in these cases, 
an explicit
+namespace reference must now be passed to the relevant function. 
Alternatively, it may make
+sense to update affected code to use a higher level code execution API that 
returns the
+resulting code execution namespace (e.g. :func:`runpy.run_path` when executing 
Python
+files from disk).
+
 To ensure debuggers and similar tools can reliably update local variables in
 scopes affected by this change, :attr:`FrameType.f_locals <frame.f_locals>` now
 returns a write-through proxy to the frame's local and locally referenced
@@ -2223,7 +2238,10 @@ Changes in the Python API
   independent snapshot on each call, and hence no longer implicitly updates
   previously returned references. Obtaining the legacy CPython behaviour now
   requires explicit calls to update the initially returned dictionary with the
-  results of subsequent calls to ``locals()``. (Changed as part of :pep:`667`.)
+  results of subsequent calls to ``locals()``. Code execution functions that
+  implicitly target ``locals()`` (such as ``exec`` and ``eval``) must be
+  passed an explicit namespace to access their results in an optimized scope.
+  (Changed as part of :pep:`667`.)
 
 * Calling :func:`locals` from a comprehension at module or class scope
   (including via ``exec`` or ``eval``) once more behaves as if the 
comprehension
@@ -2311,6 +2329,12 @@ 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`.)
+
 * :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-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