@@ -266,6 +266,21 @@ comprehensions, and generator expressions) to explicitly return independent
266
266
snapshots of the currently assigned local variables, including locally
267
267
referenced nonlocal variables captured in closures.
268
268
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
+
269
284
To ensure debuggers and similar tools can reliably update local variables in
270
285
scopes affected by this change, :attr: `FrameType.f_locals <frame.f_locals> ` now
271
286
returns a write-through proxy to the frame's local and locally referenced
@@ -2235,7 +2250,10 @@ Changes in the Python API
2235
2250
independent snapshot on each call, and hence no longer implicitly updates
2236
2251
previously returned references. Obtaining the legacy CPython behaviour now
2237
2252
requires explicit calls to update the initially returned dictionary with the
2238
- results of subsequent calls to ``locals() ``. (Changed as part of :pep: `667 `.)
2253
+ results of subsequent calls to ``locals() ``. Code execution functions that
2254
+ implicitly target ``locals() `` (such as ``exec `` and ``eval ``) must be
2255
+ passed an explicit namespace to access their results in an optimized scope.
2256
+ (Changed as part of :pep: `667 `.)
2239
2257
2240
2258
* Calling :func: `locals ` from a comprehension at module or class scope
2241
2259
(including via ``exec `` or ``eval ``) once more behaves as if the comprehension
@@ -2323,6 +2341,12 @@ Changes in the C API
2323
2341
to :c:func: `PyUnstable_Code_GetFirstFree `.
2324
2342
(Contributed by Bogdan Romanyuk in :gh: `115781 `.)
2325
2343
2344
+ * Calling :c:func: `PyFrame_GetLocals ` or :c:func: `PyEval_GetLocals ` in an
2345
+ :term: `optimized scope ` now returns a write-through proxy rather than a
2346
+ snapshot that gets updated at ill-specified times. If a snapshot is desired,
2347
+ it must be created explicitly (e.g. with :c:func: `PyDict_Copy `) or by calling
2348
+ the new :c:func: `PyEval_GetFrameLocals ` API. (Changed as part of :pep: `667 `.)
2349
+
2326
2350
* :c:func: `!PyFrame_FastToLocals ` and :c:func: `!PyFrame_FastToLocalsWithError `
2327
2351
no longer have any effect. Calling these functions has been redundant since
2328
2352
Python 3.11, when :c:func: `PyFrame_GetLocals ` was first introduced.
0 commit comments