Skip to content

Commit ca6cd26

Browse files
bpo-10544: Disallow "yield" in comprehensions and generator expressions.
1 parent cef88b9 commit ca6cd26

File tree

6 files changed

+46
-6
lines changed

6 files changed

+46
-6
lines changed

Doc/reference/expressions.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ each time the innermost block is reached.
186186
Note that the comprehension is executed in a separate scope, so names assigned
187187
to in the target list don't "leak" into the enclosing scope.
188188

189+
Yield expressions are not allowed in comprehensions
190+
except the expression for the outermost iterable.
191+
189192
Since Python 3.6, in an :keyword:`async def` function, an :keyword:`async for`
190193
clause may be used to iterate over a :term:`asynchronous iterator`.
191194
A comprehension in an :keyword:`async def` function may consist of either a
@@ -326,6 +329,9 @@ range(10) for y in bar(x))``.
326329
The parentheses can be omitted on calls with only one argument. See section
327330
:ref:`calls` for details.
328331

332+
Yield expressions are not allowed in generator expressions
333+
except the expression for the outermost iterable.
334+
329335
If a generator expression contains either :keyword:`async for`
330336
clauses or :keyword:`await` expressions it is called an
331337
:dfn:`asynchronous generator expression`. An asynchronous generator
@@ -364,6 +370,9 @@ coroutine function to be an asynchronous generator. For example::
364370
async def agen(): # defines an asynchronous generator function (PEP 525)
365371
yield 123
366372

373+
Yield expressions are not allowed in comprehensions and generator expressions
374+
except the expression for the outermost iterable.
375+
367376
Generator functions are described below, while asynchronous generator
368377
functions are described separately in section
369378
:ref:`asynchronous-generator-functions`.

Doc/whatsnew/3.7.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,12 @@ Changes in Python behavior
673673
parentheses can be omitted only on calls.
674674
(Contributed by Serhiy Storchaka in :issue:`32012` and :issue:`32023`.)
675675

676+
* Yield expressions now are not allowed in comprehensions and generator
677+
expressions except the expression for the outermost iterable since
678+
comprehensions and generator expressions are implemented using
679+
an internal function.
680+
(Contributed by Serhiy Storchaka in :issue:`10544`.)
681+
676682

677683
Changes in the Python API
678684
-------------------------

Lib/test/test_generators.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,7 +1834,9 @@ def printsolution(self, x):
18341834
An obscene abuse of a yield expression within a generator expression:
18351835
18361836
>>> list((yield 21) for i in range(4))
1837-
[21, None, 21, None, 21, None, 21, None]
1837+
Traceback (most recent call last):
1838+
...
1839+
SyntaxError: 'yield' inside generator expression
18381840
18391841
And a more sane, but still weird usage:
18401842
@@ -2106,10 +2108,6 @@ def printsolution(self, x):
21062108
>>> type(f())
21072109
<class 'generator'>
21082110
2109-
>>> def f(): x=(i for i in (yield) if (yield))
2110-
>>> type(f())
2111-
<class 'generator'>
2112-
21132111
>>> def f(d): d[(yield "a")] = d[(yield "b")] = 27
21142112
>>> data = [1,2]
21152113
>>> g = f(data)

Lib/test/test_grammar.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,21 @@ def g(): f((yield from ()), 1)
841841
# Check annotation refleak on SyntaxError
842842
check_syntax_error(self, "def g(a:(yield)): pass")
843843

844+
def test_yield_in_comprehensions(self):
845+
# Check yield in comprehensions
846+
def g(): [x for x in [(yield 1)]]
847+
def g(): [x for x in [(yield from ())]]
848+
check_syntax_error(self, "def g(): [(yield x) for x in ()]")
849+
check_syntax_error(self, "def g(): [x for x in () if not (yield x)]")
850+
check_syntax_error(self, "def g(): [y for x in () for y in [(yield x)]]")
851+
check_syntax_error(self, "def g(): {(yield x) for x in ()}")
852+
check_syntax_error(self, "def g(): {(yield x): x for x in ()}")
853+
check_syntax_error(self, "def g(): {x: (yield x) for x in ()}")
854+
check_syntax_error(self, "def g(): ((yield x) for x in ())")
855+
check_syntax_error(self, "def g(): [(yield from x) for x in ()]")
856+
check_syntax_error(self, "class C: [(yield x) for x in ()]")
857+
check_syntax_error(self, "[(yield x) for x in ()]")
858+
844859
def test_raise(self):
845860
# 'raise' test [',' test]
846861
try: raise RuntimeError('just testing')
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Yield expressions now are disallowed in comprehensions and generator
2+
expressions except the expression for the outermost iterable.

Python/symtable.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1734,7 +1734,6 @@ symtable_handle_comprehension(struct symtable *st, expr_ty e,
17341734
e->lineno, e->col_offset)) {
17351735
return 0;
17361736
}
1737-
st->st_cur->ste_generator = is_generator;
17381737
if (outermost->is_async) {
17391738
st->st_cur->ste_coroutine = 1;
17401739
}
@@ -1754,6 +1753,17 @@ symtable_handle_comprehension(struct symtable *st, expr_ty e,
17541753
if (value)
17551754
VISIT(st, expr, value);
17561755
VISIT(st, expr, elt);
1756+
if (st->st_cur->ste_generator) {
1757+
PyErr_SetString(PyExc_SyntaxError,
1758+
(e->kind == ListComp_kind) ? "'yield' inside list comprehension" :
1759+
(e->kind == SetComp_kind) ? "'yield' inside set comprehension" :
1760+
(e->kind == DictComp_kind) ? "'yield' inside dict comprehension" :
1761+
"'yield' inside generator expression");
1762+
PyErr_SyntaxLocationObject(st->st_filename,
1763+
st->st_cur->ste_lineno,
1764+
st->st_cur->ste_col_offset);
1765+
}
1766+
st->st_cur->ste_generator = is_generator;
17571767
return symtable_exit_block(st, (void *)e);
17581768
}
17591769

0 commit comments

Comments
 (0)