@@ -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
@@ -2223,7 +2238,10 @@ Changes in the Python API
2223
2238
independent snapshot on each call, and hence no longer implicitly updates
2224
2239
previously returned references. Obtaining the legacy CPython behaviour now
2225
2240
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 `.)
2227
2245
2228
2246
* Calling :func: `locals ` from a comprehension at module or class scope
2229
2247
(including via ``exec `` or ``eval ``) once more behaves as if the comprehension
@@ -2311,6 +2329,12 @@ Changes in the C API
2311
2329
to :c:func: `PyUnstable_Code_GetFirstFree `.
2312
2330
(Contributed by Bogdan Romanyuk in :gh: `115781 `.)
2313
2331
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
+
2314
2338
* :c:func: `!PyFrame_FastToLocals ` and :c:func: `!PyFrame_FastToLocalsWithError `
2315
2339
no longer have any effect. Calling these functions has been redundant since
2316
2340
Python 3.11, when :c:func: `PyFrame_GetLocals ` was first introduced.
0 commit comments