Skip to content

Commit 07ca9af

Browse files
bpo-10544: Disallow "yield" in comprehensions and generator expressions. (GH-4564)
1 parent 8b5fa28 commit 07ca9af

File tree

6 files changed

+32
-49
lines changed

6 files changed

+32
-49
lines changed

Doc/reference/expressions.rst

+9-14
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,7 @@ they may depend on the values obtained from the leftmost iterable. For example:
196196

197197
To ensure the comprehension always results in a container of the appropriate
198198
type, ``yield`` and ``yield from`` expressions are prohibited in the implicitly
199-
nested scope (in Python 3.7, such expressions emit :exc:`DeprecationWarning`
200-
when compiled, in Python 3.8+ they will emit :exc:`SyntaxError`).
199+
nested scope.
201200

202201
Since Python 3.6, in an :keyword:`async def` function, an :keyword:`async for`
203202
clause may be used to iterate over a :term:`asynchronous iterator`.
@@ -214,8 +213,8 @@ See also :pep:`530`.
214213
.. versionadded:: 3.6
215214
Asynchronous comprehensions were introduced.
216215

217-
.. deprecated:: 3.7
218-
``yield`` and ``yield from`` deprecated in the implicitly nested scope.
216+
.. versionchanged:: 3.8
217+
``yield`` and ``yield from`` prohibited in the implicitly nested scope.
219218

220219

221220
.. _lists:
@@ -350,9 +349,7 @@ The parentheses can be omitted on calls with only one argument. See section
350349

351350
To avoid interfering with the expected operation of the generator expression
352351
itself, ``yield`` and ``yield from`` expressions are prohibited in the
353-
implicitly defined generator (in Python 3.7, such expressions emit
354-
:exc:`DeprecationWarning` when compiled, in Python 3.8+ they will emit
355-
:exc:`SyntaxError`).
352+
implicitly defined generator.
356353

357354
If a generator expression contains either :keyword:`async for`
358355
clauses or :keyword:`await` expressions it is called an
@@ -368,8 +365,8 @@ which is an asynchronous iterator (see :ref:`async-iterators`).
368365
only appear in :keyword:`async def` coroutines. Starting
369366
with 3.7, any function can use asynchronous generator expressions.
370367

371-
.. deprecated:: 3.7
372-
``yield`` and ``yield from`` deprecated in the implicitly nested scope.
368+
.. versionchanged:: 3.8
369+
``yield`` and ``yield from`` prohibited in the implicitly nested scope.
373370

374371

375372
.. _yieldexpr:
@@ -401,12 +398,10 @@ coroutine function to be an asynchronous generator. For example::
401398

402399
Due to their side effects on the containing scope, ``yield`` expressions
403400
are not permitted as part of the implicitly defined scopes used to
404-
implement comprehensions and generator expressions (in Python 3.7, such
405-
expressions emit :exc:`DeprecationWarning` when compiled, in Python 3.8+
406-
they will emit :exc:`SyntaxError`)..
401+
implement comprehensions and generator expressions.
407402

408-
.. deprecated:: 3.7
409-
Yield expressions deprecated in the implicitly nested scopes used to
403+
.. versionchanged:: 3.8
404+
Yield expressions prohibited in the implicitly nested scopes used to
410405
implement comprehensions and generator expressions.
411406

412407
Generator functions are described below, while asynchronous generator

Doc/whatsnew/3.8.rst

+9
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,15 @@ This section lists previously described changes and other bugfixes
113113
that may require changes to your code.
114114

115115

116+
Changes in Python behavior
117+
--------------------------
118+
119+
* Yield expressions (both ``yield`` and ``yield from`` clauses) are now disallowed
120+
in comprehensions and generator expressions (aside from the iterable expression
121+
in the leftmost :keyword:`for` clause).
122+
(Contributed by Serhiy Storchaka in :issue:`10544`.)
123+
124+
116125
Changes in the Python API
117126
-------------------------
118127

Lib/test/support/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1061,8 +1061,8 @@ def make_bad_fd():
10611061
file.close()
10621062
unlink(TESTFN)
10631063

