# HG changeset patch # User Nick Coghlan # Date 1310224188 -36000 # Node ID 79088efb6c91fcd18825ee81f088f9c2167c5019 # Parent 3a4b983dd70b95a50ba25fc0317ac6c79481e9bc PEP 380: support yielding from subgenerators (patch by Greg Ewing and Renauld Blanch) diff -r 3a4b983dd70b -r 79088efb6c91 Doc/whatsnew/3.3.rst --- a/Doc/whatsnew/3.3.rst Sat Jul 09 11:48:50 2011 +0200 +++ b/Doc/whatsnew/3.3.rst Sun Jul 10 01:09:48 2011 +1000 @@ -55,6 +55,21 @@ ============= +PEP 380: Syntax for Delegating to a Subgenerator +================================================ + +PEP 380 adds the ``yield from`` expression, allowing a generator to delegate +part of its operations to another generator. This allows a section of code +containing 'yield' to be factored out and placed in another generator. +Additionally, the subgenerator is allowed to return with a value, and the +value is made available to the delegating generator. + +While designed primarily for use in delegating to a subgenerator, the ``yield +from`` expression actually allows delegation to arbitrary subiterators. + +(Implementation by Greg Ewing and Renauld Blanch) + + Other Language Changes ====================== diff -r 3a4b983dd70b -r 79088efb6c91 Grammar/Grammar --- a/Grammar/Grammar Sat Jul 09 11:48:50 2011 +0200 +++ b/Grammar/Grammar Sun Jul 10 01:09:48 2011 +1000 @@ -119,7 +119,7 @@ |'**' test) # The reason that keywords are test nodes instead of NAME is that using NAME # results in an ambiguity. ast.c makes sure it's a NAME. -argument: test [comp_for] | test '=' test # Really [keyword '='] test +argument: (test | yield_from) [comp_for] | test '=' (test | yield_from) # Really [keyword '='] test comp_iter: comp_for | comp_if comp_for: 'for' exprlist 'in' or_test [comp_iter] comp_if: 'if' test_nocond [comp_iter] @@ -127,4 +127,6 @@ # not used in grammar, but may appear in "node" passed from Parser to Compiler encoding_decl: NAME -yield_expr: 'yield' [testlist] +yield_expr: 'yield' [yield_arg] +yield_arg: 'from' test | testlist +yield_from: 'yield' 'from' test diff -r 3a4b983dd70b -r 79088efb6c91 Include/Python-ast.h --- a/Include/Python-ast.h Sat Jul 09 11:48:50 2011 +0200 +++ b/Include/Python-ast.h Sun Jul 10 01:09:48 2011 +1000 @@ -245,6 +245,7 @@ } GeneratorExp; struct { + int is_from; expr_ty value; } Yield; @@ -487,8 +488,9 @@ #define GeneratorExp(a0, a1, a2, a3, a4) _Py_GeneratorExp(a0, a1, a2, a3, a4) expr_ty _Py_GeneratorExp(expr_ty elt, asdl_seq * generators, int lineno, int col_offset, PyArena *arena); -#define Yield(a0, a1, a2, a3) _Py_Yield(a0, a1, a2, a3) -expr_ty _Py_Yield(expr_ty value, int lineno, int col_offset, PyArena *arena); +#define Yield(a0, a1, a2, a3, a4) _Py_Yield(a0, a1, a2, a3, a4) +expr_ty _Py_Yield(int is_from, expr_ty value, int lineno, int col_offset, + PyArena *arena); #define Compare(a0, a1, a2, a3, a4, a5) _Py_Compare(a0, a1, a2, a3, a4, a5) expr_ty _Py_Compare(expr_ty left, asdl_int_seq * ops, asdl_seq * comparators, int lineno, int col_offset, PyArena *arena); diff -r 3a4b983dd70b -r 79088efb6c91 Include/frameobject.h --- a/Include/frameobject.h Sat Jul 09 11:48:50 2011 +0200 +++ b/Include/frameobject.h Sun Jul 10 01:09:48 2011 +1000 @@ -27,6 +27,7 @@ to the current stack top. */ PyObject **f_stacktop; PyObject *f_trace; /* Trace function */ + PyObject *f_yieldfrom; /* Iterator being delegated to by yield from */ /* In a generator, we need to be able to swap between the exception state inside the generator and the exception state of the calling diff -r 3a4b983dd70b -r 79088efb6c91 Include/genobject.h --- a/Include/genobject.h Sat Jul 09 11:48:50 2011 +0200 +++ b/Include/genobject.h Sun Jul 10 01:09:48 2011 +1000 @@ -34,6 +34,7 @@ PyAPI_FUNC(PyObject *) PyGen_New(struct _frame *); PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *); +PyAPI_FUNC(int) PyGen_FetchStopIterationValue(PyObject **); #ifdef __cplusplus } diff -r 3a4b983dd70b -r 79088efb6c91 Include/graminit.h --- a/Include/graminit.h Sat Jul 09 11:48:50 2011 +0200 +++ b/Include/graminit.h Sun Jul 10 01:09:48 2011 +1000 @@ -81,3 +81,5 @@ #define comp_if 334 #define encoding_decl 335 #define yield_expr 336 +#define yield_arg 337 +#define yield_from 338 diff -r 3a4b983dd70b -r 79088efb6c91 Include/opcode.h --- a/Include/opcode.h Sat Jul 09 11:48:50 2011 +0200 +++ b/Include/opcode.h Sun Jul 10 01:09:48 2011 +1000 @@ -53,6 +53,7 @@ #define STORE_LOCALS 69 #define PRINT_EXPR 70 #define LOAD_BUILD_CLASS 71 +#define YIELD_FROM 72 #define INPLACE_LSHIFT 75 #define INPLACE_RSHIFT 76 diff -r 3a4b983dd70b -r 79088efb6c91 Include/pyerrors.h --- a/Include/pyerrors.h Sat Jul 09 11:48:50 2011 +0200 +++ b/Include/pyerrors.h Sun Jul 10 01:09:48 2011 +1000 @@ -47,6 +47,11 @@ PyObject *filename; } PyEnvironmentErrorObject; +typedef struct { + PyException_HEAD + PyObject *value; +} PyStopIterationObject; + #ifdef MS_WINDOWS typedef struct { PyException_HEAD @@ -356,6 +361,8 @@ const char *reason /* UTF-8 encoded string */ ); +/* create a StopIteration exception with the given value */ +PyAPI_FUNC(PyObject *) PyStopIteration_New(PyObject *); /* These APIs aren't really part of the error implementation, but often needed to format error messages; the native C lib APIs are diff -r 3a4b983dd70b -r 79088efb6c91 Lib/opcode.py --- a/Lib/opcode.py Sat Jul 09 11:48:50 2011 +0200 +++ b/Lib/opcode.py Sun Jul 10 01:09:48 2011 +1000 @@ -88,6 +88,7 @@ def_op('PRINT_EXPR', 70) def_op('LOAD_BUILD_CLASS', 71) +def_op('YIELD_FROM', 72) def_op('INPLACE_LSHIFT', 75) def_op('INPLACE_RSHIFT', 76) diff -r 3a4b983dd70b -r 79088efb6c91 Lib/test/test_generators.py --- a/Lib/test/test_generators.py Sat Jul 09 11:48:50 2011 +0200 +++ b/Lib/test/test_generators.py Sun Jul 10 01:09:48 2011 +1000 @@ -728,29 +728,6 @@ syntax_tests = """ ->>> def f(): -... return 22 -... yield 1 -Traceback (most recent call last): - .. -SyntaxError: 'return' with argument inside generator - ->>> def f(): -... yield 1 -... return 22 -Traceback (most recent call last): - .. -SyntaxError: 'return' with argument inside generator - -"return None" is not the same as "return" in a generator: - ->>> def f(): -... yield 1 -... return None -Traceback (most recent call last): - .. -SyntaxError: 'return' with argument inside generator - These are fine: >>> def f(): @@ -866,20 +843,6 @@ >>> type(f()) - ->>> def f(): -... if 0: -... lambda x: x # shouldn't trigger here -... return # or here -... def f(i): -... return 2*i # or here -... if 0: -... return 3 # but *this* sucks (line 8) -... if 0: -... yield 2 # because it's a generator (line 10) -Traceback (most recent call last): -SyntaxError: 'return' with argument inside generator - This one caused a crash (see SF bug 567538): >>> def f(): @@ -1566,11 +1529,6 @@ ... SyntaxError: 'yield' outside function ->>> def f(): return lambda x=(yield): 1 -Traceback (most recent call last): - ... -SyntaxError: 'return' with argument inside generator - >>> def f(): x = yield = y Traceback (most recent call last): ... diff -r 3a4b983dd70b -r 79088efb6c91 Lib/test/test_grammar.py --- a/Lib/test/test_grammar.py Sat Jul 09 11:48:50 2011 +0200 +++ b/Lib/test/test_grammar.py Sun Jul 10 01:09:48 2011 +1000 @@ -459,6 +459,7 @@ def test_yield(self): check_syntax_error(self, "class foo:yield 1") + check_syntax_error(self, "yield from") def test_raise(self): # 'raise' test [',' test] diff -r 3a4b983dd70b -r 79088efb6c91 Lib/test/test_parser.py --- a/Lib/test/test_parser.py Sat Jul 09 11:48:50 2011 +0200 +++ b/Lib/test/test_parser.py Sun Jul 10 01:09:48 2011 +1000 @@ -50,6 +50,10 @@ self.check_suite("def f(): (yield 1)*2") self.check_suite("def f(): return; yield 1") self.check_suite("def f(): yield 1; return") + self.check_suite("def f(): yield from 1") + self.check_suite("def f(): x = yield from 1") + self.check_suite("def f(): f(yield from 1)") + self.check_suite("def f(): yield 1; return 1") self.check_suite("def f():\n" " for x in range(30):\n" " yield x\n") diff -r 3a4b983dd70b -r 79088efb6c91 Lib/test/test_pep380.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_pep380.py Sun Jul 10 01:09:48 2011 +1000 @@ -0,0 +1,1096 @@ +# -*- coding: utf-8 -*- + +""" +Test suite for PEP 380 implementation + +adapted from original tests written by Greg Ewing +see +""" + +import unittest +import io +import sys +import traceback + +# XXX (ncoghlan): The line numbers make many of these tests quite fragile. +# Ideally the whole lot should be modified to use independent asserts rather +# than golden output comparisons + +class PEP380Test(unittest.TestCase): + maxDiff = None + def setUp(self): + self.out = io.StringIO() + self.saved_stdout = sys.stdout + self.saved_stderr = sys.stderr + sys.stdout = sys.stderr = self.out + + def test(self): + try: + self.case() + except: + et, ev, tb = sys.exc_info() + tb = tb.tb_next + traceback.print_exception(et, ev, tb) + # Trailing whitespace does not play nicely with checkin hooks + self.assertEqual('\n'.join(line.rstrip() for line in self.out.getvalue().split('\n')), + '\n'.join(line.rstrip() for line in self.expected.split('\n'))) + + def tearDown(self): + sys.stdout = self.saved_stdout + sys.stderr = self.saved_stderr + self.out.close() + + +class Test1(PEP380Test): + """ + Test grammar and code generation + """ + + expected = """\ +---------- g1 ---------- + 78 0 LOAD_CONST 0 (None) + 3 YIELD_VALUE + 4 POP_TOP + + 79 5 LOAD_CONST 1 (42) + 8 YIELD_VALUE + 9 POP_TOP + 10 LOAD_CONST 0 (None) + 13 RETURN_VALUE +---------- g2 ---------- + 82 0 LOAD_GLOBAL 0 (x) + 3 YIELD_FROM + 4 POP_TOP + 5 LOAD_CONST 0 (None) + 8 RETURN_VALUE +---------- g3 ---------- + 85 0 LOAD_FAST 0 (x) + 3 YIELD_FROM + 4 STORE_FAST 0 (x) + 7 LOAD_CONST 0 (None) + 10 RETURN_VALUE +""".format(__file__) + + def case(self): + import dis + + def g1(): + yield + yield 42 + + def g2(): + yield from x + + def g3(): + x = yield from x + + def disgen(g): + print("---------- %s ----------" % g.__name__) + dis.dis(g) + + disgen(g1) + disgen(g2) + disgen(g3) + + +class Test2(PEP380Test): + """ + Test delegation of initial next() call to subgenerator + """ + + expected = """\ +Starting g1 +Starting g2 +Yielded 42 +Finishing g2 +Finishing g1 +""".format(__file__) + + def case(self): + def g1(): + print("Starting g1") + yield from g2() + print("Finishing g1") + + def g2(): + print("Starting g2") + yield 42 + print("Finishing g2") + + for x in g1(): + print("Yielded", x) + + +class Test3(PEP380Test): + """ + Test raising exception in initial next() call + """ + + expected = """\ +Starting g1 +Starting g2 +Finishing g2 +Finishing g1 +Traceback (most recent call last): + File "{0}", line 159, in case + for x in g1(): + File "{0}", line 148, in g1 + yield from g2() + File "{0}", line 155, in g2 + raise ValueError("spanish inquisition occurred") +ValueError: spanish inquisition occurred +""".format(__file__) + + def case(self): + def g1(): + try: + print("Starting g1") + yield from g2() + finally: + print("Finishing g1") + + def g2(): + try: + print("Starting g2") + raise ValueError("spanish inquisition occurred") + finally: + print("Finishing g2") + + for x in g1(): + print("Yielded", x) + + +class Test4(PEP380Test): + """ + Test delegation of next() call to subgenerator + """ + + expected = """\ +Starting g1 +Yielded g1 ham +Starting g2 +Yielded g2 spam +Yielded g2 more spam +Finishing g2 +Yielded g1 eggs +Finishing g1 +""".format(__file__) + + def case(self): + + def g1(): + print("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + print("Finishing g1") + + def g2(): + print("Starting g2") + yield "g2 spam" + yield "g2 more spam" + print("Finishing g2") + + for x in g1(): + print("Yielded", x) + + +class Test5(PEP380Test): + """ + Test raising exception in delegated next() call + """ + + expected = """\ +Starting g1 +Yielded g1 ham +Starting g2 +Yielded g2 spam +Finishing g2 +Finishing g1 +Traceback (most recent call last): + File "{0}", line 239, in case + for x in g1(): + File "{0}", line 225, in g1 + yield from g2() + File "{0}", line 234, in g2 + raise ValueError("hovercraft is full of eels") +ValueError: hovercraft is full of eels +""".format(__file__) + + def case(self): + def g1(): + try: + print("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + print("Finishing g1") + + def g2(): + try: + print("Starting g2") + yield "g2 spam" + raise ValueError("hovercraft is full of eels") + yield "g2 more spam" + finally: + print("Finishing g2") + + for x in g1(): + print("Yielded", x) + + +class Test6(PEP380Test): + """ + Test delegation of send() + """ + + expected = """\ +Starting g1 +g1 received 1 +Starting g2 +Yielded g2 spam +g2 received 2 +Yielded g2 more spam +g2 received 3 +Finishing g2 +Yielded g1 eggs +g1 received 4 +Finishing g1 +""".format(__file__) + + def case(self): + def g1(): + print("Starting g1") + x = yield "g1 ham" + print("g1 received", x) + yield from g2() + x = yield "g1 eggs" + print("g1 received", x) + print("Finishing g1") + + def g2(): + print("Starting g2") + x = yield "g2 spam" + print("g2 received", x) + x = yield "g2 more spam" + print("g2 received", x) + print("Finishing g2") + + g = g1() + y = next(g) + x = 1 + try: + while 1: + y = g.send(x) + print("Yielded", y) + x += 1 + except StopIteration: + pass + + +class Test7(PEP380Test): + """ + Test handling exception while delegating 'send' + """ + + expected = """\ +Starting g1 +g1 received 1 +Starting g2 +Yielded g2 spam +g2 received 2 +Traceback (most recent call last): + File "{0}", line 337, in case + y = g.send(x) + File "{0}", line 318, in g1 + yield from g2() + File "{0}", line 327, in g2 + raise ValueError("hovercraft is full of eels") +ValueError: hovercraft is full of eels +""".format(__file__) + + def case(self): + def g1(): + print("Starting g1") + x = yield "g1 ham" + print("g1 received", x) + yield from g2() + x = yield "g1 eggs" + print("g1 received", x) + print("Finishing g1") + + def g2(): + print("Starting g2") + x = yield "g2 spam" + print("g2 received", x) + raise ValueError("hovercraft is full of eels") + x = yield "g2 more spam" + print("g2 received", x) + print("Finishing g2") + + g = g1() + y = next(g) + x = 1 + try: + while 1: + y = g.send(x) + print("Yielded", y) + x += 1 + except StopIteration: + print("StopIteration") + + + +class Test8(PEP380Test): + """ + Test delegating 'close' + """ + + expected = """\ +Starting g1 +Yielded g1 ham +Starting g2 +Yielded g2 spam +Finishing g2 +Finishing g1 +""".format(__file__) + + def case(self): + def g1(): + try: + print("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + print("Finishing g1") + + def g2(): + try: + print("Starting g2") + yield "g2 spam" + yield "g2 more spam" + finally: + print("Finishing g2") + + g = g1() + for i in range(2): + x = next(g) + print("Yielded", x) + g.close() + + +class Test9(PEP380Test): + """ + Test handling exception while delegating 'close' + """ + + expected = """\ +Starting g1 +Yielded g1 ham +Starting g2 +Yielded g2 spam +Finishing g2 +Finishing g1 +Traceback (most recent call last): + File "{0}", line 426, in g2 + yield "g2 spam" +GeneratorExit + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "{0}", line 436, in case + g.close() + File "{0}", line 418, in g1 + yield from g2() + File "{0}", line 430, in g2 + raise ValueError("nybbles have exploded with delight") +ValueError: nybbles have exploded with delight +""".format(__file__) + + def case(self): + def g1(): + try: + print("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + print("Finishing g1") + + def g2(): + try: + print("Starting g2") + yield "g2 spam" + yield "g2 more spam" + finally: + print("Finishing g2") + raise ValueError("nybbles have exploded with delight") + + g = g1() + for i in range(2): + x = next(g) + print("Yielded", x) + g.close() + + +class Test10(PEP380Test): + """ + Test delegating 'throw' + """ + + expected = """\ +Starting g1 +Yielded g1 ham +Starting g2 +Yielded g2 spam +Finishing g2 +Finishing g1 +Traceback (most recent call last): + File "{0}", line 484, in case + g.throw(e) + File "{0}", line 466, in g1 + yield from g2() + File "{0}", line 474, in g2 + yield "g2 spam" +ValueError: tomato ejected +""".format(__file__) + + def case(self): + def g1(): + try: + print("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + print("Finishing g1") + + def g2(): + try: + print("Starting g2") + yield "g2 spam" + yield "g2 more spam" + finally: + print("Finishing g2") + + g = g1() + for i in range(2): + x = next(g) + print("Yielded", x) + e = ValueError("tomato ejected") + g.throw(e) + + +class Test11(PEP380Test): + """ + Test 'value' attribute of StopIteration exception + """ + + expected = """\ +StopIteration: +value = None +StopIteration: spam +value = spam +StopIteration: spam +value = eggs +""".format(__file__) + + def case(self): + def pex(e): + print("%s: %s" % (e.__class__.__name__, e)) + print("value =", e.value) + + e = StopIteration() + pex(e) + e = StopIteration("spam") + pex(e) + e.value = "eggs" + pex(e) + + +class Test12(PEP380Test): + """ + Test generator return value + """ + + expected = """\ +Starting g1 +Yielded g1 ham +Starting g2 +Yielded g2 spam +Yielded g2 more spam +Finishing g2 +g2 returned None +Starting g2 +Yielded g2 spam +Yielded g2 more spam +Finishing g2 +g2 returned 42 +Yielded g1 eggs +Finishing g1 +""".format(__file__) + + def case(self): + def g1(): + print("Starting g1") + yield "g1 ham" + ret = yield from g2() + print("g2 returned", ret) + ret = yield from g2(42) + print("g2 returned", ret) + yield "g1 eggs" + print("Finishing g1") + + def g2(v = None): + print("Starting g2") + yield "g2 spam" + yield "g2 more spam" + print("Finishing g2") + if v: + return v + + for x in g1(): + print("Yielded", x) + + +class Test13(PEP380Test): + """ + Test delegation of next() to non-generator + """ + + expected = """\ +Yielded 0 +Yielded 1 +Yielded 2 +""".format(__file__) + + def case(self): + def g(): + yield from range(3) + + for x in g(): + print("Yielded", x) + + +class Test14(PEP380Test): + """ + Test conversion of send(None) to next() + """ + + expected = """\ +Yielded: 0 +Yielded: 1 +Yielded: 2 +""".format(__file__) + + def case(self): + def g(): + yield from range(3) + + gi = g() + for x in range(3): + y = gi.send(None) + print("Yielded:", y) + + +class Test15(PEP380Test): + """ + Test delegation of close() to non-generator + """ + + expected = """\ +starting g +finishing g +""".format(__file__) + + def case(self): + def g(): + try: + print("starting g") + yield from range(3) + print("g should not be here") + finally: + print("finishing g") + + gi = g() + next(gi) + gi.close() + + +class Test16(PEP380Test): + """ + Test delegating 'throw' to non-generator + """ + + expected = """\ +Starting g +Yielded 0 +Yielded 1 +Yielded 2 +Yielded 3 +Yielded 4 +Finishing g +Traceback (most recent call last): + File "{0}", line 657, in case + gi.throw(e) + File "{0}", line 648, in g + yield from range(10) +ValueError: tomato ejected +""".format(__file__) + + def case(self): + def g(): + try: + print("Starting g") + yield from range(10) + finally: + print("Finishing g") + + gi = g() + for i in range(5): + x = next(gi) + print("Yielded", x) + e = ValueError("tomato ejected") + gi.throw(e) + + +class Test17(PEP380Test): + """ + Test attempting to send to non-generator + """ + + expected = """\ +starting g +finishing g +Traceback (most recent call last): + File "{0}", line 688, in case + y = gi.send(42) + File "{0}", line 680, in g + yield from range(3) +AttributeError: send +""".format(__file__) + + def case(self): + def g(): + try: + print("starting g") + yield from range(3) + print("g should not be here") + finally: + print("finishing g") + + gi = g() + next(gi) + for x in range(3): + y = gi.send(42) + print("Should not have yielded:", y) + + +class Test18(PEP380Test): + """ + Test exception in initial next() call + """ + + expected = """\ +g1 about to yield from g2 +Traceback (most recent call last): + File "{0}", line 719, in case + next(gi) + File "{0}", line 712, in g1 + yield from g2() + File "{0}", line 716, in g2 + yield 1/0 +ZeroDivisionError: division by zero +""".format(__file__) + + def case(self): + def g1(): + print("g1 about to yield from g2") + yield from g2() + print("g1 should not be here") + + def g2(): + yield 1/0 + + gi = g1() + next(gi) + + +class Test19(PEP380Test): + """ + Test attempted yield-from loop + """ + + expected = """\ +g1: starting +Yielded: y1 +g1: about to yield from g2 +g2: starting +Yielded: y2 +g2: about to yield from g1 +Traceback (most recent call last): + File "{0}", line 760, in case + for y in gi: + File "{0}", line 756, in g2 + yield from gi + File "{0}", line 749, in g1 + yield from g2() +ValueError: generator already executing +""".format(__file__) + + def case(self): + def g1(): + print("g1: starting") + yield "y1" + print("g1: about to yield from g2") + yield from g2() + print("g1 should not be here") + + def g2(): + print("g2: starting") + yield "y2" + print("g2: about to yield from g1") + yield from gi + print("g2 should not be here") + + gi = g1() + for y in gi: + print("Yielded:", y) + + +class Test20(PEP380Test): + """ + Test returning value from delegated 'throw' + """ + + expected = """\ +Starting g1 +Yielded g1 ham +Starting g2 +Yielded g2 spam +Caught LunchError in g2 +Yielded g2 yet more spam +Yielded g1 eggs +Finishing g1 +""".format(__file__) + + def case(self): + def g1(): + try: + print("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + print("Finishing g1") + + def g2(): + try: + print("Starting g2") + yield "g2 spam" + yield "g2 more spam" + except LunchError: + print("Caught LunchError in g2") + yield "g2 lunch saved" + yield "g2 yet more spam" + + class LunchError(Exception): + pass + + g = g1() + for i in range(2): + x = next(g) + print("Yielded", x) + e = LunchError("tomato ejected") + g.throw(e) + for x in g: + print("Yielded", x) + + +class Test21(PEP380Test): + """ + Test next and return with value + """ + + expected = """\ +g starting +f resuming g +g returning None +f caught StopIteration() +g starting +f resuming g +g returning 42 +f caught StopIteration(42,) +""".format(__file__) + + def case(self): + def f(r): + gi = g(r) + next(gi) + try: + print("f resuming g") + next(gi) + print("f SHOULD NOT BE HERE") + except StopIteration as e: + print("f caught", repr(e)) + + def g(r): + print("g starting") + yield + print("g returning", r) + return r + + f(None) + f(42) + + +class Test22(PEP380Test): + """ + Test send and return with value + """ + + expected = """\ +g starting +f sending spam to g +g received spam +g returning None +f caught StopIteration() +g starting +f sending spam to g +g received spam +g returning 42 +f caught StopIteration(42,) +""".format(__file__) + + def case(self): + def f(r): + gi = g(r) + next(gi) + try: + print("f sending spam to g") + gi.send("spam") + print("f SHOULD NOT BE HERE") + except StopIteration as e: + print("f caught", repr(e)) + + def g(r): + print("g starting") + x = yield + print("g received", x) + print("g returning", r) + return r + + f(None) + f(42) + + +class Test23(PEP380Test): + """ + Test parsing yield from as function argument + """ + + expected = """\ +909 0 LOAD_GLOBAL 0 (f) + 3 LOAD_GLOBAL 1 (x) + 6 YIELD_FROM + 7 CALL_FUNCTION 1 + 10 POP_TOP + 11 LOAD_CONST 0 (None) + 14 RETURN_VALUE +""".format(__file__) + + def case(self): + from dis import dis + + def g(): + f(yield from x) + + dis(g) + + +class Test24(PEP380Test): + """ + Test parser module + """ + + expected = """\ +(257, (268, (269, (270, (276, (280, (336, (1, 'yield'), (337, (1, 'from'), (302, (306, (307, (308, (309, (312, (313, (314, (315, (316, (317, (318, (319, (320, (2, '1')))))))))))))))))))), (4, ''))), (4, ''), (0, '')) +True +(257, (268, (269, (270, (271, (272, (302, (306, (307, (308, (309, (312, (313, (314, (315, (316, (317, (318, (319, (320, (1, 'f')), (322, (7, '('), (330, (331, (338, (1, 'yield'), (1, 'from'), (302, (306, (307, (308, (309, (312, (313, (314, (315, (316, (317, (318, (319, (320, (2, '1')))))))))))))))))), (8, ')')))))))))))))))))), (4, ''))), (4, ''), (0, '')) +True +""".format(__file__) + + def case(self): + import parser + + def test(src): + st1 = parser.suite(src) + tup1 = st1.totuple() + print(tup1) + st2 = parser.sequence2st(tup1) + tup2 = st2.totuple() + print(tup1 == tup2) + + test("yield from 1") + test("f(yield from 1)") + + +class Test25(PEP380Test): + """ + Test catching an exception thrown into a + subgenerator and returning a value + """ + + expected = """\ +1 +inner caught ValueError +inner returned 2 to outer +2 +""".format(__file__) + + def case(self): + def inner(): + try: + yield 1 + except ValueError: + print("inner caught ValueError") #pass + return 2 + + def outer(): + v = yield from inner() + print("inner returned %r to outer" % v) + yield v + + g = outer() + print(next(g)) # prints 1 + print(g.throw(ValueError)) # prints 2 + + +class Test26(PEP380Test): + """ + Test throwing GeneratorExit into a subgenerator that + catches it and returns normally. + """ + + expected = """\ +Enter g +Enter f +Traceback (most recent call last): + File "{0}", line 1005, in case + gi.throw(GeneratorExit) + File "{0}", line 1000, in g + yield from f() +GeneratorExit +""".format(__file__) + + def case(self): + def f(): + try: + print("Enter f") + yield + print("Exit f") + except GeneratorExit: + return + + def g(): + print("Enter g") + yield from f() + print("Exit g") + + gi = g() + next(gi) + gi.throw(GeneratorExit) + + +class Test27(PEP380Test): + """ + Test throwing GeneratorExit into a subgenerator that + catches it and yields. + """ + + expected = """\ +Enter g +Enter f +Traceback (most recent call last): + File "{0}", line 1041, in case + gi.throw(GeneratorExit) + File "{0}", line 1036, in g + yield from f() +RuntimeError: generator ignored GeneratorExit +""".format(__file__) + + def case(self): + def f(): + try: + print("Enter f") + yield + print("Exit f") + except GeneratorExit: + yield + + def g(): + print("Enter g") + yield from f() + print("Exit g") + + gi = g() + next(gi) + gi.throw(GeneratorExit) + + +class Test28(PEP380Test): + """ + Test throwing GeneratorExit into a subgenerator that + catches it and raises a different exception. + """ + + expected = """\ +Enter g +Enter f +Traceback (most recent call last): + File "{0}", line 1074, in f + yield +GeneratorExit + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "{0}", line 1086, in case + gi.throw(GeneratorExit) + File "{0}", line 1081, in g + yield from f() + File "{0}", line 1077, in f + raise ValueError("Vorpal bunny encountered") +ValueError: Vorpal bunny encountered +""".format(__file__) + + def case(self): + def f(): + try: + print("Enter f") + yield + print("Exit f") + except GeneratorExit: + raise ValueError("Vorpal bunny encountered") + + def g(): + print("Enter g") + yield from f() + print("Exit g") + + gi = g() + next(gi) + gi.throw(GeneratorExit) + + +def test_main(): + from test import support + test_classes = [Test1, Test2, Test3, Test4, Test5, Test6, Test7, Test8, Test9, Test10, Test11, Test12, Test13, Test14, Test15, Test16, Test17, Test18, Test19, Test20, Test21, Test22, Test23, Test24, Test25, Test26, Test27, Test28] + support.run_unittest(*test_classes) + + +if __name__ == '__main__': + test_main() diff -r 3a4b983dd70b -r 79088efb6c91 Lib/test/test_sys.py --- a/Lib/test/test_sys.py Sat Jul 09 11:48:50 2011 +0200 +++ b/Lib/test/test_sys.py Sun Jul 10 01:09:48 2011 +1000 @@ -726,7 +726,7 @@ nfrees = len(x.f_code.co_freevars) extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\ ncells + nfrees - 1 - check(x, size(vh + '12P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) + check(x, size(vh + '13P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) # function def func(): pass check(func, size(h + '11P')) diff -r 3a4b983dd70b -r 79088efb6c91 Misc/ACKS --- a/Misc/ACKS Sat Jul 09 11:48:50 2011 +0200 +++ b/Misc/ACKS Sun Jul 10 01:09:48 2011 +1000 @@ -93,6 +93,7 @@ Philippe Biondi Stuart Bishop Roy Bixler +Renaud Blanch Mike Bland Martin Bless Pablo Bleyer diff -r 3a4b983dd70b -r 79088efb6c91 Misc/NEWS --- a/Misc/NEWS Sat Jul 09 11:48:50 2011 +0200 +++ b/Misc/NEWS Sun Jul 10 01:09:48 2011 +1000 @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- PEP 380, Issue #11682: Add "yield from " to support easy delegation to + subgenerators (patch by Greg Ewing and Renaud Blanch) + - Issue #9611, #9015: FileIO.read() clamps the length to INT_MAX on Windows. - Issue #9642: Uniformize the tests on the availability of the mbcs codec, add diff -r 3a4b983dd70b -r 79088efb6c91 Modules/parsermodule.c --- a/Modules/parsermodule.c Sat Jul 09 11:48:50 2011 +0200 +++ b/Modules/parsermodule.c Sun Jul 10 01:09:48 2011 +1000 @@ -976,6 +976,7 @@ VALIDATER(testlist_comp); VALIDATER(yield_expr); VALIDATER(or_test); VALIDATER(test_nocond); VALIDATER(lambdef_nocond); +VALIDATER(yield_arg); VALIDATER(yield_from); #undef VALIDATER @@ -1636,22 +1637,61 @@ } -/* yield_expr: 'yield' [testlist] +/* yield_expr: 'yield' [yield_arg] */ static int validate_yield_expr(node *tree) { int nch = NCH(tree); - int res = (validate_ntype(tree, yield_expr) - && ((nch == 1) || (nch == 2)) - && validate_name(CHILD(tree, 0), "yield")); - - if (res && (nch == 2)) - res = validate_testlist(CHILD(tree, 1)); - - return (res); + if (nch < 1 || nch > 2) + return 0; + if (!validate_ntype(tree, yield_expr)) + return 0; + if (!validate_name(CHILD(tree, 0), "yield")) + return 0; + if (nch == 2) { + if (!validate_yield_arg(CHILD(tree, 1))) + return 0; + } + return 1; } +/* yield_arg: 'from' test | testlist + */ +static int +validate_yield_arg(node *tree) +{ + int nch = NCH(tree); + if (!validate_ntype(tree, yield_arg)) + return 0; + if (nch == 1) { + if (!validate_testlist(CHILD(tree, nch - 1))) + return 0; + } + else if (nch == 2) { + if (!validate_name(CHILD(tree, 0), "from")) + return 0; + if (!validate_test(CHILD(tree, 1))) + return 0; + } + else + return 0; + return 1; +} + +/* yield_from: 'yield' 'from' test + */ +static int +validate_yield_from(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, yield_from) + && (nch == 3) + && validate_name(CHILD(tree, 0), "yield") + && validate_name(CHILD(tree, 1), "from") + && validate_test(CHILD(tree, 2))); + return res; +} /* yield_stmt: yield_expr */ @@ -2665,9 +2705,13 @@ { int nch = NCH(tree); int res = (validate_ntype(tree, argument) - && ((nch == 1) || (nch == 2) || (nch == 3)) - && validate_test(CHILD(tree, 0))); - + && ((nch == 1) || (nch == 2) || (nch == 3))); + if (res) { + if (TYPE(CHILD(tree, 0)) == yield_from) + res = (nch == 1) && validate_yield_from(CHILD(tree, 0)); + else + res = validate_test(CHILD(tree, 0)); + } if (res && (nch == 2)) res = validate_comp_for(CHILD(tree, 1)); else if (res && (nch == 3)) diff -r 3a4b983dd70b -r 79088efb6c91 Objects/exceptions.c --- a/Objects/exceptions.c Sat Jul 09 11:48:50 2011 +0200 +++ b/Objects/exceptions.c Sun Jul 10 01:09:48 2011 +1000 @@ -471,8 +471,65 @@ /* * StopIteration extends Exception */ -SimpleExtendsException(PyExc_Exception, StopIteration, - "Signal the end from iterator.__next__()."); + +//SimpleExtendsException(PyExc_Exception, StopIteration, +// "Signal the end from iterator.__next__()."); + +static PyMemberDef StopIteration_members[] = { + {"value", T_OBJECT, offsetof(PyStopIterationObject, value), 0, + PyDoc_STR("generator return value")}, + {NULL} /* Sentinel */ +}; + +static int +StopIteration_init(PyStopIterationObject *self, PyObject *args, PyObject *kwds) +{ + Py_ssize_t size = PyTuple_GET_SIZE(args); + PyObject *value; + + if (BaseException_init((PyBaseExceptionObject *)self, args, kwds) == -1) + return -1; + Py_CLEAR(self->value); + if (size > 0) + value = PyTuple_GET_ITEM(args, 0); + else + value = Py_None; + Py_INCREF(value); + self->value = value; + return 0; +} + +static int +StopIteration_clear(PyStopIterationObject *self) +{ + Py_CLEAR(self->value); + return BaseException_clear((PyBaseExceptionObject *)self); +} + +static void +StopIteration_dealloc(PyStopIterationObject *self) +{ + _PyObject_GC_UNTRACK(self); + StopIteration_clear(self); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static int +StopIteration_traverse(PyStopIterationObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->value); + return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg); +} + +PyObject * +PyStopIteration_New(PyObject *value) +{ + return PyObject_CallFunctionObjArgs(PyExc_StopIteration, value, NULL); +} + +ComplexExtendsException(PyExc_Exception, StopIteration, StopIteration, + StopIteration_dealloc, 0, StopIteration_members, 0, + "Signal the end from iterator.__next__()."); /* diff -r 3a4b983dd70b -r 79088efb6c91 Objects/frameobject.c --- a/Objects/frameobject.c Sat Jul 09 11:48:50 2011 +0200 +++ b/Objects/frameobject.c Sun Jul 10 01:09:48 2011 +1000 @@ -15,11 +15,12 @@ #define OFF(x) offsetof(PyFrameObject, x) static PyMemberDef frame_memberlist[] = { - {"f_back", T_OBJECT, OFF(f_back), READONLY}, - {"f_code", T_OBJECT, OFF(f_code), READONLY}, - {"f_builtins", T_OBJECT, OFF(f_builtins),READONLY}, - {"f_globals", T_OBJECT, OFF(f_globals), READONLY}, - {"f_lasti", T_INT, OFF(f_lasti), READONLY}, + {"f_back", T_OBJECT, OFF(f_back), READONLY}, + {"f_code", T_OBJECT, OFF(f_code), READONLY}, + {"f_builtins", T_OBJECT, OFF(f_builtins), READONLY}, + {"f_globals", T_OBJECT, OFF(f_globals), READONLY}, + {"f_lasti", T_INT, OFF(f_lasti), READONLY}, + {"f_yieldfrom", T_OBJECT, OFF(f_yieldfrom), READONLY}, {NULL} /* Sentinel */ }; @@ -444,6 +445,7 @@ Py_CLEAR(f->f_exc_type); Py_CLEAR(f->f_exc_value); Py_CLEAR(f->f_exc_traceback); + Py_CLEAR(f->f_yieldfrom); co = f->f_code; if (co->co_zombieframe == NULL) @@ -475,6 +477,7 @@ Py_VISIT(f->f_exc_type); Py_VISIT(f->f_exc_value); Py_VISIT(f->f_exc_traceback); + Py_VISIT(f->f_yieldfrom); /* locals */ slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars); @@ -508,6 +511,7 @@ Py_CLEAR(f->f_exc_value); Py_CLEAR(f->f_exc_traceback); Py_CLEAR(f->f_trace); + Py_CLEAR(f->f_yieldfrom); /* locals */ slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars); @@ -711,6 +715,7 @@ f->f_lasti = -1; f->f_lineno = code->co_firstlineno; f->f_iblock = 0; + f->f_yieldfrom = NULL; _PyObject_GC_TRACK(f); return f; diff -r 3a4b983dd70b -r 79088efb6c91 Objects/genobject.c --- a/Objects/genobject.c Sat Jul 09 11:48:50 2011 +0200 +++ b/Objects/genobject.c Sun Jul 10 01:09:48 2011 +1000 @@ -5,6 +5,9 @@ #include "structmember.h" #include "opcode.h" +static PyObject *gen_close(PyGenObject *gen, PyObject *args); +static void gen_undelegate(PyGenObject *); + static int gen_traverse(PyGenObject *gen, visitproc visit, void *arg) { @@ -90,12 +93,15 @@ /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ - if (result == Py_None && f->f_stacktop == NULL) { + if (result && f->f_stacktop == NULL) { + if (result == Py_None) + PyErr_SetNone(PyExc_StopIteration); + else { + PyObject *e = PyStopIteration_New(result); + PyErr_SetObject(PyExc_StopIteration, e); + } Py_DECREF(result); result = NULL; - /* Set exception if not called by gen_iternext() */ - if (arg) - PyErr_SetNone(PyExc_StopIteration); } if (!result || f->f_stacktop == NULL) { @@ -114,17 +120,82 @@ static PyObject * gen_send(PyGenObject *gen, PyObject *arg) { - return gen_send_ex(gen, arg, 0); + int exc = 0; + PyObject *ret; + PyObject *yf = gen->gi_frame ? gen->gi_frame->f_yieldfrom : NULL; + Py_INCREF(arg); + if (yf) { + Py_INCREF(yf); + if (PyGen_CheckExact(yf)) + ret = gen_send((PyGenObject *)yf, arg); + else { + if (arg == Py_None) + ret = PyIter_Next(yf); + else + ret = PyObject_CallMethod(yf, "send", "O", arg); + } + if (ret) { + Py_DECREF(yf); + goto done; + } + gen_undelegate(gen); + Py_DECREF(arg); + arg = NULL; + if (PyGen_FetchStopIterationValue(&arg) < 0) + exc = 1; + Py_DECREF(yf); + } + ret = gen_send_ex(gen, arg, exc); +done: + Py_XDECREF(arg); + return ret; } PyDoc_STRVAR(close_doc, "close(arg) -> raise GeneratorExit inside generator."); +/* + * This helper function is used by gen_close and gen_throw to + * close a subiterator being delegated to by yield-from. + */ + +static int +gen_close_iter(PyObject *yf) { + PyObject *retval = NULL; + + if (PyGen_CheckExact(yf)) { + retval = gen_close((PyGenObject *)yf, NULL); + if (!retval) + return -1; + } + else { + PyObject *meth = PyObject_GetAttrString(yf, "close"); + if (meth) { + retval = PyObject_CallFunction(meth, ""); + Py_DECREF(meth); + if (!retval) + return -1; + } + } + Py_XDECREF(retval); + return 0; +} + static PyObject * gen_close(PyGenObject *gen, PyObject *args) { PyObject *retval; - PyErr_SetNone(PyExc_GeneratorExit); + PyObject *yf = gen->gi_frame ? gen->gi_frame->f_yieldfrom : NULL; + int err = 0; + + if (yf) { + Py_INCREF(yf); + err = gen_close_iter(yf); + gen_undelegate(gen); + Py_DECREF(yf); + } + if (err == 0) + PyErr_SetNone(PyExc_GeneratorExit); retval = gen_send_ex(gen, Py_None, 1); if (retval) { Py_DECREF(retval); @@ -215,10 +286,51 @@ PyObject *typ; PyObject *tb = NULL; PyObject *val = NULL; + PyObject *yf = gen->gi_frame ? gen->gi_frame->f_yieldfrom : NULL; if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb)) return NULL; + if (yf) { + PyObject *ret; + int err; + Py_INCREF(yf); + if (PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit)) { + err = gen_close_iter(yf); + Py_DECREF(yf); + gen_undelegate(gen); + if (err < 0) + return gen_send_ex(gen, Py_None, 1); + goto throw_here; + } + if (PyGen_CheckExact(yf)) + ret = gen_throw((PyGenObject *)yf, args); + else { + PyObject *meth = PyObject_GetAttrString(yf, "throw"); + if (!meth) { + PyErr_Clear(); + Py_DECREF(yf); + gen_undelegate(gen); + goto throw_here; + } + ret = PyObject_CallObject(meth, args); + Py_DECREF(meth); + } + Py_DECREF(yf); + if (!ret) { + PyObject *val; + gen_undelegate(gen); + if (PyGen_FetchStopIterationValue(&val) == 0) { + ret = gen_send_ex(gen, val, 0); + Py_DECREF(val); + } + else + ret = gen_send_ex(gen, Py_None, 1); + } + return ret; + } + +throw_here: /* First, check the traceback argument, replacing None with NULL. */ if (tb == Py_None) @@ -276,9 +388,74 @@ static PyObject * gen_iternext(PyGenObject *gen) { - return gen_send_ex(gen, NULL, 0); + PyObject *val = NULL; + PyObject *ret; + int exc = 0; + PyObject *yf = gen->gi_frame ? gen->gi_frame->f_yieldfrom : NULL; + if (yf) { + Py_INCREF(yf); + /* ceval.c ensures that yf is an iterator */ + ret = yf->ob_type->tp_iternext(yf); + if (ret) { + Py_DECREF(yf); + return ret; + } + gen_undelegate(gen); + if (PyGen_FetchStopIterationValue(&val) < 0) + exc = 1; + Py_DECREF(yf); + } + ret = gen_send_ex(gen, val, exc); + Py_XDECREF(val); + return ret; } +/* + * In certain recursive situations, a generator may lose its frame + * before we get a chance to clear f_yieldfrom, so we use this + * helper function. + */ + +static void +gen_undelegate(PyGenObject *gen) { + if (gen->gi_frame) { + Py_XDECREF(gen->gi_frame->f_yieldfrom); + gen->gi_frame->f_yieldfrom = NULL; + } +} + +/* + * If StopIteration exception is set, fetches its 'value' + * attribute if any, otherwise sets pvalue to None. + * + * Returns 0 if no exception or StopIteration is set. + * If any other exception is set, returns -1 and leaves + * pvalue unchanged. + */ + +int +PyGen_FetchStopIterationValue(PyObject **pvalue) { + PyObject *et, *ev, *tb; + PyObject *value = NULL; + + if (PyErr_ExceptionMatches(PyExc_StopIteration)) { + PyErr_Fetch(&et, &ev, &tb); + Py_XDECREF(et); Py_XDECREF(tb); + if (ev) { + value = ((PyStopIterationObject *)ev)->value; + Py_XINCREF(value); + Py_DECREF(ev); + } + } + else if (PyErr_Occurred()) + return -1; + if (!value) { + value = Py_None; + Py_INCREF(value); + } + *pvalue = value; + return 0; +} static PyObject * gen_repr(PyGenObject *gen) diff -r 3a4b983dd70b -r 79088efb6c91 Parser/Python.asdl --- a/Parser/Python.asdl Sat Jul 09 11:48:50 2011 +0200 +++ b/Parser/Python.asdl Sun Jul 10 01:09:48 2011 +1000 @@ -59,7 +59,7 @@ | DictComp(expr key, expr value, comprehension* generators) | GeneratorExp(expr elt, comprehension* generators) -- the grammar constrains where yield expressions can occur - | Yield(expr? value) + | Yield(int is_from, expr? value) -- need sequences for compare to distinguish between -- x < 4 < 3 and (x < 4) < 3 | Compare(expr left, cmpop* ops, expr* comparators) diff -r 3a4b983dd70b -r 79088efb6c91 Python/Python-ast.c --- a/Python/Python-ast.c Sat Jul 09 11:48:50 2011 +0200 +++ b/Python/Python-ast.c Sun Jul 10 01:09:48 2011 +1000 @@ -205,6 +205,7 @@ }; static PyTypeObject *Yield_type; static char *Yield_fields[]={ + "is_from", "value", }; static PyTypeObject *Compare_type; @@ -732,7 +733,7 @@ GeneratorExp_type = make_type("GeneratorExp", expr_type, GeneratorExp_fields, 2); if (!GeneratorExp_type) return 0; - Yield_type = make_type("Yield", expr_type, Yield_fields, 1); + Yield_type = make_type("Yield", expr_type, Yield_fields, 2); if (!Yield_type) return 0; Compare_type = make_type("Compare", expr_type, Compare_fields, 3); if (!Compare_type) return 0; @@ -1669,13 +1670,14 @@ } expr_ty -Yield(expr_ty value, int lineno, int col_offset, PyArena *arena) +Yield(int is_from, expr_ty value, int lineno, int col_offset, PyArena *arena) { expr_ty p; p = (expr_ty)PyArena_Malloc(arena, sizeof(*p)); if (!p) return NULL; p->kind = Yield_kind; + p->v.Yield.is_from = is_from; p->v.Yield.value = value; p->lineno = lineno; p->col_offset = col_offset; @@ -2717,6 +2719,11 @@ case Yield_kind: result = PyType_GenericNew(Yield_type, NULL, NULL); if (!result) goto failed; + value = ast2obj_int(o->v.Yield.is_from); + if (!value) goto failed; + if (PyObject_SetAttrString(result, "is_from", value) == -1) + goto failed; + Py_DECREF(value); value = ast2obj_expr(o->v.Yield.value); if (!value) goto failed; if (PyObject_SetAttrString(result, "value", value) == -1) @@ -5259,8 +5266,21 @@ return 1; } if (isinstance) { + int is_from; expr_ty value; + if (PyObject_HasAttrString(obj, "is_from")) { + int res; + tmp = PyObject_GetAttrString(obj, "is_from"); + if (tmp == NULL) goto failed; + res = obj2ast_int(tmp, &is_from, arena); + if (res != 0) goto failed; + Py_XDECREF(tmp); + tmp = NULL; + } else { + PyErr_SetString(PyExc_TypeError, "required field \"is_from\" missing from Yield"); + return 1; + } if (PyObject_HasAttrString(obj, "value")) { int res; tmp = PyObject_GetAttrString(obj, "value"); @@ -5272,7 +5292,7 @@ } else { value = NULL; } - *out = Yield(value, lineno, col_offset, arena); + *out = Yield(is_from, value, lineno, col_offset, arena); if (*out == NULL) goto failed; return 0; } diff -r 3a4b983dd70b -r 79088efb6c91 Python/ast.c --- a/Python/ast.c Sat Jul 09 11:48:50 2011 +0200 +++ b/Python/ast.c Sun Jul 10 01:09:48 2011 +1000 @@ -1895,13 +1895,28 @@ } return ast_for_binop(c, n); case yield_expr: { + node *an = NULL; + node *en = NULL; + int is_from = 0; expr_ty exp = NULL; - if (NCH(n) == 2) { - exp = ast_for_testlist(c, CHILD(n, 1)); + if (NCH(n) > 1) + an = CHILD(n, 1); /* yield_arg */ + if (an) { + en = CHILD(an, NCH(an) - 1); + if (NCH(an) == 2) { + is_from = 1; + exp = ast_for_expr(c, en); + } + else + exp = ast_for_testlist(c, en); if (!exp) return NULL; } - return Yield(exp, LINENO(n), n->n_col_offset, c->c_arena); + return Yield(is_from, exp, LINENO(n), n->n_col_offset, c->c_arena); + } + case yield_from: { + expr_ty exp = ast_for_expr(c, CHILD(n, 2)); + return Yield(1, exp, LINENO(n), n->n_col_offset, c->c_arena); } case factor: if (NCH(n) == 1) { @@ -1925,7 +1940,7 @@ /* arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test) - argument: [test '='] test [comp_for] # Really [keyword '='] test + argument: [test '='] (test | yield_from) [comp_for] # Really [keyword '='] test */ int i, nargs, nkeywords, ngens; @@ -2219,7 +2234,7 @@ continue_stmt: 'continue' return_stmt: 'return' [testlist] yield_stmt: yield_expr - yield_expr: 'yield' testlist + yield_expr: 'yield' testlist | 'yield' 'from' test raise_stmt: 'raise' [test [',' test [',' test]]] */ node *ch; diff -r 3a4b983dd70b -r 79088efb6c91 Python/ceval.c --- a/Python/ceval.c Sat Jul 09 11:48:50 2011 +0200 +++ b/Python/ceval.c Sun Jul 10 01:09:48 2011 +1000 @@ -1829,8 +1829,47 @@ why = WHY_RETURN; goto fast_block_end; + TARGET(YIELD_FROM) + u = POP(); + x = PyObject_GetIter(u); + Py_DECREF(u); + if (!x) + break; + /* x is now the iterator, make the first next() call */ + retval = (*x->ob_type->tp_iternext)(x); + if (!retval) { + /* iter may be exhausted */ + Py_DECREF(x); + if (PyErr_ExceptionMatches(PyExc_StopIteration)) { + /* try to get return value from exception */ + PyObject *et, *ev, *tb; + PyErr_Fetch(&et, &ev, &tb); + Py_XDECREF(et); Py_XDECREF(tb); + u = NULL; + if (ev) { + u = PyObject_GetAttrString(ev, "value"); + Py_DECREF(ev); + if (!u) PyErr_Clear(); + } + if (!u) { + u = Py_None; + Py_INCREF(u); + } + /* u is return value */ + PUSH(u); + continue; + } + /* some other exception */ + x = NULL; + break; + } + /* x is iterator, retval is value to be yielded */ + f->f_yieldfrom = x; + goto yield_retval; + TARGET(YIELD_VALUE) retval = POP(); + yield_retval: f->f_stacktop = stack_pointer; why = WHY_YIELD; goto fast_yield; diff -r 3a4b983dd70b -r 79088efb6c91 Python/compile.c --- a/Python/compile.c Sat Jul 09 11:48:50 2011 +0200 +++ b/Python/compile.c Sun Jul 10 01:09:48 2011 +1000 @@ -763,6 +763,7 @@ case IMPORT_STAR: return -1; case YIELD_VALUE: + case YIELD_FROM: return 0; case POP_BLOCK: @@ -3202,7 +3203,12 @@ else { ADDOP_O(c, LOAD_CONST, Py_None, consts); } - ADDOP(c, YIELD_VALUE); + if (e->v.Yield.is_from) { + ADDOP(c, YIELD_FROM); + } + else { + ADDOP(c, YIELD_VALUE); + } break; case Compare_kind: return compiler_compare(c, e); diff -r 3a4b983dd70b -r 79088efb6c91 Python/graminit.c --- a/Python/graminit.c Sat Jul 09 11:48:50 2011 +0200 +++ b/Python/graminit.c Sun Jul 10 01:09:48 2011 +1000 @@ -1700,29 +1700,36 @@ {1, arcs_74_6}, {2, arcs_74_7}, }; -static arc arcs_75_0[1] = { +static arc arcs_75_0[2] = { {24, 1}, + {164, 2}, }; static arc arcs_75_1[3] = { - {158, 2}, - {29, 3}, + {158, 3}, + {29, 4}, {0, 1}, }; -static arc arcs_75_2[1] = { +static arc arcs_75_2[2] = { + {158, 3}, {0, 2}, }; static arc arcs_75_3[1] = { - {24, 2}, + {0, 3}, }; -static state states_75[4] = { - {1, arcs_75_0}, +static arc arcs_75_4[2] = { + {24, 3}, + {164, 3}, +}; +static state states_75[5] = { + {2, arcs_75_0}, {3, arcs_75_1}, - {1, arcs_75_2}, + {2, arcs_75_2}, {1, arcs_75_3}, + {2, arcs_75_4}, }; static arc arcs_76_0[2] = { {158, 1}, - {165, 1}, + {166, 1}, }; static arc arcs_76_1[1] = { {0, 1}, @@ -1744,7 +1751,7 @@ {107, 4}, }; static arc arcs_77_4[2] = { - {164, 5}, + {165, 5}, {0, 4}, }; static arc arcs_77_5[1] = { @@ -1765,7 +1772,7 @@ {109, 2}, }; static arc arcs_78_2[2] = { - {164, 3}, + {165, 3}, {0, 2}, }; static arc arcs_78_3[1] = { @@ -1788,10 +1795,10 @@ {1, arcs_79_1}, }; static arc arcs_80_0[1] = { - {167, 1}, + {168, 1}, }; static arc arcs_80_1[2] = { - {9, 2}, + {169, 2}, {0, 1}, }; static arc arcs_80_2[1] = { @@ -1802,171 +1809,208 @@ {2, arcs_80_1}, {1, arcs_80_2}, }; -static dfa dfas[81] = { +static arc arcs_81_0[2] = { + {73, 1}, + {9, 2}, +}; +static arc arcs_81_1[1] = { + {24, 2}, +}; +static arc arcs_81_2[1] = { + {0, 2}, +}; +static state states_81[3] = { + {2, arcs_81_0}, + {1, arcs_81_1}, + {1, arcs_81_2}, +}; +static arc arcs_82_0[1] = { + {168, 1}, +}; +static arc arcs_82_1[1] = { + {73, 2}, +}; +static arc arcs_82_2[1] = { + {24, 3}, +}; +static arc arcs_82_3[1] = { + {0, 3}, +}; +static state states_82[4] = { + {1, arcs_82_0}, + {1, arcs_82_1}, + {1, arcs_82_2}, + {1, arcs_82_3}, +}; +static dfa dfas[83] = { {256, "single_input", 0, 3, states_0, - "\004\050\060\200\000\000\000\240\340\223\160\220\045\200\020\000\000\206\120\076\204"}, + "\004\050\060\200\000\000\000\240\340\223\160\220\045\200\020\000\000\206\120\076\004\001"}, {257, "file_input", 0, 2, states_1, - "\204\050\060\200\000\000\000\240\340\223\160\220\045\200\020\000\000\206\120\076\204"}, + "\204\050\060\200\000\000\000\240\340\223\160\220\045\200\020\000\000\206\120\076\004\001"}, {258, "eval_input", 0, 3, states_2, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\000"}, {259, "decorator", 0, 7, states_3, - "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {260, "decorators", 0, 2, states_4, - "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {261, "decorated", 0, 3, states_5, - "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {262, "funcdef", 0, 8, states_6, - "\000\000\020\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\020\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {263, "parameters", 0, 4, states_7, - "\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {264, "typedargslist", 0, 18, states_8, - "\000\000\040\200\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\040\200\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {265, "tfpdef", 0, 4, states_9, - "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {266, "varargslist", 0, 18, states_10, - "\000\000\040\200\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\040\200\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {267, "vfpdef", 0, 2, states_11, - "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {268, "stmt", 0, 2, states_12, - "\000\050\060\200\000\000\000\240\340\223\160\220\045\200\020\000\000\206\120\076\204"}, + "\000\050\060\200\000\000\000\240\340\223\160\220\045\200\020\000\000\206\120\076\004\001"}, {269, "simple_stmt", 0, 4, states_13, - "\000\040\040\200\000\000\000\240\340\223\160\000\000\200\020\000\000\206\120\076\200"}, + "\000\040\040\200\000\000\000\240\340\223\160\000\000\200\020\000\000\206\120\076\000\001"}, {270, "small_stmt", 0, 2, states_14, - "\000\040\040\200\000\000\000\240\340\223\160\000\000\200\020\000\000\206\120\076\200"}, + "\000\040\040\200\000\000\000\240\340\223\160\000\000\200\020\000\000\206\120\076\000\001"}, {271, "expr_stmt", 0, 6, states_15, - "\000\040\040\200\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000"}, + "\000\040\040\200\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\000"}, {272, "testlist_star_expr", 0, 3, states_16, - "\000\040\040\200\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000"}, + "\000\040\040\200\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\000"}, {273, "augassign", 0, 2, states_17, - "\000\000\000\000\000\000\376\037\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\376\037\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {274, "del_stmt", 0, 3, states_18, - "\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {275, "pass_stmt", 0, 2, states_19, - "\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {276, "flow_stmt", 0, 2, states_20, - "\000\000\000\000\000\000\000\000\340\001\000\000\000\000\000\000\000\000\000\000\200"}, + "\000\000\000\000\000\000\000\000\340\001\000\000\000\000\000\000\000\000\000\000\000\001"}, {277, "break_stmt", 0, 2, states_21, - "\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {278, "continue_stmt", 0, 2, states_22, - "\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {279, "return_stmt", 0, 3, states_23, - "\000\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {280, "yield_stmt", 0, 2, states_24, - "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\200"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001"}, {281, "raise_stmt", 0, 5, states_25, - "\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000\000\000"}, {282, "import_stmt", 0, 2, states_26, - "\000\000\000\000\000\000\000\000\000\022\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\022\000\000\000\000\000\000\000\000\000\000\000\000"}, {283, "import_name", 0, 3, states_27, - "\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000\000\000\000"}, {284, "import_from", 0, 8, states_28, - "\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000\000\000\000\000"}, {285, "import_as_name", 0, 4, states_29, - "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {286, "dotted_as_name", 0, 4, states_30, - "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {287, "import_as_names", 0, 3, states_31, - "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {288, "dotted_as_names", 0, 2, states_32, - "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {289, "dotted_name", 0, 2, states_33, - "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {290, "global_stmt", 0, 3, states_34, - "\000\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000\000\000"}, {291, "nonlocal_stmt", 0, 3, states_35, - "\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000\000\000"}, {292, "assert_stmt", 0, 5, states_36, - "\000\000\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\000\000\000"}, {293, "compound_stmt", 0, 2, states_37, - "\000\010\020\000\000\000\000\000\000\000\000\220\045\000\000\000\000\000\000\000\004"}, + "\000\010\020\000\000\000\000\000\000\000\000\220\045\000\000\000\000\000\000\000\004\000"}, {294, "if_stmt", 0, 8, states_38, - "\000\000\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000\000"}, {295, "while_stmt", 0, 8, states_39, - "\000\000\000\000\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000"}, {296, "for_stmt", 0, 10, states_40, - "\000\000\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000"}, {297, "try_stmt", 0, 13, states_41, - "\000\000\000\000\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\000\000"}, {298, "with_stmt", 0, 5, states_42, - "\000\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000"}, {299, "with_item", 0, 4, states_43, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\000"}, {300, "except_clause", 0, 5, states_44, - "\000\000\000\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000"}, {301, "suite", 0, 5, states_45, - "\004\040\040\200\000\000\000\240\340\223\160\000\000\200\020\000\000\206\120\076\200"}, + "\004\040\040\200\000\000\000\240\340\223\160\000\000\200\020\000\000\206\120\076\000\001"}, {302, "test", 0, 6, states_46, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\000"}, {303, "test_nocond", 0, 2, states_47, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\000"}, {304, "lambdef", 0, 5, states_48, - "\000\000\000\000\000\000\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000"}, {305, "lambdef_nocond", 0, 5, states_49, - "\000\000\000\000\000\000\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000"}, {306, "or_test", 0, 2, states_50, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\020\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\020\000\000\206\120\076\000\000"}, {307, "and_test", 0, 2, states_51, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\020\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\020\000\000\206\120\076\000\000"}, {308, "not_test", 0, 3, states_52, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\020\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\020\000\000\206\120\076\000\000"}, {309, "comparison", 0, 2, states_53, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000\000"}, {310, "comp_op", 0, 4, states_54, - "\000\000\000\000\000\000\000\000\000\000\000\000\002\000\220\177\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\002\000\220\177\000\000\000\000\000\000"}, {311, "star_expr", 0, 3, states_55, - "\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {312, "expr", 0, 2, states_56, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000\000"}, {313, "xor_expr", 0, 2, states_57, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000\000"}, {314, "and_expr", 0, 2, states_58, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000\000"}, {315, "shift_expr", 0, 2, states_59, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000\000"}, {316, "arith_expr", 0, 2, states_60, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000\000"}, {317, "term", 0, 2, states_61, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000\000"}, {318, "factor", 0, 3, states_62, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000\000"}, {319, "power", 0, 4, states_63, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\120\076\000\000"}, {320, "atom", 0, 9, states_64, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\120\076\000\000"}, {321, "testlist_comp", 0, 5, states_65, - "\000\040\040\200\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000"}, + "\000\040\040\200\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\000"}, {322, "trailer", 0, 7, states_66, - "\000\040\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\020\000\000"}, + "\000\040\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\020\000\000\000"}, {323, "subscriptlist", 0, 3, states_67, - "\000\040\040\002\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000"}, + "\000\040\040\002\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\000"}, {324, "subscript", 0, 5, states_68, - "\000\040\040\002\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000"}, + "\000\040\040\002\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\000"}, {325, "sliceop", 0, 3, states_69, - "\000\000\000\002\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\002\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {326, "exprlist", 0, 3, states_70, - "\000\040\040\200\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000"}, + "\000\040\040\200\000\000\000\000\000\200\000\000\000\000\000\000\000\206\120\076\000\000"}, {327, "testlist", 0, 3, states_71, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\000"}, {328, "dictorsetmaker", 0, 11, states_72, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000"}, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\000"}, {329, "classdef", 0, 8, states_73, - "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\004"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\004\000"}, {330, "arglist", 0, 8, states_74, - "\000\040\040\200\001\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000"}, - {331, "argument", 0, 4, states_75, - "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000"}, + "\000\040\040\200\001\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\001"}, + {331, "argument", 0, 5, states_75, + "\000\040\040\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\001"}, {332, "comp_iter", 0, 2, states_76, - "\000\000\000\000\000\000\000\000\000\000\000\020\001\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\020\001\000\000\000\000\000\000\000\000\000"}, {333, "comp_for", 0, 6, states_77, - "\000\000\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000"}, {334, "comp_if", 0, 4, states_78, - "\000\000\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000"}, + "\000\000\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000\000"}, {335, "encoding_decl", 0, 2, states_79, - "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + "\000\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {336, "yield_expr", 0, 3, states_80, - "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\200"}, + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001"}, + {337, "yield_arg", 0, 3, states_81, + "\000\040\040\000\000\000\000\000\000\202\000\000\000\200\020\000\000\206\120\076\000\000"}, + {338, "yield_from", 0, 4, states_82, + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001"}, }; -static label labels[168] = { +static label labels[170] = { {0, "EMPTY"}, {256, 0}, {4, 0}, @@ -2131,14 +2175,16 @@ {325, 0}, {1, "class"}, {331, 0}, + {338, 0}, {332, 0}, {334, 0}, {335, 0}, {1, "yield"}, + {337, 0}, }; grammar _PyParser_Grammar = { - 81, + 83, dfas, - {168, labels}, + {170, labels}, 256 }; diff -r 3a4b983dd70b -r 79088efb6c91 Python/opcode_targets.h --- a/Python/opcode_targets.h Sat Jul 09 11:48:50 2011 +0200 +++ b/Python/opcode_targets.h Sun Jul 10 01:09:48 2011 +1000 @@ -71,7 +71,7 @@ &&TARGET_STORE_LOCALS, &&TARGET_PRINT_EXPR, &&TARGET_LOAD_BUILD_CLASS, - &&_unknown_opcode, + &&TARGET_YIELD_FROM, &&_unknown_opcode, &&_unknown_opcode, &&TARGET_INPLACE_LSHIFT, diff -r 3a4b983dd70b -r 79088efb6c91 Python/symtable.c --- a/Python/symtable.c Sat Jul 09 11:48:50 2011 +0200 +++ b/Python/symtable.c Sun Jul 10 01:09:48 2011 +1000 @@ -1133,14 +1133,6 @@ if (s->v.Return.value) { VISIT(st, expr, s->v.Return.value); st->st_cur->ste_returns_value = 1; - if (st->st_cur->ste_generator) { - PyErr_SetString(PyExc_SyntaxError, - RETURN_VAL_IN_GENERATOR); - PyErr_SyntaxLocationEx(st->st_filename, - s->lineno, - s->col_offset); - return 0; - } } break; case Delete_kind: @@ -1342,13 +1334,6 @@ if (e->v.Yield.value) VISIT(st, expr, e->v.Yield.value); st->st_cur->ste_generator = 1; - if (st->st_cur->ste_returns_value) { - PyErr_SetString(PyExc_SyntaxError, - RETURN_VAL_IN_GENERATOR); - PyErr_SyntaxLocationEx(st->st_filename, - e->lineno, e->col_offset); - return 0; - } break; case Compare_kind: VISIT(st, expr, e->v.Compare.left);