[Python-checkins] gh-104619: never leak comprehension locals to outer locals() (#104637)

JelleZijlstra webhook-mailer at python.org
Thu May 18 21:50:31 EDT 2023


https://github.com/python/cpython/commit/70c77964778817907fbcc2a047a2abad4eb6e127
commit: 70c77964778817907fbcc2a047a2abad4eb6e127
branch: main
author: Carl Meyer <carl at oddbird.net>
committer: JelleZijlstra <jelle.zijlstra at gmail.com>
date: 2023-05-18T18:50:24-07:00
summary:

gh-104619: never leak comprehension locals to outer locals() (#104637)

files:
M Lib/test/test_listcomps.py
M Python/compile.c

diff --git a/Lib/test/test_listcomps.py b/Lib/test/test_listcomps.py
index fdd2d66b49e0..185658ab5a4c 100644
--- a/Lib/test/test_listcomps.py
+++ b/Lib/test/test_listcomps.py
@@ -516,6 +516,19 @@ def test_assign_to_comp_iter_var_in_outer_function(self):
         """
         self._check_in_scopes(code, {"a": [1]}, scopes=["function"])
 
+    def test_no_leakage_to_locals(self):
+        code = """
+            def b():
+                [a for b in [1] for _ in []]
+                return b, locals()
+            r, s = b()
+            x = r is b
+            y = list(s.keys())
+        """
+        self._check_in_scopes(code, {"x": True, "y": []}, scopes=["module"])
+        self._check_in_scopes(code, {"x": True, "y": ["b"]}, scopes=["function"])
+        self._check_in_scopes(code, raises=NameError, scopes=["class"])
+
 
 __test__ = {'doctests' : doctests}
 
diff --git a/Python/compile.c b/Python/compile.c
index 2db03f7176ea..cfe8224a7e27 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -5490,31 +5490,29 @@ push_inlined_comprehension_state(struct compiler *c, location loc,
                 }
                 Py_DECREF(outv);
             }
-            if (outsc == LOCAL || outsc == CELL || outsc == FREE) {
-                // local names bound in comprehension must be isolated from
-                // outer scope; push existing value (which may be NULL if
-                // not defined) on stack
+            // local names bound in comprehension must be isolated from
+            // outer scope; push existing value (which may be NULL if
+            // not defined) on stack
+            if (state->pushed_locals == NULL) {
+                state->pushed_locals = PyList_New(0);
                 if (state->pushed_locals == NULL) {
-                    state->pushed_locals = PyList_New(0);
-                    if (state->pushed_locals == NULL) {
-                        return ERROR;
-                    }
-                }
-                // in the case of a cell, this will actually push the cell
-                // itself to the stack, then we'll create a new one for the
-                // comprehension and restore the original one after
-                ADDOP_NAME(c, loc, LOAD_FAST_AND_CLEAR, k, varnames);
-                if (scope == CELL) {
-                    if (outsc == FREE) {
-                        ADDOP_NAME(c, loc, MAKE_CELL, k, freevars);
-                    } else {
-                        ADDOP_NAME(c, loc, MAKE_CELL, k, cellvars);
-                    }
-                }
-                if (PyList_Append(state->pushed_locals, k) < 0) {
                     return ERROR;
                 }
             }
+            // in the case of a cell, this will actually push the cell
+            // itself to the stack, then we'll create a new one for the
+            // comprehension and restore the original one after
+            ADDOP_NAME(c, loc, LOAD_FAST_AND_CLEAR, k, varnames);
+            if (scope == CELL) {
+                if (outsc == FREE) {
+                    ADDOP_NAME(c, loc, MAKE_CELL, k, freevars);
+                } else {
+                    ADDOP_NAME(c, loc, MAKE_CELL, k, cellvars);
+                }
+            }
+            if (PyList_Append(state->pushed_locals, k) < 0) {
+                return ERROR;
+            }
         }
     }
     if (state->pushed_locals) {



More information about the Python-checkins mailing list