[2.7] bpo-10544: Deprecate "yield" in comprehensions and generator expressions in Py3k mode. (GH-4579) (#4676)
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 0f0da72..cabf548 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -847,8 +847,8 @@
file.close()
unlink(TESTFN)
-def check_syntax_error(testcase, statement, lineno=None, offset=None):
- with testcase.assertRaises(SyntaxError) as cm:
+def check_syntax_error(testcase, statement, errtext='', lineno=None, offset=None):
+ with testcase.assertRaisesRegexp(SyntaxError, errtext) as cm:
compile(statement, '<test string>', 'exec')
err = cm.exception
if lineno is not None:
diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py
index 5e1a3e5..0f7bf19 100644
--- a/Lib/test/test_generators.py
+++ b/Lib/test/test_generators.py
@@ -1524,13 +1524,7 @@
[None]
-
-An obscene abuse of a yield expression within a generator expression:
-
->>> list((yield 21) for i in range(4))
-[21, None, 21, None, 21, None, 21, None]
-
-And a more sane, but still weird usage:
+Yield is allowed only in the outermost iterable in generator expression:
>>> def f(): list(i for i in [(yield 26)])
>>> type(f())
@@ -1571,7 +1565,7 @@
>>> def f(): return lambda x=(yield): 1
Traceback (most recent call last):
...
-SyntaxError: 'return' with argument inside generator (<doctest test.test_generators.__test__.coroutine[22]>, line 1)
+SyntaxError: 'return' with argument inside generator (<doctest test.test_generators.__test__.coroutine[21]>, line 1)
>>> def f(): x = yield = y
Traceback (most recent call last):
@@ -1784,7 +1778,7 @@
>>> type(f())
<type 'generator'>
->>> def f(): x=(i for i in (yield) if (yield))
+>>> def f(): x=(i for i in (yield) if i)
>>> type(f())
<type 'generator'>
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
index 5f77c1d..fc675c3 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -493,6 +493,46 @@
def testYield(self):
check_syntax_error(self, "class foo:yield 1")
+ def test_yield_in_comprehensions(self):
+ # Check yield in comprehensions
+ def g(): [x for x in [(yield 1)]]
+
+ def check(code, warntext):
+ with check_py3k_warnings((warntext, DeprecationWarning)):
+ compile(code, '<test string>', 'exec')
+ if sys.py3kwarning:
+ import warnings
+ with warnings.catch_warnings():
+ warnings.filterwarnings('error', category=DeprecationWarning)
+ with self.assertRaises(SyntaxError) as cm:
+ compile(code, '<test string>', 'exec')
+ self.assertIn(warntext, str(cm.exception))
+
+ check("def g(): [(yield x) for x in ()]",
+ "'yield' inside list comprehension")
+ check("def g(): [x for x in () if not (yield x)]",
+ "'yield' inside list comprehension")
+ check("def g(): [y for x in () for y in [(yield x)]]",
+ "'yield' inside list comprehension")
+ check("def g(): {(yield x) for x in ()}",
+ "'yield' inside set comprehension")
+ check("def g(): {(yield x): x for x in ()}",
+ "'yield' inside dict comprehension")
+ check("def g(): {x: (yield x) for x in ()}",
+ "'yield' inside dict comprehension")
+ check("def g(): ((yield x) for x in ())",
+ "'yield' inside generator expression")
+ with check_py3k_warnings(("'yield' inside list comprehension",
+ DeprecationWarning)):
+ check_syntax_error(self, "class C: [(yield x) for x in ()]")
+ check("class C: ((yield x) for x in ())",
+ "'yield' inside generator expression")
+ with check_py3k_warnings(("'yield' inside list comprehension",
+ DeprecationWarning)):
+ check_syntax_error(self, "[(yield x) for x in ()]")
+ check("((yield x) for x in ())",
+ "'yield' inside generator expression")
+
def testRaise(self):
# 'raise' test [',' test]
try: raise RuntimeError, 'just testing'