diff --git Include/pyerrors.h Include/pyerrors.h index 1eee16d..a6d637c 100644 --- Include/pyerrors.h +++ Include/pyerrors.h @@ -111,6 +111,7 @@ PyAPI_DATA(PyObject *) PyExc_BaseException; PyAPI_DATA(PyObject *) PyExc_Exception; PyAPI_DATA(PyObject *) PyExc_StopIteration; PyAPI_DATA(PyObject *) PyExc_GeneratorExit; +PyAPI_DATA(PyObject *) PyExc_GeneratorReturn; PyAPI_DATA(PyObject *) PyExc_ArithmeticError; PyAPI_DATA(PyObject *) PyExc_LookupError; diff --git Lib/test/exception_hierarchy.txt Lib/test/exception_hierarchy.txt index 73ccb66..355b44b 100644 --- Lib/test/exception_hierarchy.txt +++ Lib/test/exception_hierarchy.txt @@ -2,6 +2,7 @@ BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit + +-- GeneratorReturn +-- Exception +-- StopIteration +-- ArithmeticError diff --git Lib/test/test_generators.py Lib/test/test_generators.py index 90af15b..eae1910 100644 --- Lib/test/test_generators.py +++ Lib/test/test_generators.py @@ -723,35 +723,19 @@ Ye olde Fibonacci generator, tee style. """ -# syntax_tests mostly provokes SyntaxErrors. Also fiddling with #if 0 -# hackery. - 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(): ... yield 1 @@ -866,20 +850,6 @@ These are fine: >>> 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 +1536,6 @@ Traceback (most recent call last): ... 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): ... @@ -1802,6 +1767,60 @@ enclosing function a generator: >>> data [27, 27] + +Test GeneratorReturn exception. + +General test on correct exception generated by a return statement: + +>>> def test(): +... yield 10 +... return 11 +... yield 12 +>>> list(test()) +Traceback (most recent call last): + ... +GeneratorReturn: 11 + + +Try..finally block test: + +>>> def test(): +... try: +... yield 1/0 +... except: +... pass +... finally: +... return 42 +>>> list(test()) +Traceback (most recent call last): + ... +GeneratorReturn: 42 + + +Complex return/yield test: + +>>> def test(): +... return (yield) +>>> gen = test() +>>> next(gen) +>>> gen.send(42) +Traceback (most recent call last): + ... +GeneratorReturn: 42 + + +Test GeneratorReturn non-catchability in generator that returned value: + +>>> def test(): +... try: +... yield +... return 100 +... except GeneratorReturn: +... pass +>>> list(test()) +Traceback (most recent call last): + ... +GeneratorReturn: 100 """ refleaks_tests = """ diff --git Objects/exceptions.c Objects/exceptions.c index b82b6ba..83ea6a4 100644 --- Objects/exceptions.c +++ Objects/exceptions.c @@ -481,6 +481,11 @@ SimpleExtendsException(PyExc_Exception, StopIteration, SimpleExtendsException(PyExc_BaseException, GeneratorExit, "Request that a generator exit."); +/* + * GeneratorReturn extends BaseException + */ +SimpleExtendsException(PyExc_BaseException, GeneratorReturn, + "A value returned from a generator."); /* * SystemExit extends BaseException @@ -1890,6 +1895,7 @@ _PyExc_Init(void) PRE_INIT(TypeError) PRE_INIT(StopIteration) PRE_INIT(GeneratorExit) + PRE_INIT(GeneratorReturn) PRE_INIT(SystemExit) PRE_INIT(KeyboardInterrupt) PRE_INIT(ImportError) @@ -1952,6 +1958,7 @@ _PyExc_Init(void) POST_INIT(TypeError) POST_INIT(StopIteration) POST_INIT(GeneratorExit) + POST_INIT(GeneratorReturn) POST_INIT(SystemExit) POST_INIT(KeyboardInterrupt) POST_INIT(ImportError) diff --git Objects/genobject.c Objects/genobject.c index 34ee7dc..c5e8793 100644 --- Objects/genobject.c +++ Objects/genobject.c @@ -92,12 +92,15 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc) /* 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 = PyObject_CallFunctionObjArgs(PyExc_GeneratorReturn, result, NULL); + PyErr_SetObject(PyExc_GeneratorReturn, 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) { diff --git Python/symtable.c Python/symtable.c index 55c9f47..5116040 100644 --- Python/symtable.c +++ Python/symtable.c @@ -19,9 +19,6 @@ #define IMPORT_STAR_WARNING "import * only allowed at module level" -#define RETURN_VAL_IN_GENERATOR \ - "'return' with argument inside generator" - static PySTEntryObject * ste_new(struct symtable *st, identifier name, _Py_block_ty block, @@ -1155,13 +1152,6 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) 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_SyntaxLocation(st->st_filename, - s->lineno); - return 0; - } } break; case Delete_kind: @@ -1364,13 +1354,6 @@ symtable_visit_expr(struct symtable *st, expr_ty e) 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_SyntaxLocation(st->st_filename, - e->lineno); - return 0; - } break; case Compare_kind: VISIT(st, expr, e->v.Compare.left);