diff --git a/Lib/test/test_listcomps.py b/Lib/test/test_listcomps.py index fdd2d66b49e..185658ab5a4 100644 --- a/Lib/test/test_listcomps.py +++ b/Lib/test/test_listcomps.py @@ -516,6 +516,19 @@ class ListComprehensionTest(unittest.TestCase): """ 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 2db03f7176e..cfe8224a7e2 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) {