1064-
defcheck_syntax_error(testcase, statement, *, lineno=None, offset=None):
1065-
withtestcase.assertRaises(SyntaxError) ascm:
1064+
defcheck_syntax_error(testcase, statement, errtext='', *, lineno=None, offset=None):
1065+
withtestcase.assertRaisesRegex(SyntaxError, errtext) ascm:
10661066
compile(statement, '<test string>', 'exec')
10671067
err=cm.exception
10681068
testcase.assertIsNotNone(err.lineno)

Lib/test/test_grammar.py

+3-9
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ def __getitem__(self, item):
251251

252252
classGrammarTests(unittest.TestCase):
253253

254+
check_syntax_error=check_syntax_error
255+
254256
# single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
255257
# XXX can't test in a script -- this rule is only used when interactive
256258

@@ -920,15 +922,7 @@ def test_yield_in_comprehensions(self):
920922
defg(): [xforxin [(yield1)]]
921923
defg(): [xforxin [(yieldfrom ())]]
922924

923-
defcheck(code, warntext):
924-
withself.assertWarnsRegex(DeprecationWarning, warntext):
925-
compile(code, '<test string>', 'exec')
926-
importwarnings
927-
withwarnings.catch_warnings():
928-
warnings.filterwarnings('error', category=DeprecationWarning)
929-
withself.assertRaisesRegex(SyntaxError, warntext):
930-
compile(code, '<test string>', 'exec')
931-
925+
check=self.check_syntax_error
932926
check("def g(): [(yield x) for x in ()]",
933927
"'yield' inside list comprehension")
934928
check("def g(): [x for x in () if not (yield x)]",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Yield expressions are now disallowed in comprehensions and generator
2+
expressions except the expression for the outermost iterable.

Python/symtable.c

+7-24
Original file line numberDiff line numberDiff line change
@@ -1754,35 +1754,18 @@ symtable_handle_comprehension(struct symtable *st, expr_ty e,
17541754
VISIT(st, expr, value);
17551755
VISIT(st, expr, elt);
17561756
if (st->st_cur->ste_generator) {
1757-
PyObject*msg=PyUnicode_FromString(
1757+
PyErr_SetString(PyExc_SyntaxError,
17581758
(e->kind==ListComp_kind) ? "'yield' inside list comprehension" :
17591759
(e->kind==SetComp_kind) ? "'yield' inside set comprehension" :
17601760
(e->kind==DictComp_kind) ? "'yield' inside dict comprehension" :
17611761
"'yield' inside generator expression");
1762-
if (msg==NULL) {
1763-
symtable_exit_block(st, (void*)e);
1764-
return0;
1765-
}
1766-
if (PyErr_WarnExplicitObject(PyExc_DeprecationWarning,
1767-
msg, st->st_filename, st->st_cur->ste_lineno,
1768-
NULL, NULL) ==-1)
1769-
{
1770-
if (PyErr_ExceptionMatches(PyExc_DeprecationWarning)) {
1771-
/* Replace the DeprecationWarning exception with a SyntaxError
1772-
to get a more accurate error report */
1773-
PyErr_Clear();
1774-
PyErr_SetObject(PyExc_SyntaxError, msg);
1775-
PyErr_SyntaxLocationObject(st->st_filename,
1776-
st->st_cur->ste_lineno,
1777-
st->st_cur->ste_col_offset);
1778-
}
1779-
Py_DECREF(msg);
1780-
symtable_exit_block(st, (void*)e);
1781-
return0;
1782-
}
1783-
Py_DECREF(msg);
1762+
PyErr_SyntaxLocationObject(st->st_filename,
1763+
st->st_cur->ste_lineno,
1764+
st->st_cur->ste_col_offset);
1765+
symtable_exit_block(st, (void*)e);
1766+
return0;
17841767
}
1785-
st->st_cur->ste_generator|= is_generator;
1768+
st->st_cur->ste_generator=is_generator;
17861769
returnsymtable_exit_block(st, (void*)e);
17871770
}
17881771

0 commit comments

Comments
 (0)
close