bpo-10544: Disallow "yield" in comprehensions and generator expressions. (GH-4564)
This commit is contained in:
parent
8b5fa289fd
commit
07ca9afaa8
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)]",
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Yield expressions are now disallowed in comprehensions and generator
|
||||||
|
expressions except the expression for the outermost iterable.
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue