Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 90b34de

Browse files
committed
run all listcomp scope tests in module, class, and func scope
1 parent b6a025b commit 90b34de

File tree

1 file changed

+134
-144
lines changed

1 file changed

+134
-144
lines changed

Lib/test/test_listcomps.py

Lines changed: 134 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import doctest
2+
import textwrap
23
import unittest
34

45

@@ -87,154 +88,143 @@
8788
>>> [None for i in range(10)]
8889
[None, None, None, None, None, None, None, None, None, None]
8990
90-
########### Tests for various scoping corner cases ############
91-
92-
Return lambdas that use the iteration variable as a default argument
93-
94-
>>> items = [(lambda i=i: i) for i in range(5)]
95-
>>> [x() for x in items]
96-
[0, 1, 2, 3, 4]
97-
98-
Same again, only this time as a closure variable
99-
100-
>>> items = [(lambda: i) for i in range(5)]
101-
>>> [x() for x in items]
102-
[4, 4, 4, 4, 4]
103-
104-
Another way to test that the iteration variable is local to the list comp
105-
106-
>>> items = [(lambda: i) for i in range(5)]
107-
>>> i = 20
108-
>>> [x() for x in items]
109-
[4, 4, 4, 4, 4]
110-
111-
And confirm that a closure can jump over the list comp scope
112-
113-
>>> items = [(lambda: y) for i in range(5)]
114-
>>> y = 2
115-
>>> [x() for x in items]
116-
[2, 2, 2, 2, 2]
117-
118-
We also repeat each of the above scoping tests inside a function:
119-
120-
>>> def test_func():
121-
... items = [(lambda i=i: i) for i in range(5)]
122-
... return [x() for x in items]
123-
>>> test_func()
124-
[0, 1, 2, 3, 4]
125-
126-
>>> def test_func():
127-
... items = [(lambda: i) for i in range(5)]
128-
... return [x() for x in items]
129-
>>> test_func()
130-
[4, 4, 4, 4, 4]
131-
132-
>>> def test_func():
133-
... items = [(lambda: i) for i in range(5)]
134-
... i = 20
135-
... return [x() for x in items]
136-
>>> test_func()
137-
[4, 4, 4, 4, 4]
138-
139-
>>> def test_func():
140-
... items = [(lambda: y) for i in range(5)]
141-
... y = 2
142-
... return [x() for x in items]
143-
>>> test_func()
144-
[2, 2, 2, 2, 2]
145-
146-
And in class scope:
147-
148-
>>> class C:
149-
... items = [(lambda i=i: i) for i in range(5)]
150-
... ret = [x() for x in items]
151-
>>> C.ret
152-
[0, 1, 2, 3, 4]
153-
154-
>>> class C:
155-
... items = [(lambda: i) for i in range(5)]
156-
... ret = [x() for x in items]
157-
>>> C.ret
158-
[4, 4, 4, 4, 4]
159-
160-
>>> class C:
161-
... items = [(lambda: i) for i in range(5)]
162-
... i = 20
163-
... ret = [x() for x in items]
164-
>>> C.ret
165-
[4, 4, 4, 4, 4]
166-
>>> C.i
167-
20
168-
169-
>>> class C:
170-
... items = [(lambda: y) for i in range(5)]
171-
... y = 2
172-
... ret = [x() for x in items]
173-
>>> C.ret
174-
[2, 2, 2, 2, 2]
175-
176-
Some more tests for scoping edge cases, each in func/module/class scope:
177-
178-
>>> def test_func():
179-
... y = 10
180-
... items = [(lambda: y) for y in range(5)]
181-
... x = y
182-
... y = 20
183-
... return x, [z() for z in items]
184-
>>> test_func()
185-
(10, [4, 4, 4, 4, 4])
186-
187-
>>> g = -1
188-
>>> def test_func():
189-
... def inner():
190-
... return g
191-
... [g for g in range(5)]
192-
... return inner
193-
>>> test_func()()
194-
-1
195-
196-
>>> def test_func():
197-
... x = -1
198-
... items = [(x:=y) for y in range(3)]
199-
... return x
200-
>>> test_func()
201-
2
202-
203-
>>> def test_func(lst):
204-
... ret = [lambda: x for x in lst]
205-
... inc = [x + 1 for x in lst]
206-
... [x for x in inc]
207-
... return ret
208-
>>> test_func(range(3))[0]()
209-
2
210-
211-
>>> def test_func(lst):
212-
... x = -1
213-
... funcs = [lambda: x for x in lst]
214-
... items = [x + 1 for x in lst]
215-
... return x
216-
>>> test_func(range(3))
217-
-1
218-
219-
>>> def test_func(x):
220-
... return [x for x in x]
221-
>>> test_func([1])
222-
[1]
223-
224-
>>> def test_func():
225-
... x = 1
226-
... def g():
227-
... [x for x in range(3)]
228-
... return x
229-
... g()
230-
... return x
231-
>>> test_func()
232-
1
233-
23491
"""
23592

23693

23794
class ListComprehensionTest(unittest.TestCase):
95+
def _check_in_scopes(self, code, outputs, ns=None, scopes=None):
96+
code = textwrap.dedent(code)
97+
scopes = scopes or ["module", "class", "function"]
98+
for scope in scopes:
99+
with self.subTest(scope=scope):
100+
if scope == "class":
101+
newcode = textwrap.dedent("""
102+
class C:
103+
{code}
104+
""").format(code=textwrap.indent(code, " "))
105+
def get_output(moddict, name):
106+
return getattr(moddict["C"], name)
107+
elif scope == "function":
108+
newcode = textwrap.dedent("""
109+
def f():
110+
{code}
111+
return locals()
112+
""").format(code=textwrap.indent(code, " "))
113+
def get_output(moddict, name):
114+
return moddict["f"]()[name]
115+
else:
116+
newcode = code
117+
def get_output(moddict, name):
118+
return moddict[name]
119+
ns = ns or {}
120+
exec(newcode, ns)
121+
for k, v in outputs.items():
122+
self.assertEqual(get_output(ns, k), v)
123+
124+
def test_lambdas_with_iteration_var_as_default(self):
125+
code = """
126+
items = [(lambda i=i: i) for i in range(5)]
127+
y = [x() for x in items]
128+
"""
129+
outputs = {"y": [0, 1, 2, 3, 4]}
130+
self._check_in_scopes(code, outputs)
131+
132+
def test_lambdas_with_free_var(self):
133+
code = """
134+
items = [(lambda: i) for i in range(5)]
135+
y = [x() for x in items]
136+
"""
137+
outputs = {"y": [4, 4, 4, 4, 4]}
138+
self._check_in_scopes(code, outputs)
139+
140+
def test_inner_cell_shadows_outer(self):
141+
code = """
142+
items = [(lambda: i) for i in range(5)]
143+
i = 20
144+
y = [x() for x in items]
145+
"""
146+
outputs = {"y": [4, 4, 4, 4, 4]}
147+
self._check_in_scopes(code, outputs)
148+
149+
def test_closure_can_jump_over_comp_scope(self):
150+
code = """
151+
items = [(lambda: y) for i in range(5)]
152+
y = 2
153+
z = [x() for x in items]
154+
"""
155+
outputs = {"z": [2, 2, 2, 2, 2]}
156+
self._check_in_scopes(code, outputs)
157+
158+
def test_inner_cell_shadows_outer_redefined(self):
159+
code = """
160+
y = 10
161+
items = [(lambda: y) for y in range(5)]
162+
x = y
163+
y = 20
164+
out = [z() for z in items]
165+
"""
166+
outputs = {"x": 10, "out": [4, 4, 4, 4, 4]}
167+
self._check_in_scopes(code, outputs)
168+
169+
def test_shadows_outer_cell(self):
170+
code = """
171+
def inner():
172+
return g
173+
[g for g in range(5)]
174+
x = inner()
175+
"""
176+
outputs = {"x": -1}
177+
self._check_in_scopes(code, outputs, ns={"g": -1})
178+
179+
def test_assignment_expression(self):
180+
code = """
181+
x = -1
182+
items = [(x:=y) for y in range(3)]
183+
"""
184+
outputs = {"x": 2}
185+
# assignment expression in comprehension is disallowed in class scope
186+
self._check_in_scopes(code, outputs, scopes=["module", "function"])
187+
188+
def test_free_var_in_comp_child(self):
189+
code = """
190+
lst = range(3)
191+
funcs = [lambda: x for x in lst]
192+
inc = [x + 1 for x in lst]
193+
[x for x in inc]
194+
x = funcs[0]()
195+
"""
196+
outputs = {"x": 2}
197+
self._check_in_scopes(code, outputs)
198+
199+
def test_shadow_with_free_and_local(self):
200+
code = """
201+
lst = range(3)
202+
x = -1
203+
funcs = [lambda: x for x in lst]
204+
items = [x + 1 for x in lst]
205+
"""
206+
outputs = {"x": -1}
207+
self._check_in_scopes(code, outputs)
208+
209+
def test_shadow_comp_iterable_name(self):
210+
code = """
211+
x = [1]
212+
y = [x for x in x]
213+
"""
214+
outputs = {"x": [1]}
215+
self._check_in_scopes(code, outputs)
216+
217+
def test_nested_free(self):
218+
code = """
219+
x = 1
220+
def g():
221+
[x for x in range(3)]
222+
return x
223+
g()
224+
"""
225+
outputs = {"x": 1}
226+
self._check_in_scopes(code, outputs)
227+
238228
def test_unbound_local_after_comprehension(self):
239229
def f():
240230
if False:

0 commit comments

Comments
 (0)