Skip to content

Commit 65d1887

Browse files
[2.7] bpo-10544: Deprecate "yield" in comprehensions and generator expressions in Py3k mode. (GH-4579) (#4676)
1 parent be6b74c commit 65d1887

File tree

5 files changed

+120
-28
lines changed

5 files changed

+120
-28
lines changed

Lib/test/support/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -847,8 +847,8 @@ def make_bad_fd():
847847
file.close()
848848
unlink(TESTFN)
849849

850-
def check_syntax_error(testcase, statement, lineno=None, offset=None):
851-
with testcase.assertRaises(SyntaxError) as cm:
850+
def check_syntax_error(testcase, statement, errtext='', lineno=None, offset=None):
851+
with testcase.assertRaisesRegexp(SyntaxError, errtext) as cm:
852852
compile(statement, '<test string>', 'exec')
853853
err = cm.exception
854854
if lineno is not None:

Lib/test/test_generators.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,13 +1524,7 @@ def printsolution(self, x):
15241524
[None]
15251525
15261526
1527-
1528-
An obscene abuse of a yield expression within a generator expression:
1529-
1530-
>>> list((yield 21) for i in range(4))
1531-
[21, None, 21, None, 21, None, 21, None]
1532-
1533-
And a more sane, but still weird usage:
1527+
Yield is allowed only in the outermost iterable in generator expression:
15341528
15351529
>>> def f(): list(i for i in [(yield 26)])
15361530
>>> type(f())
@@ -1571,7 +1565,7 @@ def printsolution(self, x):
15711565
>>> def f(): return lambda x=(yield): 1
15721566
Traceback (most recent call last):
15731567
...
1574-
SyntaxError: 'return' with argument inside generator (<doctest test.test_generators.__test__.coroutine[22]>, line 1)
1568+
SyntaxError: 'return' with argument inside generator (<doctest test.test_generators.__test__.coroutine[21]>, line 1)
15751569
15761570
>>> def f(): x = yield = y
15771571
Traceback (most recent call last):
@@ -1784,7 +1778,7 @@ def printsolution(self, x):
17841778
>>> type(f())
17851779
<type 'generator'>
17861780
1787-
>>> def f(): x=(i for i in (yield) if (yield))
1781+
>>> def f(): x=(i for i in (yield) if i)
17881782
>>> type(f())
17891783
<type 'generator'>
17901784

Lib/test/test_grammar.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,46 @@ def g2(): return 1
493493
def testYield(self):
494494
check_syntax_error(self, "class foo:yield 1")
495495

496+
def test_yield_in_comprehensions(self):
497+
# Check yield in comprehensions
498+
def g(): [x for x in [(yield 1)]]
499+
500+
def check(code, warntext):
501+
with check_py3k_warnings((warntext, DeprecationWarning)):
502+
compile(code, '<test string>', 'exec')
503+
if sys.py3kwarning:
504+
import warnings
505+
with warnings.catch_warnings():
506+
warnings.filterwarnings('error', category=DeprecationWarning)
507+
with self.assertRaises(SyntaxError) as cm:
508+
compile(code, '<test string>', 'exec')
509+
self.assertIn(warntext, str(cm.exception))
510+
511+
check("def g(): [(yield x) for x in ()]",
512+
"'yield' inside list comprehension")
513+
check("def g(): [x for x in () if not (yield x)]",
514+
"'yield' inside list comprehension")
515+
check("def g(): [y for x in () for y in [(yield x)]]",
516+
"'yield' inside list comprehension")
517+
check("def g(): {(yield x) for x in ()}",
518+
"'yield' inside set comprehension")
519+
check("def g(): {(yield x): x for x in ()}",
520+
"'yield' inside dict comprehension")
521+
check("def g(): {x: (yield x) for x in ()}",
522+
"'yield' inside dict comprehension")
523+
check("def g(): ((yield x) for x in ())",
524+
"'yield' inside generator expression")
525+
with check_py3k_warnings(("'yield' inside list comprehension",
526+
DeprecationWarning)):
527+
check_syntax_error(self, "class C: [(yield x) for x in ()]")
528+
check("class C: ((yield x) for x in ())",
529+
"'yield' inside generator expression")
530+
with check_py3k_warnings(("'yield' inside list comprehension",
531+
DeprecationWarning)):
532+
check_syntax_error(self, "[(yield x) for x in ()]")
533+
check("((yield x) for x in ())",
534+
"'yield' inside generator expression")
535+
496536
def testRaise(self):
497537
# 'raise' test [',' test]
498538
try: raise RuntimeError, 'just testing'
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Yield expressions are now deprecated in comprehensions and generator
2+
expressions when checking Python 3 compatibility. They are still
3+
permitted in the definition of the outermost iterable, as that is
4+
evaluated directly in the enclosing scope.

Python/symtable.c

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,14 @@ PyTypeObject PySTEntry_Type = {
162162
};
163163

164164
static int symtable_analyze(struct symtable *st);
165-
static int symtable_warn(struct symtable *st, char *msg, int lineno);
165+
static int symtable_warn(struct symtable *st,
166+
PyObject *warn, const char *msg, int lineno);
166167
static int symtable_enter_block(struct symtable *st, identifier name,
167168
_Py_block_ty block, void *ast, int lineno);
168169
static int symtable_exit_block(struct symtable *st, void *ast);
169170
static int symtable_visit_stmt(struct symtable *st, stmt_ty s);
170171
static int symtable_visit_expr(struct symtable *st, expr_ty s);
172+
static int symtable_visit_listcomp(struct symtable *st, expr_ty e);
171173
static int symtable_visit_genexp(struct symtable *st, expr_ty s);
172174
static int symtable_visit_setcomp(struct symtable *st, expr_ty e);
173175
static int symtable_visit_dictcomp(struct symtable *st, expr_ty e);
@@ -796,14 +798,18 @@ symtable_analyze(struct symtable *st)
796798

797799

798800
static int
799-
symtable_warn(struct symtable *st, char *msg, int lineno)
801+
symtable_warn(struct symtable *st, PyObject *warn, const char *msg, int lineno)
800802
{
801-
if (PyErr_WarnExplicit(PyExc_SyntaxWarning, msg, st->st_filename,
802-
lineno, NULL, NULL) < 0) {
803-
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
803+
if (lineno < 0) {
804+
lineno = st->st_cur->ste_lineno;
805+
}
806+
if (PyErr_WarnExplicit(warn, msg, st->st_filename, lineno, NULL, NULL) < 0) {
807+
if (PyErr_ExceptionMatches(warn)) {
808+
/* Replace the warning exception with a SyntaxError
809+
to get a more accurate error report */
810+
PyErr_Clear();
804811
PyErr_SetString(PyExc_SyntaxError, msg);
805-
PyErr_SyntaxLocation(st->st_filename,
806-
st->st_cur->ste_lineno);
812+
PyErr_SyntaxLocation(st->st_filename, lineno);
807813
}
808814
return 0;
809815
}
@@ -1153,7 +1159,7 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
11531159
PyOS_snprintf(buf, sizeof(buf),
11541160
GLOBAL_AFTER_USE,
11551161
c_name);
1156-
if (!symtable_warn(st, buf, s->lineno))
1162+
if (!symtable_warn(st, PyExc_SyntaxWarning, buf, s->lineno))
11571163
return 0;
11581164
}
11591165
if (!symtable_add_def(st, name, DEF_GLOBAL))
@@ -1221,8 +1227,8 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
12211227
VISIT_SEQ(st, expr, e->v.Set.elts);
12221228
break;
12231229
case ListComp_kind:
1224-
VISIT(st, expr, e->v.ListComp.elt);
1225-
VISIT_SEQ(st, comprehension, e->v.ListComp.generators);
1230+
if (!symtable_visit_listcomp(st, e))
1231+
return 0;
12261232
break;
12271233
case GeneratorExp_kind:
12281234
if (!symtable_visit_genexp(st, e))
@@ -1420,12 +1426,11 @@ symtable_visit_alias(struct symtable *st, alias_ty a)
14201426
return r;
14211427
}
14221428
else {
1423-
if (st->st_cur->ste_type != ModuleBlock) {
1424-
int lineno = st->st_cur->ste_lineno;
1425-
if (!symtable_warn(st, IMPORT_STAR_WARNING, lineno)) {
1426-
Py_DECREF(store_name);
1427-
return 0;
1428-
}
1429+
if (st->st_cur->ste_type != ModuleBlock &&
1430+
!symtable_warn(st, PyExc_SyntaxWarning, IMPORT_STAR_WARNING, -1))
1431+
{
1432+
Py_DECREF(store_name);
1433+
return 0;
14291434
}
14301435
st->st_cur->ste_unoptimized |= OPT_IMPORT_STAR;
14311436
Py_DECREF(store_name);
@@ -1509,7 +1514,10 @@ symtable_handle_comprehension(struct symtable *st, expr_ty e,
15091514
!symtable_enter_block(st, scope_name, FunctionBlock, (void *)e, 0)) {
15101515
return 0;
15111516
}
1512-
st->st_cur->ste_generator = is_generator;
1517+
/* In order to check for yield expressions under '-3', we clear
1518+
the generator flag, and restore it at the end */
1519+
is_generator |= st->st_cur->ste_generator;
1520+
st->st_cur->ste_generator = 0;
15131521
/* Outermost iter is received as an argument */
15141522
if (!symtable_implicit_arg(st, 0)) {
15151523
symtable_exit_block(st, (void *)e);
@@ -1527,9 +1535,55 @@ symtable_handle_comprehension(struct symtable *st, expr_ty e,
15271535
if (value)
15281536
VISIT_IN_BLOCK(st, expr, value, (void*)e);
15291537
VISIT_IN_BLOCK(st, expr, elt, (void*)e);
1538+
if (Py_Py3kWarningFlag && st->st_cur->ste_generator) {
1539+
const char *msg = (
1540+
(e->kind == SetComp_kind) ? "'yield' inside set comprehension" :
1541+
(e->kind == DictComp_kind) ? "'yield' inside dict comprehension" :
1542+
"'yield' inside generator expression");
1543+
if (!symtable_warn(st, PyExc_DeprecationWarning, msg, -1)) {
1544+
symtable_exit_block(st, (void *)e);
1545+
return 0;
1546+
}
1547+
}
1548+
st->st_cur->ste_generator |= is_generator;
15301549
return symtable_exit_block(st, (void *)e);
15311550
}
15321551

1552+
static int
1553+
symtable_visit_listcomp(struct symtable *st, expr_ty e)
1554+
{
1555+
asdl_seq *generators = e->v.ListComp.generators;
1556+
int i, is_generator;
1557+
/* In order to check for yield expressions under '-3', we clear
1558+
the generator flag, and restore it at the end */
1559+
is_generator = st->st_cur->ste_generator;
1560+
st->st_cur->ste_generator = 0;
1561+
VISIT(st, expr, e->v.ListComp.elt);
1562+
for (i = 0; i < asdl_seq_LEN(generators); i++) {
1563+
comprehension_ty lc = (comprehension_ty)asdl_seq_GET(generators, i);
1564+
VISIT(st, expr, lc->target);
1565+
if (i == 0 && !st->st_cur->ste_generator) {
1566+
/* 'yield' in the outermost iterator doesn't cause a warning */
1567+
VISIT(st, expr, lc->iter);
1568+
is_generator |= st->st_cur->ste_generator;
1569+
st->st_cur->ste_generator = 0;
1570+
}
1571+
else {
1572+
VISIT(st, expr, lc->iter);
1573+
}
1574+
VISIT_SEQ(st, expr, lc->ifs);
1575+
}
1576+
1577+
if (Py_Py3kWarningFlag && st->st_cur->ste_generator) {
1578+
const char *msg = "'yield' inside list comprehension";
1579+
if (!symtable_warn(st, PyExc_DeprecationWarning, msg, -1)) {
1580+
return 0;
1581+
}
1582+
}
1583+
st->st_cur->ste_generator |= is_generator;
1584+
return 1;
1585+
}
1586+
15331587
static int
15341588
symtable_visit_genexp(struct symtable *st, expr_ty e)
15351589
{

0 commit comments

Comments
 (0)