Skip to content

Commit 619d9bd

Browse files
author
Alexandre Bouayad
authored
Fix crash with yield in comprehension (#12048)
Fixes #10189 Produce an error when encountering a yield expression (both `yield` and `yield from` clauses) in comprehensions and generator expressions. The latter is a syntax error in python 3.8+; see #10189 and also [python issue 10544](https://bugs.python.org/issue10544).
1 parent 6d24332 commit 619d9bd

File tree

3 files changed

+60
-19
lines changed

3 files changed

+60
-19
lines changed

mypy/semanal.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3830,13 +3830,15 @@ def visit_star_expr(self, expr: StarExpr) -> None:
38303830
expr.expr.accept(self)
38313831

38323832
def visit_yield_from_expr(self, e: YieldFromExpr) -> None:
3833-
if not self.is_func_scope(): # not sure
3833+
if not self.is_func_scope():
38343834
self.fail('"yield from" outside function', e, serious=True, blocker=True)
3835+
elif self.is_comprehension_stack[-1]:
3836+
self.fail('"yield from" inside comprehension or generator expression',
3837+
e, serious=True, blocker=True)
3838+
elif self.function_stack[-1].is_coroutine:
3839+
self.fail('"yield from" in async function', e, serious=True, blocker=True)
38353840
else:
3836-
if self.function_stack[-1].is_coroutine:
3837-
self.fail('"yield from" in async function', e, serious=True, blocker=True)
3838-
else:
3839-
self.function_stack[-1].is_generator = True
3841+
self.function_stack[-1].is_generator = True
38403842
if e.expr:
38413843
e.expr.accept(self)
38423844

@@ -4214,20 +4216,22 @@ def visit__promote_expr(self, expr: PromoteExpr) -> None:
42144216
if analyzed is not None:
42154217
expr.type = analyzed
42164218

4217-
def visit_yield_expr(self, expr: YieldExpr) -> None:
4219+
def visit_yield_expr(self, e: YieldExpr) -> None:
42184220
if not self.is_func_scope():
4219-
self.fail('"yield" outside function', expr, serious=True, blocker=True)
4220-
else:
4221-
if self.function_stack[-1].is_coroutine:
4222-
if self.options.python_version < (3, 6):
4223-
self.fail('"yield" in async function', expr, serious=True, blocker=True)
4224-
else:
4225-
self.function_stack[-1].is_generator = True
4226-
self.function_stack[-1].is_async_generator = True
4221+
self.fail('"yield" outside function', e, serious=True, blocker=True)
4222+
elif self.is_comprehension_stack[-1]:
4223+
self.fail('"yield" inside comprehension or generator expression',
4224+
e, serious=True, blocker=True)
4225+
elif self.function_stack[-1].is_coroutine:
4226+
if self.options.python_version < (3, 6):
4227+
self.fail('"yield" in async function', e, serious=True, blocker=True)
42274228
else:
42284229
self.function_stack[-1].is_generator = True
4229-
if expr.expr:
4230-
expr.expr.accept(self)
4230+
self.function_stack[-1].is_async_generator = True
4231+
else:
4232+
self.function_stack[-1].is_generator = True
4233+
if e.expr:
4234+
e.expr.accept(self)
42314235

42324236
def visit_await_expr(self, expr: AwaitExpr) -> None:
42334237
if not self.is_func_scope():

mypy/semanal_main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ def semantic_analyze_target(target: str,
307307
Return tuple with these items:
308308
- list of deferred targets
309309
- was some definition incomplete (need to run another pass)
310-
- were any new names were defined (or placeholders replaced)
310+
- were any new names defined (or placeholders replaced)
311311
"""
312312
state.manager.processed_targets.append(target)
313313
tree = state.tree

test-data/unit/check-semanal-error.test

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,47 @@ continue # E: "continue" outside loop
7777

7878
[case testYieldOutsideFunction]
7979
yield # E: "yield" outside function
80-
81-
[case testYieldFromOutsideFunction]
8280
x = 1
8381
yield from x # E: "yield from" outside function
82+
[(yield 1) for _ in x] # E: "yield" inside comprehension or generator expression
83+
{(yield 1) for _ in x} # E: "yield" inside comprehension or generator expression
84+
{i: (yield 1) for i in x} # E: "yield" inside comprehension or generator expression
85+
((yield 1) for _ in x) # E: "yield" inside comprehension or generator expression
86+
y = 1
87+
[(yield from x) for _ in y] # E: "yield from" inside comprehension or generator expression
88+
{(yield from x) for _ in y} # E: "yield from" inside comprehension or generator expression
89+
{i: (yield from x) for i in y} # E: "yield from" inside comprehension or generator expression
90+
((yield from x) for _ in y) # E: "yield from" inside comprehension or generator expression
91+
def f(y):
92+
[x for x in (yield y)]
93+
{x for x in (yield y)}
94+
{x: x for x in (yield y)}
95+
(x for x in (yield y))
96+
[x for x in (yield from y)]
97+
{x for x in (yield from y)}
98+
{x: x for x in (yield from y)}
99+
(x for x in (yield from y))
100+
def g(y):
101+
[(yield 1) for _ in y] # E: "yield" inside comprehension or generator expression
102+
{(yield 1) for _ in y} # E: "yield" inside comprehension or generator expression
103+
{i: (yield 1) for i in y} # E: "yield" inside comprehension or generator expression
104+
((yield 1) for _ in y) # E: "yield" inside comprehension or generator expression
105+
lst = 1
106+
[(yield from lst) for _ in y] # E: "yield from" inside comprehension or generator expression
107+
{(yield from lst) for _ in y} # E: "yield from" inside comprehension or generator expression
108+
{i: (yield from lst) for i in y} # E: "yield from" inside comprehension or generator expression
109+
((yield from lst) for _ in y) # E: "yield from" inside comprehension or generator expression
110+
def h(y):
111+
lst = 1
112+
[x for x in lst if (yield y)] # E: "yield" inside comprehension or generator expression
113+
{x for x in lst if (yield y)} # E: "yield" inside comprehension or generator expression
114+
{x: x for x in lst if (yield y)} # E: "yield" inside comprehension or generator expression
115+
(x for x in lst if (yield y)) # E: "yield" inside comprehension or generator expression
116+
lst = 1
117+
[x for x in lst if (yield from y)] # E: "yield from" inside comprehension or generator expression
118+
{x for x in lst if (yield from y)} # E: "yield from" inside comprehension or generator expression
119+
{x: x for x in lst if (yield from y)} # E: "yield from" inside comprehension or generator expression
120+
(x for x in lst if (yield from y)) # E: "yield from" inside comprehension or generator expression
84121

85122
[case testImportFuncDup]
86123

0 commit comments

Comments
 (0)