bpo-10544: Disallow "yield" in comprehensions and generator expressions. (GH-4564)

This commit is contained in:
Serhiy Storchaka 2018-02-04 10:53:48 +02:00 committed by GitHub
parent 8b5fa289fd
commit 07ca9afaa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 32 additions and 49 deletions

View File

@ -196,8 +196,7 @@ they may depend on the values obtained from the leftmost iterable. For example:
To ensure the comprehension always results in a container of the appropriate To ensure the comprehension always results in a container of the appropriate
type, ``yield`` and ``yield from`` expressions are prohibited in the implicitly type, ``yield`` and ``yield from`` expressions are prohibited in the implicitly
nested scope (in Python 3.7, such expressions emit :exc:`DeprecationWarning` nested scope.
when compiled, in Python 3.8+ they will emit :exc:`SyntaxError`).
Since Python 3.6, in an :keyword:`async def` function, an :keyword:`async for` Since Python 3.6, in an :keyword:`async def` function, an :keyword:`async for`
clause may be used to iterate over a :term:`asynchronous iterator`. clause may be used to iterate over a :term:`asynchronous iterator`.
@ -214,8 +213,8 @@ See also :pep:`530`.
.. versionadded:: 3.6 .. versionadded:: 3.6
Asynchronous comprehensions were introduced. Asynchronous comprehensions were introduced.
.. deprecated:: 3.7 .. versionchanged:: 3.8
``yield`` and ``yield from`` deprecated in the implicitly nested scope. ``yield`` and ``yield from`` prohibited in the implicitly nested scope.
.. _lists: .. _lists:
@ -350,9 +349,7 @@ The parentheses can be omitted on calls with only one argument. See section
To avoid interfering with the expected operation of the generator expression To avoid interfering with the expected operation of the generator expression
itself, ``yield`` and ``yield from`` expressions are prohibited in the itself, ``yield`` and ``yield from`` expressions are prohibited in the
implicitly defined generator (in Python 3.7, such expressions emit implicitly defined generator.
:exc:`DeprecationWarning` when compiled, in Python 3.8+ they will emit
:exc:`SyntaxError`).
If a generator expression contains either :keyword:`async for` If a generator expression contains either :keyword:`async for`
clauses or :keyword:`await` expressions it is called an clauses or :keyword:`await` expressions it is called an
@ -368,8 +365,8 @@ which is an asynchronous iterator (see :ref:`async-iterators`).
only appear in :keyword:`async def` coroutines. Starting only appear in :keyword:`async def` coroutines. Starting
with 3.7, any function can use asynchronous generator expressions. with 3.7, any function can use asynchronous generator expressions.
.. deprecated:: 3.7 .. versionchanged:: 3.8
``yield`` and ``yield from`` deprecated in the implicitly nested scope. ``yield`` and ``yield from`` prohibited in the implicitly nested scope.
.. _yieldexpr: .. _yieldexpr:
@ -401,12 +398,10 @@ coroutine function to be an asynchronous generator. For example::
Due to their side effects on the containing scope, ``yield`` expressions Due to their side effects on the containing scope, ``yield`` expressions
are not permitted as part of the implicitly defined scopes used to are not permitted as part of the implicitly defined scopes used to
implement comprehensions and generator expressions (in Python 3.7, such implement comprehensions and generator expressions.
expressions emit :exc:`DeprecationWarning` when compiled, in Python 3.8+
they will emit :exc:`SyntaxError`)..
.. deprecated:: 3.7 .. versionchanged:: 3.8
Yield expressions deprecated in the implicitly nested scopes used to Yield expressions prohibited in the implicitly nested scopes used to
implement comprehensions and generator expressions. implement comprehensions and generator expressions.
Generator functions are described below, while asynchronous generator Generator functions are described below, while asynchronous generator

View File

@ -113,6 +113,15 @@ This section lists previously described changes and other bugfixes
that may require changes to your code. that may require changes to your code.
Changes in Python behavior
--------------------------
* Yield expressions (both ``yield`` and ``yield from`` clauses) are now disallowed
in comprehensions and generator expressions (aside from the iterable expression
in the leftmost :keyword:`for` clause).
(Contributed by Serhiy Storchaka in :issue:`10544`.)
Changes in the Python API Changes in the Python API
------------------------- -------------------------

