@@ -266,6 +266,21 @@ comprehensions, and generator expressions) to explicitly return independent
266266snapshots of the currently assigned local variables, including locally
267267referenced nonlocal variables captured in closures.
268268
269+ This change to the semantics of :func: `locals ` in optimized scopes also affects the default
270+ behaviour of code execution functions that implicitly target ``locals() `` if no explicit
271+ namespace is provided (such as :func: `exec ` and :func: `eval `). In previous versions, whether
272+ or not changes could be accessed by calling ``locals() `` after calling the code execution
273+ function was implementation dependent. In CPython specifically, such code would typically
274+ appear to work as desired, but could sometimes fail in optimized scopes based on other code
275+ (including debuggers and code execution tracing tools) potentially resetting the shared
276+ snapshot in that scope. Now, the code will always run against an independent snapshot of the
277+ local variables in optimized scopes, and hence the changes will never be visible in
278+ subsequent calls to ``locals() ``. To access the changes made in these cases, an explicit
279+ namespace reference must now be passed to the relevant function. Alternatively, it may make
280+ sense to update affected code to use a higher level code execution API that returns the
281+ resulting code execution namespace (e.g. :func: `runpy.run_path ` when executing Python
282+ files from disk).
283+
269284To ensure debuggers and similar tools can reliably update local variables in
270285scopes affected by this change, :attr: `FrameType.f_locals <frame.f_locals> ` now
271286returns a write-through proxy to the frame's local and locally referenced
@@ -2223,7 +2238,10 @@ Changes in the Python API
22232238 independent snapshot on each call, and hence no longer implicitly updates
22242239 previously returned references. Obtaining the legacy CPython behaviour now
22252240 requires explicit calls to update the initially returned dictionary with the
2226- results of subsequent calls to ``locals() ``. (Changed as part of :pep: `667 `.)
2241+ results of subsequent calls to ``locals() ``. Code execution functions that
2242+ implicitly target ``locals() `` (such as ``exec `` and ``eval ``) must be
2243+ passed an explicit namespace to access their results in an optimized scope.
2244+ (Changed as part of :pep: `667 `.)
22272245
22282246* Calling :func: `locals ` from a comprehension at module or class scope
22292247 (including via ``exec `` or ``eval ``) once more behaves as if the comprehension
@@ -2311,6 +2329,12 @@ Changes in the C API
23112329 to :c:func: `PyUnstable_Code_GetFirstFree `.
23122330 (Contributed by Bogdan Romanyuk in :gh: `115781 `.)
23132331
2332+ * Calling :c:func: `PyFrame_GetLocals ` or :c:func: `PyEval_GetLocals ` in an
2333+ :term: `optimized scope ` now returns a write-through proxy rather than a
2334+ snapshot that gets updated at ill-specified times. If a snapshot is desired,
2335+ it must be created explicitly (e.g. with :c:func: `PyDict_Copy `) or by calling
2336+ the new :c:func: `PyEval_GetFrameLocals ` API. (Changed as part of :pep: `667 `.)
2337+
23142338* :c:func: `!PyFrame_FastToLocals ` and :c:func: `!PyFrame_FastToLocalsWithError `
23152339 no longer have any effect. Calling these functions has been redundant since
23162340 Python 3.11, when :c:func: `PyFrame_GetLocals ` was first introduced.
0 commit comments