]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-130809: Fix `PyFrame_LocalsToFast` copying the wrong value (#130816)
authorKyle Cutler <67761731+kycutler@users.noreply.github.com>
Tue, 11 Mar 2025 00:27:07 +0000 (17:27 -0700)
committerGitHub <noreply@github.com>
Tue, 11 Mar 2025 00:27:07 +0000 (20:27 -0400)
* gh-130809: Fix `PyFrame_LocalsToFast` copying the wrong value

* Skip hidden locals

* test, blurb

* Update Misc/NEWS.d/next/Core_and_Builtins/2025-03-04-12-52-21.gh-issue-130809.fSXq60.rst

Co-authored-by: Tian Gao <gaogaotiantian@hotmail.com>
* Update test

* PR feedback

* formatting

* comment

---------

Co-authored-by: Tian Gao <gaogaotiantian@hotmail.com>
Lib/test/test_listcomps.py
Misc/NEWS.d/next/Core_and_Builtins/2025-03-04-12-52-21.gh-issue-130809.fSXq60.rst [new file with mode: 0644]
Objects/frameobject.c

index 2065afd455de5c50f6e98cb19d6acfe3a28a916b..3e2ef1809779c383f7b203aead723561dcb81304 100644 (file)
@@ -709,6 +709,23 @@ class ListComprehensionTest(unittest.TestCase):
         self._check_in_scopes(code, {"x": 2, "y": [3]}, ns={"x": 3}, scopes=["class"])
         self._check_in_scopes(code, {"x": 2, "y": [2]}, ns={"x": 3}, scopes=["function", "module"])
 
+    def test_name_collision_locals(self):
+        # GH-130809: The existence of a hidden fast from list comprehension
+        # should not cause frame.f_locals on module level to return a new dict
+        # every time it is accessed.
+
+        code = """
+            import sys
+            frame = sys._getframe()
+            f_locals = frame.f_locals
+            foo = 1
+            [foo for foo in [0]]
+            # calls _PyFrame_LocalsToFast which triggers the issue
+            from abc import *
+            same_f_locals = frame.f_locals is f_locals
+        """
+        self._check_in_scopes(code, {"foo": 1, "same_f_locals": True}, scopes=["module"])
+
     def test_exception_locations(self):
         # The location of an exception raised from __init__ or
         # __next__ should should be the iterator expression
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-03-04-12-52-21.gh-issue-130809.fSXq60.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-03-04-12-52-21.gh-issue-130809.fSXq60.rst
new file mode 100644 (file)
index 0000000..419b2a3
--- /dev/null
@@ -0,0 +1,2 @@
+Fixed an issue where ``_PyFrame_LocalsToFast`` tries to write module level
+values to hidden fasts.
index d33c3cde526e9ff1feae9e35bdefe5a77edf8cf6..44f2726f2ccaf167a9ed1e2851185905d1d62177 100644 (file)
@@ -1405,6 +1405,9 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
         if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED)) {
             continue;
         }
+        if (kind & CO_FAST_HIDDEN) {
+            continue;
+        }
         PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
         PyObject *value = PyObject_GetItem(locals, name);
         /* We only care about NULLs if clear is true. */