View File

@ -1061,8 +1061,8 @@ def make_bad_fd():
file.close() file.close()
unlink(TESTFN) unlink(TESTFN)
def check_syntax_error(testcase, statement, *, lineno=None, offset=None): def check_syntax_error(testcase, statement, errtext='', *, lineno=None, offset=None):
with testcase.assertRaises(SyntaxError) as cm: with testcase.assertRaisesRegex(SyntaxError, errtext) as cm:
compile(statement, '<test string>', 'exec') compile(statement, '<test string>', 'exec')
err = cm.exception err = cm.exception
testcase.assertIsNotNone(err.lineno) testcase.assertIsNotNone(err.lineno)

View File

@ -251,6 +251,8 @@ class CNS:
class GrammarTests(unittest.TestCase): class GrammarTests(unittest.TestCase):
check_syntax_error = check_syntax_error
# single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE # single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
# XXX can't test in a script -- this rule is only used when interactive # XXX can't test in a script -- this rule is only used when interactive
@ -920,15 +922,7 @@ class GrammarTests(unittest.TestCase):
def g(): [x for x in [(yield 1)]] def g(): [x for x in [(yield 1)]]
def g(): [x for x in [(yield from ())]] def g(): [x for x in [(yield from ())]]
def check(code, warntext): check = self.check_syntax_error
with self.assertWarnsRegex(DeprecationWarning, warntext):
compile(code, '<test string>', 'exec')
import warnings
with warnings.catch_warnings():
warnings.filterwarnings('error', category=DeprecationWarning)
with self.assertRaisesRegex(SyntaxError, warntext):
compile(code, '<test string>', 'exec')
check("def g(): [(yield x) for x in ()]", check("def g(): [(yield x) for x in ()]",
"'yield' inside list comprehension") "'yield' inside list comprehension")
check("def g(): [x for x in () if not (yield x)]", check("def g(): [x for x in () if not (yield x)]",

View File

@ -0,0 +1,2 @@
Yield expressions are now disallowed in comprehensions and generator
expressions except the expression for the outermost iterable.

View File

@ -1754,35 +1754,18 @@ symtable_handle_comprehension(struct symtable *st, expr_ty e,
VISIT(st, expr, value); VISIT(st, expr, value);
VISIT(st, expr, elt); VISIT(st, expr, elt);
if (st->st_cur->ste_generator) { if (st->st_cur->ste_generator) {
PyObject *msg = PyUnicode_FromString( PyErr_SetString(PyExc_SyntaxError,
(e->kind == ListComp_kind) ? "'yield' inside list comprehension" : (e->kind == ListComp_kind) ? "'yield' inside list comprehension" :
(e->kind == SetComp_kind) ? "'yield' inside set comprehension" : (e->kind == SetComp_kind) ? "'yield' inside set comprehension" :
(e->kind == DictComp_kind) ? "'yield' inside dict comprehension" : (e->kind == DictComp_kind) ? "'yield' inside dict comprehension" :
"'yield' inside generator expression"); "'yield' inside generator expression");
if (msg == NULL) { PyErr_SyntaxLocationObject(st->st_filename,
symtable_exit_block(st, (void *)e); st->st_cur->ste_lineno,
return 0; st->st_cur->ste_col_offset);
} symtable_exit_block(st, (void *)e);
if (PyErr_WarnExplicitObject(PyExc_DeprecationWarning, return 0;
msg, st->st_filename, st->st_cur->ste_lineno,
NULL, NULL) == -1)
{
if (PyErr_ExceptionMatches(PyExc_DeprecationWarning)) {
/* Replace the DeprecationWarning exception with a SyntaxError
to get a more accurate error report */
PyErr_Clear();
PyErr_SetObject(PyExc_SyntaxError, msg);
PyErr_SyntaxLocationObject(st->st_filename,
st->st_cur->ste_lineno,
st->st_cur->ste_col_offset);
}
Py_DECREF(msg);
symtable_exit_block(st, (void *)e);
return 0;
}
Py_DECREF(msg);
} }
st->st_cur->ste_generator |= is_generator; st->st_cur->ste_generator = is_generator;
return symtable_exit_block(st, (void *)e); return symtable_exit_block(st, (void *)e);
} }