diff -r 101919aba8d6 -r 0d1d76f68750 Doc/library/dis.rst --- a/Doc/library/dis.rst Mon Oct 31 09:05:10 2011 +0100 +++ b/Doc/library/dis.rst Wed Nov 09 20:56:37 2011 +1000 @@ -51,17 +51,21 @@ .. versionadded:: 3.2 -.. function:: show_code(x) +.. function:: show_code(x, *, file=None) Print detailed code object information for the supplied function, method, source code string or code object to stdout. - This is a convenient shorthand for ``print(code_info(x))``, intended for - interactive exploration at the interpreter prompt. + This is a convenient shorthand for ``print(code_info(x), file=file)``, + intended for interactive exploration at the interpreter prompt. .. versionadded:: 3.2 -.. function:: dis(x=None) + .. versionchanged:: 3.3 + Added ``file`` parameter + + +.. function:: dis(x=None, *, file=None) Disassemble the *x* object. *x* can denote either a module, a class, a method, a function, a code object, a string of source code or a byte sequence @@ -72,16 +76,28 @@ disassembled. If no object is provided, this function disassembles the last traceback. + The disassembly is written as text to the supplied ``file`` argument if + provided and to ``sys.stdout`` otherwise. -.. function:: distb(tb=None) + .. versionchanged:: 3.3 + Added ``file`` parameter + + +.. function:: distb(tb=None, *, file=None) Disassemble the top-of-stack function of a traceback, using the last traceback if none was passed. The instruction causing the exception is indicated. + The disassembly is written as text to the supplied ``file`` argument if + provided and to ``sys.stdout`` otherwise. -.. function:: disassemble(code, lasti=-1) - disco(code, lasti=-1) + .. versionchanged:: 3.3 + Added ``file`` parameter + + +.. function:: disassemble(code, lasti=-1, *, file=None) + disco(code, lasti=-1, *, file=None) Disassemble a code object, indicating the last instruction if *lasti* was provided. The output is divided in the following columns: @@ -97,6 +113,23 @@ The parameter interpretation recognizes local and global variable names, constant values, branch targets, and compare operators. + The disassembly is written as text to the supplied ``file`` argument if + provided and to ``sys.stdout`` otherwise. + + .. versionchanged:: 3.3 + Added ``file`` parameter + + +.. function:: get_opinfo(x) + + Return an iterator over the opcodes for the supplied function, method, + source code string or code object. + + The iterator generates a series of :class:`OpInfo` named tuples giving the + details of each opcode in the supplied code. + + .. versionadded:: 3.3 + .. function:: findlinestarts(code) @@ -133,7 +166,10 @@ .. data:: hasfree - Sequence of bytecodes that access a free variable. + Sequence of bytecodes that access a free variable (note that 'free' in + this context refers to names in the current scope that are referenced by + inner scopes or names in outer scopes that are referenced from this scope. + It does *not* include references to global or builtin scopes). .. data:: hasname @@ -166,6 +202,55 @@ Python Bytecode Instructions ---------------------------- +The :func:`get_opinfo` operation provides details of bytecode instructions +as :class:`OpInfo` instances: + +.. class:: OpInfo + + Details for a bytecode operation + + .. data:: opcode + + numeric code for operation + + + .. data:: opname + + human readable name for operation + + + .. data:: arg + + numeric argument to operation (if any), otherwise None + + + .. data:: argval + + resolved arg value (if known), otherwise same as arg + + + .. data:: argrepr + + human readable description of operation argument + + + .. data:: offset + + start index of operation within bytecode sequence + + + .. data:: starts_line + + line started by this opcode (if any), otherwise None + + + .. data:: is_jump_target + + True if other code jumps to here, otherwise False + + .. versionadded:: 3.3 + + The Python compiler currently generates the following bytecode instructions. @@ -431,6 +516,13 @@ Pops ``TOS`` and yields it from a :term:`generator`. +.. opcode:: YIELD_FROM + + Pops ``TOS`` and delegates to it as a subiterator from a :term:`generator`. + + .. versionadded:: 3.3 + + .. opcode:: IMPORT_STAR Loads all symbols not starting with ``'_'`` directly from the module TOS to the diff -r 101919aba8d6 -r 0d1d76f68750 Doc/whatsnew/3.3.rst --- a/Doc/whatsnew/3.3.rst Mon Oct 31 09:05:10 2011 +0100 +++ b/Doc/whatsnew/3.3.rst Wed Nov 09 20:56:37 2011 +1000 @@ -183,6 +183,22 @@ print("You are not allowed to read document.txt") +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, integrated into 3.3 by Renaud Blanch, Ryan +Kelly and Nick Coghlan) + + Other Language Changes ====================== diff -r 101919aba8d6 -r 0d1d76f68750 Grammar/Grammar --- a/Grammar/Grammar Mon Oct 31 09:05:10 2011 +0100 +++ b/Grammar/Grammar Wed Nov 09 20:56:37 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) [comp_for] | test '=' test # 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,5 @@ # 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 diff -r 101919aba8d6 -r 0d1d76f68750 Include/Python-ast.h --- a/Include/Python-ast.h Mon Oct 31 09:05:10 2011 +0100 +++ b/Include/Python-ast.h Wed Nov 09 20:56:37 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 101919aba8d6 -r 0d1d76f68750 Include/frameobject.h --- a/Include/frameobject.h Mon Oct 31 09:05:10 2011 +0100 +++ b/Include/frameobject.h Wed Nov 09 20:56:37 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 101919aba8d6 -r 0d1d76f68750 Include/genobject.h --- a/Include/genobject.h Mon Oct 31 09:05:10 2011 +0100 +++ b/Include/genobject.h Wed Nov 09 20:56:37 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 101919aba8d6 -r 0d1d76f68750 Include/graminit.h --- a/Include/graminit.h Mon Oct 31 09:05:10 2011 +0100 +++ b/Include/graminit.h Wed Nov 09 20:56:37 2011 +1000 @@ -81,3 +81,4 @@ #define comp_if 334 #define encoding_decl 335 #define yield_expr 336 +#define yield_arg 337 diff -r 101919aba8d6 -r 0d1d76f68750 Include/opcode.h --- a/Include/opcode.h Mon Oct 31 09:05:10 2011 +0100 +++ b/Include/opcode.h Wed Nov 09 20:56:37 2011 +1000 @@ -52,6 +52,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 101919aba8d6 -r 0d1d76f68750 Include/pyerrors.h --- a/Include/pyerrors.h Mon Oct 31 09:05:10 2011 +0100 +++ b/Include/pyerrors.h Wed Nov 09 20:56:37 2011 +1000 @@ -51,6 +51,11 @@ Py_ssize_t written; /* only for BlockingIOError, -1 otherwise */ } PyOSErrorObject; +typedef struct { + PyException_HEAD + PyObject *value; +} PyStopIterationObject; + /* Compatibility typedefs */ typedef PyOSErrorObject PyEnvironmentErrorObject; #ifdef MS_WINDOWS @@ -380,6 +385,8 @@ const char *reason /* UTF-8 encoded string */ ); +/* create a StopIteration exception with the given value */ +PyAPI_FUNC(PyObject *) PyStopIteration_Create(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 101919aba8d6 -r 0d1d76f68750 Lib/dis.py --- a/Lib/dis.py Mon Oct 31 09:05:10 2011 +0100 +++ b/Lib/dis.py Wed Nov 09 20:56:37 2011 +1000 @@ -2,6 +2,7 @@ import sys import types +import collections from opcode import * from opcode import __all__ as _opcodes_all @@ -25,7 +26,7 @@ c = compile(source, name, 'exec') return c -def dis(x=None): +def dis(x=None, *, file=None): """Disassemble classes, methods, functions, or code. With no argument, disassemble the last traceback. @@ -42,23 +43,23 @@ items = sorted(x.__dict__.items()) for name, x1 in items: if isinstance(x1, _have_code): - print("Disassembly of %s:" % name) + print("Disassembly of %s:" % name, file=file) try: dis(x1) except TypeError as msg: - print("Sorry:", msg) - print() + print("Sorry:", msg, file=file) + print(file=file) elif hasattr(x, 'co_code'): # Code object - disassemble(x) + disassemble(x, file=file) elif isinstance(x, (bytes, bytearray)): # Raw bytecode - _disassemble_bytes(x) + _disassemble_bytes(x, file=file) elif isinstance(x, str): # Source code - _disassemble_str(x) + _disassemble_str(x, file=file) else: raise TypeError("don't know how to disassemble %s objects" % type(x).__name__) -def distb(tb=None): +def distb(tb=None, *, file=None): """Disassemble a traceback (default: last traceback).""" if tb is None: try: @@ -66,7 +67,7 @@ except AttributeError: raise RuntimeError("no last traceback to disassemble") while tb.tb_next: tb = tb.tb_next - disassemble(tb.tb_frame.f_code, tb.tb_lasti) + disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file) # The inspect module interrogates this dictionary to build its # list of CO_* constants. It is also used by pretty_flags to @@ -95,19 +96,22 @@ names.append(hex(flags)) return ", ".join(names) -def code_info(x): - """Formatted details of methods, functions, or code.""" +def _get_code_object(x): + """Helper to handle methods, functions, strings and raw code objects""" if hasattr(x, '__func__'): # Method x = x.__func__ if hasattr(x, '__code__'): # Function x = x.__code__ if isinstance(x, str): # Source code - x = _try_compile(x, "") + x = _try_compile(x, "") if hasattr(x, 'co_code'): # Code object - return _format_code_info(x) - else: - raise TypeError("don't know how to disassemble %s objects" % - type(x).__name__) + return x + raise TypeError("don't know how to disassemble %s objects" % + type(x).__name__) + +def code_info(x): + """Formatted details of methods, functions, or code.""" + return _format_code_info(_get_code_object(x)) def _format_code_info(co): lines = [] @@ -140,100 +144,165 @@ lines.append("%4d: %s" % i_n) return "\n".join(lines) -def show_code(co): +def show_code(co, *, file=None): """Print details of methods, functions, or code to stdout.""" - print(code_info(co)) + print(code_info(co), file=file) -def disassemble(co, lasti=-1): - """Disassemble a code object.""" - code = co.co_code +_OpInfo = collections.namedtuple("_OpInfo", + "opcode opname arg argval argrepr offset starts_line is_jump_target") + +class OpInfo(_OpInfo): + """Details for a bytecode operation + + Defined fields: + opcode - numeric code for operation + opname - human readable name for operation + arg - numeric argument to operation (if any), otherwise None + argval - resolved arg value (if known), otherwise same as arg + argrepr - human readable description of operation argument + offset - start index of operation within bytecode sequence + starts_line - line started by this opcode (if any), otherwise None + is_jump_target - True if other code jumps to here, otherwise False + """ + +def get_opinfo(x): + """Iterator for the opcodes in methods, functions or code + + Generates a series of OpInfo namedtuples giving the details of + each opcode in the supplied code. + """ + co = _get_code_object(x) + cell_names = co.co_cellvars + co.co_freevars + linestarts = dict(findlinestarts(co)) + return _get_opinfo_bytes(co.co_code, co.co_varnames, co.co_names, + co.co_consts, cell_names, linestarts) + +def _get_arg_info(arg, info_source): + """Helper to get optional details about the operation argument + + Returns the dereferenced argval and its repr() if the info + source is defined. + Otherwise return the arg and its repr(). + """ + argval = arg + if info_source is not None: + argval = info_source[arg] + if isinstance(argval, str): + details = argval + else: + details = repr(argval) + return argval, details + + +def _get_opinfo_bytes(code, varnames=None, names=None, constants=None, + cells=None, linestarts=None): + """Iterate over the opcodes in a bytecode string. + + Generates a sequence of OpInfo namedtuples giving the details of each + opcode. Additional information about the code's runtime environment + (e.g. variable names, constants) can be specified using optional + arguments. + + """ labels = findlabels(code) - linestarts = dict(findlinestarts(co)) - n = len(code) - i = 0 extended_arg = 0 + linestart = None free = None - while i < n: - op = code[i] - if i in linestarts: - if i > 0: - print() - print("%3d" % linestarts[i], end=' ') - else: - print(' ', end=' ') - - if i == lasti: print('-->', end=' ') - else: print(' ', end=' ') - if i in labels: print('>>', end=' ') - else: print(' ', end=' ') - print(repr(i).rjust(4), end=' ') - print(opname[op].ljust(20), end=' ') - i = i+1 - if op >= HAVE_ARGUMENT: - oparg = code[i] + code[i+1]*256 + extended_arg - extended_arg = 0 - i = i+2 - if op == EXTENDED_ARG: - extended_arg = oparg*65536 - print(repr(oparg).rjust(5), end=' ') - if op in hasconst: - print('(' + repr(co.co_consts[oparg]) + ')', end=' ') - elif op in hasname: - print('(' + co.co_names[oparg] + ')', end=' ') - elif op in hasjrel: - print('(to ' + repr(i + oparg) + ')', end=' ') - elif op in haslocal: - print('(' + co.co_varnames[oparg] + ')', end=' ') - elif op in hascompare: - print('(' + cmp_op[oparg] + ')', end=' ') - elif op in hasfree: - if free is None: - free = co.co_cellvars + co.co_freevars - print('(' + free[oparg] + ')', end=' ') - print() - -def _disassemble_bytes(code, lasti=-1, varnames=None, names=None, - constants=None): - labels = findlabels(code) + # enumerate() is not an option, since we sometimes process + # multiple elements on a single pass through the loop n = len(code) i = 0 while i < n: op = code[i] - if i == lasti: print('-->', end=' ') - else: print(' ', end=' ') - if i in labels: print('>>', end=' ') - else: print(' ', end=' ') - print(repr(i).rjust(4), end=' ') - print(opname[op].ljust(15), end=' ') + offset = i + if linestarts is not None: + linestart = linestarts.get(i, None) + is_jump_target = i in labels i = i+1 + arg = None + argval = None + argrepr = '' if op >= HAVE_ARGUMENT: - oparg = code[i] + code[i+1]*256 + arg = code[i] + code[i+1]*256 + extended_arg + extended_arg = 0 i = i+2 - print(repr(oparg).rjust(5), end=' ') + if op == EXTENDED_ARG: + extended_arg = arg*65536 + # Set argval to the dereferenced value of the argument when + # availabe, and argrepr to the string representation of argval. + # _disassemble_bytes needs the string repr of the + # raw name index for LOAD_GLOBAL, LOAD_CONST, etc. + argval = arg if op in hasconst: - if constants: - print('(' + repr(constants[oparg]) + ')', end=' ') - else: - print('(%d)'%oparg, end=' ') + argval, argrepr = _get_arg_info(arg, constants) elif op in hasname: - if names is not None: - print('(' + names[oparg] + ')', end=' ') - else: - print('(%d)'%oparg, end=' ') + argval, argrepr = _get_arg_info(arg, names) elif op in hasjrel: - print('(to ' + repr(i + oparg) + ')', end=' ') + argval = i + arg + argrepr = "to " + repr(argval) elif op in haslocal: - if varnames: - print('(' + varnames[oparg] + ')', end=' ') - else: - print('(%d)' % oparg, end=' ') + argval, argrepr = _get_arg_info(arg, varnames) elif op in hascompare: - print('(' + cmp_op[oparg] + ')', end=' ') - print() + argval = cmp_op[arg] + argrepr = argval + elif op in hasfree: + argval, argrepr = _get_arg_info(arg, cells) + yield OpInfo(op, opname[op], + arg, argval, argrepr, + offset, linestart, is_jump_target) -def _disassemble_str(source): +def disassemble(co, lasti=-1, *, file=None): + """Disassemble a code object.""" + cell_names = co.co_cellvars + co.co_freevars + linestarts = dict(findlinestarts(co)) + _disassemble_bytes(co.co_code, lasti, co.co_varnames, co.co_names, + co.co_consts, cell_names, linestarts, file=file) + +def _disassemble_bytes(code, lasti=-1, varnames=None, names=None, + constants=None, cells=None, linestarts=None, + *, file=None): + for opinfo in _get_opinfo_bytes(code, varnames, names, + constants, cells, linestarts): + # Column: Source code line number + # Omitted entirely if we have no line number info + if linestarts is not None: + if opinfo.starts_line is not None: + if opinfo.offset > 0: + print(file=file) + print("%3d" % opinfo.starts_line, end=' ', file=file) + else: + print(' ', end=' ', file=file) + # Column: Current instruction indicator + if opinfo.offset == lasti: + print('-->', end=' ', file=file) + else: + print(' ', end=' ', file=file) + # Column: Jump target marker + if opinfo.is_jump_target: + print('>>', end=' ', file=file) + else: + print(' ', end=' ', file=file) + # Column: Instruction offset from start of code sequence + print(repr(opinfo.offset).rjust(4), end=' ', file=file) + # Column: Opcode name + # XXX: _disassemble_bytes historically uses a smaller field + # Will standardise this once more of the stdlib tests have + # switched over to using get_opinfo rather than dis output + if linestarts is None: + print(opinfo.opname.ljust(15), end=' ', file=file) + else: + print(opinfo.opname.ljust(20), end=' ', file=file) + # Column: Opcode argument + if opinfo.arg is not None: + print(repr(opinfo.arg).rjust(5), end=' ', file=file) + # Column: Opcode argument details + if opinfo.argrepr: + print('(' + opinfo.argrepr + ')', end=' ', file=file) + print(file=file) + +def _disassemble_str(source, *, file=None): """Compile the source string, then disassemble the code object.""" - disassemble(_try_compile(source, '')) + disassemble(_try_compile(source, ''), file=file) disco = disassemble # XXX For backwards compatibility @@ -244,19 +313,21 @@ """ labels = [] + # enumerate() is not an option, since we sometimes process + # multiple elements on a single pass through the loop n = len(code) i = 0 while i < n: op = code[i] i = i+1 if op >= HAVE_ARGUMENT: - oparg = code[i] + code[i+1]*256 + arg = code[i] + code[i+1]*256 i = i+2 label = -1 if op in hasjrel: - label = i+oparg + label = i+arg elif op in hasjabs: - label = oparg + label = arg if label >= 0: if label not in labels: labels.append(label) diff -r 101919aba8d6 -r 0d1d76f68750 Lib/opcode.py --- a/Lib/opcode.py Mon Oct 31 09:05:10 2011 +0100 +++ b/Lib/opcode.py Wed Nov 09 20:56:37 2011 +1000 @@ -87,6 +87,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 101919aba8d6 -r 0d1d76f68750 Lib/test/bytecode_helper.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/bytecode_helper.py Wed Nov 09 20:56:37 2011 +1000 @@ -0,0 +1,64 @@ +"""bytecode_helper - support tools for testing correct bytecode generation""" + +import unittest +import dis +import io + +_UNSPECIFIED = object() + +class BytecodeTestCase(unittest.TestCase): + """Custom assertion methods for inspecting bytecode.""" + + def get_disassembly_as_string(self, co): + s = io.StringIO() + dis.dis(co, file=s) + return s.getvalue() + + def assertBytecodeMatches(self, x, expected, attrs=("opname", "argval")): + for opinfo, values in zip(dis.get_opinfo(x), expected): + for attr, expected_value in zip(attrs, values): + self.assertEqual(getattr(opinfo, attr), expected_value) + + def assertBytecodeExactlyMatches(self, x, expected, *, line_offset=0): + """Throws AssertionError if any discrepancy is found in bytecode""" + actual = dis.get_opinfo(x) + for op, expected_op in zip(actual, expected): + self.assertEqual(op.opcode, expected_op.opcode) + self.assertEqual(op.opname, expected_op.opname) + self.assertEqual(op.arg, expected_op.arg) + self.assertEqual(op.argval, expected_op.argval) + self.assertEqual(op.argrepr, expected_op.argrepr) + self.assertEqual(op.offset, expected_op.offset) + if expected_op.starts_line is None: + self.assertIsNone(op.starts_line) + else: + self.assertEqual(op.starts_line, + expected_op.starts_line + line_offset) + self.assertEqual(op.is_jump_target, expected_op.is_jump_target) + + def assertInBytecode(self, x, opname, argval=_UNSPECIFIED): + """Returns opinfo if op is found, otherwise throws AssertionError""" + for opinfo in dis.get_opinfo(x): + if opinfo.opname == opname: + if argval is _UNSPECIFIED or opinfo.argval == argval: + return opinfo + disassembly = self.get_disassembly_as_string(x) + if argval is _UNSPECIFIED: + msg = '%s not found in bytecode:\n%s' % (opname, disassembly) + else: + msg = '(%s,%r) not found in bytecode:\n%s' + msg = msg % (opname, argval, disassembly) + self.fail(msg) + + def assertNotInBytecode(self, x, opname, argval=_UNSPECIFIED): + """Throws AssertionError if op is found""" + for opinfo in dis.get_opinfo(x): + if opinfo.opname == opname: + disassembly = self.get_disassembly_as_string(co) + if opargval is _UNSPECIFIED: + msg = '%s occurs in bytecode:\n%s' % (opname, disassembly) + elif opinfo.argval == argval: + msg = '(%s,%r) occurs in bytecode:\n%s' + msg = msg % (opname, argval, disassembly) + self.fail(msg) + diff -r 101919aba8d6 -r 0d1d76f68750 Lib/test/test_ast.py --- a/Lib/test/test_ast.py Mon Oct 31 09:05:10 2011 +0100 +++ b/Lib/test/test_ast.py Wed Nov 09 20:56:37 2011 +1000 @@ -802,7 +802,8 @@ self._check_comprehension(factory) def test_yield(self): - self.expr(ast.Yield(ast.Name("x", ast.Store())), "must have Load") + self.expr(ast.Yield(0, ast.Name("x", ast.Store())), "must have Load") + self.expr(ast.Yield(1, ast.Name("x", ast.Store())), "must have Load") def test_compare(self): left = ast.Name("x", ast.Load()) diff -r 101919aba8d6 -r 0d1d76f68750 Lib/test/test_dis.py --- a/Lib/test/test_dis.py Mon Oct 31 09:05:10 2011 +0100 +++ b/Lib/test/test_dis.py Wed Nov 09 20:56:37 2011 +1000 @@ -1,6 +1,7 @@ # Minimal tests for dis module from test.support import run_unittest, captured_stdout +from test.bytecode_helper import BytecodeTestCase import difflib import unittest import sys @@ -298,29 +299,16 @@ Argument count: 1 Kw-only arguments: 0 Number of locals: 1 -Stack size: 4 +Stack size: 3 Flags: OPTIMIZED, NEWLOCALS, NOFREE Constants: 0: %r - 1: '__func__' - 2: '__code__' - 3: '' - 4: 'co_code' - 5: "don't know how to disassemble %%s objects" -%sNames: - 0: hasattr - 1: __func__ - 2: __code__ - 3: isinstance - 4: str - 5: _try_compile - 6: _format_code_info - 7: TypeError - 8: type - 9: __name__ +Names: + 0: _format_code_info + 1: _get_code_object Variable names: - 0: x""" % (('Formatted details of methods, functions, or code.', ' 6: None\n') - if sys.flags.optimize < 2 else (None, '')) + 0: x""" % (('Formatted details of methods, functions, or code.',) + if sys.flags.optimize < 2 else (None,)) @staticmethod def tricky(x, y, z=True, *args, c, d, e=[], **kwds): @@ -382,7 +370,7 @@ code_info_expr_str = """\ Name: -Filename: +Filename: Argument count: 0 Kw-only arguments: 0 Number of locals: 0 @@ -395,7 +383,7 @@ code_info_simple_stmt_str = """\ Name: -Filename: +Filename: Argument count: 0 Kw-only arguments: 0 Number of locals: 0 @@ -409,7 +397,7 @@ code_info_compound_stmt_str = """\ Name: -Filename: +Filename: Argument count: 0 Kw-only arguments: 0 Number of locals: 0 @@ -443,6 +431,9 @@ with captured_stdout() as output: dis.show_code(x) self.assertRegex(output.getvalue(), expected+"\n") + output = io.StringIO() + dis.show_code(x, file=output) + self.assertRegex(output.getvalue(), expected) def test_code_info_object(self): self.assertRaises(TypeError, dis.code_info, object()) @@ -451,8 +442,245 @@ self.assertEqual(dis.pretty_flags(0), '0x0') +# Fodder for opinfo generation tests +# Editing any of these may require recalculating the expected output +def outer(a=1, b=2): + def f(c=3, d=4): + def inner(e=5, f=6): + print(a, b, c, d, e, f) + print(a, b, c, d) + return inner + print(a, b, '', 1, [], {}, "Hello world!") + return f + +def jumpy(): + # This won't actually run! + for i in range(10): + print(i) + if i < 4: + continue + if i > 6: + break + else: + print("I can haz else clause?") + while i: + print(i) + i -= 1 + if i > 6: + continue + if i < 4: + break + else: + print("Who let lolcatz into this test suite?") + try: + 1 / 0 + except ZeroDivisionError: + print("Here we go, here we go, here we go...") + else: + with i as dodgy: + print("Never reach this") + finally: + print("OK, now we're done") + +# End fodder for opinfo generation tests +# Following lines are useful to generate the expected results after editing +# print('[\n ', ',\n '.join(map(str, dis.get_opinfo(outer))), ',\n]', sep='') +# print('[\n ', ',\n '.join(map(str, dis.get_opinfo(outer()))), ',\n]', sep='') +# print('[\n ', ',\n '.join(map(str, dis.get_opinfo(outer()()))), ',\n]', sep='') +# print('[\n ', ',\n '.join(map(str, dis.get_opinfo(jumpy))), ',\n]', sep='') + +expected_outer_offset = outer.__code__.co_firstlineno - 1 +expected_jumpy_offset = jumpy.__code__.co_firstlineno - 1 +code_object_f = outer.__code__.co_consts[3] +code_object_inner = code_object_f.co_consts[3] + +OpInfo = dis.OpInfo +expected_opinfo_outer = [ + OpInfo(opcode=100, opname='LOAD_CONST', arg=1, argval=3, argrepr='3', offset=0, starts_line=2, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=2, argval=4, argrepr='4', offset=3, starts_line=None, is_jump_target=False), + OpInfo(opcode=135, opname='LOAD_CLOSURE', arg=0, argval='a', argrepr='a', offset=6, starts_line=None, is_jump_target=False), + OpInfo(opcode=135, opname='LOAD_CLOSURE', arg=1, argval='b', argrepr='b', offset=9, starts_line=None, is_jump_target=False), + OpInfo(opcode=102, opname='BUILD_TUPLE', arg=2, argval=2, argrepr='', offset=12, starts_line=None, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=3, argval=code_object_f, argrepr=repr(code_object_f), + offset=15, starts_line=None, is_jump_target=False), + OpInfo(opcode=134, opname='MAKE_CLOSURE', arg=2, argval=2, argrepr='', offset=18, starts_line=None, is_jump_target=False), + OpInfo(opcode=125, opname='STORE_FAST', arg=2, argval='f', argrepr='f', offset=21, starts_line=None, is_jump_target=False), + OpInfo(opcode=116, opname='LOAD_GLOBAL', arg=0, argval='print', argrepr='print', offset=24, starts_line=7, is_jump_target=False), + OpInfo(opcode=136, opname='LOAD_DEREF', arg=0, argval='a', argrepr='a', offset=27, starts_line=None, is_jump_target=False), + OpInfo(opcode=136, opname='LOAD_DEREF', arg=1, argval='b', argrepr='b', offset=30, starts_line=None, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=4, argval='', argrepr='', offset=33, starts_line=None, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=5, argval=1, argrepr='1', offset=36, starts_line=None, is_jump_target=False), + OpInfo(opcode=103, opname='BUILD_LIST', arg=0, argval=0, argrepr='', offset=39, starts_line=None, is_jump_target=False), + OpInfo(opcode=105, opname='BUILD_MAP', arg=0, argval=0, argrepr='', offset=42, starts_line=None, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=6, argval='Hello world!', argrepr='Hello world!', offset=45, starts_line=None, is_jump_target=False), + OpInfo(opcode=131, opname='CALL_FUNCTION', arg=7, argval=7, argrepr='', offset=48, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=51, starts_line=None, is_jump_target=False), + OpInfo(opcode=124, opname='LOAD_FAST', arg=2, argval='f', argrepr='f', offset=52, starts_line=8, is_jump_target=False), + OpInfo(opcode=83, opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=55, starts_line=None, is_jump_target=False), +] +expected_opinfo_f = [ + OpInfo(opcode=100, opname='LOAD_CONST', arg=1, argval=5, argrepr='5', offset=0, starts_line=3, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=2, argval=6, argrepr='6', offset=3, starts_line=None, is_jump_target=False), + OpInfo(opcode=135, opname='LOAD_CLOSURE', arg=2, argval='a', argrepr='a', offset=6, starts_line=None, is_jump_target=False), + OpInfo(opcode=135, opname='LOAD_CLOSURE', arg=0, argval='c', argrepr='c', offset=9, starts_line=None, is_jump_target=False), + OpInfo(opcode=135, opname='LOAD_CLOSURE', arg=3, argval='b', argrepr='b', offset=12, starts_line=None, is_jump_target=False), + OpInfo(opcode=135, opname='LOAD_CLOSURE', arg=1, argval='d', argrepr='d', offset=15, starts_line=None, is_jump_target=False), + OpInfo(opcode=102, opname='BUILD_TUPLE', arg=4, argval=4, argrepr='', offset=18, starts_line=None, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=3, argval=code_object_inner, argrepr=repr(code_object_inner), + offset=21, starts_line=None, is_jump_target=False), + OpInfo(opcode=134, opname='MAKE_CLOSURE', arg=2, argval=2, argrepr='', offset=24, starts_line=None, is_jump_target=False), + OpInfo(opcode=125, opname='STORE_FAST', arg=2, argval='inner', argrepr='inner', offset=27, starts_line=None, is_jump_target=False), + OpInfo(opcode=116, opname='LOAD_GLOBAL', arg=0, argval='print', argrepr='print', offset=30, starts_line=5, is_jump_target=False), + OpInfo(opcode=136, opname='LOAD_DEREF', arg=2, argval='a', argrepr='a', offset=33, starts_line=None, is_jump_target=False), + OpInfo(opcode=136, opname='LOAD_DEREF', arg=3, argval='b', argrepr='b', offset=36, starts_line=None, is_jump_target=False), + OpInfo(opcode=136, opname='LOAD_DEREF', arg=0, argval='c', argrepr='c', offset=39, starts_line=None, is_jump_target=False), + OpInfo(opcode=136, opname='LOAD_DEREF', arg=1, argval='d', argrepr='d', offset=42, starts_line=None, is_jump_target=False), + OpInfo(opcode=131, opname='CALL_FUNCTION', arg=4, argval=4, argrepr='', offset=45, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=48, starts_line=None, is_jump_target=False), + OpInfo(opcode=124, opname='LOAD_FAST', arg=2, argval='inner', argrepr='inner', offset=49, starts_line=6, is_jump_target=False), + OpInfo(opcode=83, opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=52, starts_line=None, is_jump_target=False), +] +expected_opinfo_inner = [ + OpInfo(opcode=116, opname='LOAD_GLOBAL', arg=0, argval='print', argrepr='print', offset=0, starts_line=4, is_jump_target=False), + OpInfo(opcode=136, opname='LOAD_DEREF', arg=0, argval='a', argrepr='a', offset=3, starts_line=None, is_jump_target=False), + OpInfo(opcode=136, opname='LOAD_DEREF', arg=2, argval='b', argrepr='b', offset=6, starts_line=None, is_jump_target=False), + OpInfo(opcode=136, opname='LOAD_DEREF', arg=1, argval='c', argrepr='c', offset=9, starts_line=None, is_jump_target=False), + OpInfo(opcode=136, opname='LOAD_DEREF', arg=3, argval='d', argrepr='d', offset=12, starts_line=None, is_jump_target=False), + OpInfo(opcode=124, opname='LOAD_FAST', arg=0, argval='e', argrepr='e', offset=15, starts_line=None, is_jump_target=False), + OpInfo(opcode=124, opname='LOAD_FAST', arg=1, argval='f', argrepr='f', offset=18, starts_line=None, is_jump_target=False), + OpInfo(opcode=131, opname='CALL_FUNCTION', arg=6, argval=6, argrepr='', offset=21, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=24, starts_line=None, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=0, argval=None, argrepr='None', offset=25, starts_line=None, is_jump_target=False), + OpInfo(opcode=83, opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=28, starts_line=None, is_jump_target=False), +] + +expected_opinfo_jumpy = [ + OpInfo(opcode=120, opname='SETUP_LOOP', arg=74, argval=77, argrepr='to 77', offset=0, starts_line=3, is_jump_target=False), + OpInfo(opcode=116, opname='LOAD_GLOBAL', arg=0, argval='range', argrepr='range', offset=3, starts_line=None, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=1, argval=10, argrepr='10', offset=6, starts_line=None, is_jump_target=False), + OpInfo(opcode=131, opname='CALL_FUNCTION', arg=1, argval=1, argrepr='', offset=9, starts_line=None, is_jump_target=False), + OpInfo(opcode=68, opname='GET_ITER', arg=None, argval=None, argrepr='', offset=12, starts_line=None, is_jump_target=False), + OpInfo(opcode=93, opname='FOR_ITER', arg=50, argval=66, argrepr='to 66', offset=13, starts_line=None, is_jump_target=True), + OpInfo(opcode=125, opname='STORE_FAST', arg=0, argval='i', argrepr='i', offset=16, starts_line=None, is_jump_target=False), + OpInfo(opcode=116, opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print', offset=19, starts_line=4, is_jump_target=False), + OpInfo(opcode=124, opname='LOAD_FAST', arg=0, argval='i', argrepr='i', offset=22, starts_line=None, is_jump_target=False), + OpInfo(opcode=131, opname='CALL_FUNCTION', arg=1, argval=1, argrepr='', offset=25, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=28, starts_line=None, is_jump_target=False), + OpInfo(opcode=124, opname='LOAD_FAST', arg=0, argval='i', argrepr='i', offset=29, starts_line=5, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=2, argval=4, argrepr='4', offset=32, starts_line=None, is_jump_target=False), + OpInfo(opcode=107, opname='COMPARE_OP', arg=0, argval='<', argrepr='<', offset=35, starts_line=None, is_jump_target=False), + OpInfo(opcode=114, opname='POP_JUMP_IF_FALSE', arg=47, argval=47, argrepr='', offset=38, starts_line=None, is_jump_target=False), + OpInfo(opcode=113, opname='JUMP_ABSOLUTE', arg=13, argval=13, argrepr='', offset=41, starts_line=6, is_jump_target=False), + OpInfo(opcode=110, opname='JUMP_FORWARD', arg=0, argval=47, argrepr='to 47', offset=44, starts_line=None, is_jump_target=False), + OpInfo(opcode=124, opname='LOAD_FAST', arg=0, argval='i', argrepr='i', offset=47, starts_line=7, is_jump_target=True), + OpInfo(opcode=100, opname='LOAD_CONST', arg=3, argval=6, argrepr='6', offset=50, starts_line=None, is_jump_target=False), + OpInfo(opcode=107, opname='COMPARE_OP', arg=4, argval='>', argrepr='>', offset=53, starts_line=None, is_jump_target=False), + OpInfo(opcode=114, opname='POP_JUMP_IF_FALSE', arg=13, argval=13, argrepr='', offset=56, starts_line=None, is_jump_target=False), + OpInfo(opcode=80, opname='BREAK_LOOP', arg=None, argval=None, argrepr='', offset=59, starts_line=8, is_jump_target=False), + OpInfo(opcode=113, opname='JUMP_ABSOLUTE', arg=13, argval=13, argrepr='', offset=60, starts_line=None, is_jump_target=False), + OpInfo(opcode=113, opname='JUMP_ABSOLUTE', arg=13, argval=13, argrepr='', offset=63, starts_line=None, is_jump_target=False), + OpInfo(opcode=87, opname='POP_BLOCK', arg=None, argval=None, argrepr='', offset=66, starts_line=None, is_jump_target=True), + OpInfo(opcode=116, opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print', offset=67, starts_line=10, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=4, argval='I can haz else clause?', argrepr='I can haz else clause?', offset=70, starts_line=None, is_jump_target=False), + OpInfo(opcode=131, opname='CALL_FUNCTION', arg=1, argval=1, argrepr='', offset=73, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=76, starts_line=None, is_jump_target=False), + OpInfo(opcode=120, opname='SETUP_LOOP', arg=74, argval=154, argrepr='to 154', offset=77, starts_line=11, is_jump_target=True), + OpInfo(opcode=124, opname='LOAD_FAST', arg=0, argval='i', argrepr='i', offset=80, starts_line=None, is_jump_target=True), + OpInfo(opcode=114, opname='POP_JUMP_IF_FALSE', arg=143, argval=143, argrepr='', offset=83, starts_line=None, is_jump_target=False), + OpInfo(opcode=116, opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print', offset=86, starts_line=12, is_jump_target=False), + OpInfo(opcode=124, opname='LOAD_FAST', arg=0, argval='i', argrepr='i', offset=89, starts_line=None, is_jump_target=False), + OpInfo(opcode=131, opname='CALL_FUNCTION', arg=1, argval=1, argrepr='', offset=92, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=95, starts_line=None, is_jump_target=False), + OpInfo(opcode=124, opname='LOAD_FAST', arg=0, argval='i', argrepr='i', offset=96, starts_line=13, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=5, argval=1, argrepr='1', offset=99, starts_line=None, is_jump_target=False), + OpInfo(opcode=56, opname='INPLACE_SUBTRACT', arg=None, argval=None, argrepr='', offset=102, starts_line=None, is_jump_target=False), + OpInfo(opcode=125, opname='STORE_FAST', arg=0, argval='i', argrepr='i', offset=103, starts_line=None, is_jump_target=False), + OpInfo(opcode=124, opname='LOAD_FAST', arg=0, argval='i', argrepr='i', offset=106, starts_line=14, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=3, argval=6, argrepr='6', offset=109, starts_line=None, is_jump_target=False), + OpInfo(opcode=107, opname='COMPARE_OP', arg=4, argval='>', argrepr='>', offset=112, starts_line=None, is_jump_target=False), + OpInfo(opcode=114, opname='POP_JUMP_IF_FALSE', arg=124, argval=124, argrepr='', offset=115, starts_line=None, is_jump_target=False), + OpInfo(opcode=113, opname='JUMP_ABSOLUTE', arg=80, argval=80, argrepr='', offset=118, starts_line=15, is_jump_target=False), + OpInfo(opcode=110, opname='JUMP_FORWARD', arg=0, argval=124, argrepr='to 124', offset=121, starts_line=None, is_jump_target=False), + OpInfo(opcode=124, opname='LOAD_FAST', arg=0, argval='i', argrepr='i', offset=124, starts_line=16, is_jump_target=True), + OpInfo(opcode=100, opname='LOAD_CONST', arg=2, argval=4, argrepr='4', offset=127, starts_line=None, is_jump_target=False), + OpInfo(opcode=107, opname='COMPARE_OP', arg=0, argval='<', argrepr='<', offset=130, starts_line=None, is_jump_target=False), + OpInfo(opcode=114, opname='POP_JUMP_IF_FALSE', arg=80, argval=80, argrepr='', offset=133, starts_line=None, is_jump_target=False), + OpInfo(opcode=80, opname='BREAK_LOOP', arg=None, argval=None, argrepr='', offset=136, starts_line=17, is_jump_target=False), + OpInfo(opcode=113, opname='JUMP_ABSOLUTE', arg=80, argval=80, argrepr='', offset=137, starts_line=None, is_jump_target=False), + OpInfo(opcode=113, opname='JUMP_ABSOLUTE', arg=80, argval=80, argrepr='', offset=140, starts_line=None, is_jump_target=False), + OpInfo(opcode=87, opname='POP_BLOCK', arg=None, argval=None, argrepr='', offset=143, starts_line=None, is_jump_target=True), + OpInfo(opcode=116, opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print', offset=144, starts_line=19, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=6, argval='Who let lolcatz into this test suite?', argrepr='Who let lolcatz into this test suite?', offset=147, starts_line=None, is_jump_target=False), + OpInfo(opcode=131, opname='CALL_FUNCTION', arg=1, argval=1, argrepr='', offset=150, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=153, starts_line=None, is_jump_target=False), + OpInfo(opcode=122, opname='SETUP_FINALLY', arg=72, argval=229, argrepr='to 229', offset=154, starts_line=20, is_jump_target=True), + OpInfo(opcode=121, opname='SETUP_EXCEPT', arg=12, argval=172, argrepr='to 172', offset=157, starts_line=None, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=5, argval=1, argrepr='1', offset=160, starts_line=21, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=7, argval=0, argrepr='0', offset=163, starts_line=None, is_jump_target=False), + OpInfo(opcode=27, opname='BINARY_TRUE_DIVIDE', arg=None, argval=None, argrepr='', offset=166, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=167, starts_line=None, is_jump_target=False), + OpInfo(opcode=87, opname='POP_BLOCK', arg=None, argval=None, argrepr='', offset=168, starts_line=None, is_jump_target=False), + OpInfo(opcode=110, opname='JUMP_FORWARD', arg=28, argval=200, argrepr='to 200', offset=169, starts_line=None, is_jump_target=False), + OpInfo(opcode=4, opname='DUP_TOP', arg=None, argval=None, argrepr='', offset=172, starts_line=22, is_jump_target=True), + OpInfo(opcode=116, opname='LOAD_GLOBAL', arg=2, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=173, starts_line=None, is_jump_target=False), + OpInfo(opcode=107, opname='COMPARE_OP', arg=10, argval='exception match', argrepr='exception match', offset=176, starts_line=None, is_jump_target=False), + OpInfo(opcode=114, opname='POP_JUMP_IF_FALSE', arg=199, argval=199, argrepr='', offset=179, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=182, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=183, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=184, starts_line=None, is_jump_target=False), + OpInfo(opcode=116, opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print', offset=185, starts_line=23, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=8, argval='Here we go, here we go, here we go...', argrepr='Here we go, here we go, here we go...', offset=188, starts_line=None, is_jump_target=False), + OpInfo(opcode=131, opname='CALL_FUNCTION', arg=1, argval=1, argrepr='', offset=191, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=194, starts_line=None, is_jump_target=False), + OpInfo(opcode=89, opname='POP_EXCEPT', arg=None, argval=None, argrepr='', offset=195, starts_line=None, is_jump_target=False), + OpInfo(opcode=110, opname='JUMP_FORWARD', arg=26, argval=225, argrepr='to 225', offset=196, starts_line=None, is_jump_target=False), + OpInfo(opcode=88, opname='END_FINALLY', arg=None, argval=None, argrepr='', offset=199, starts_line=None, is_jump_target=True), + OpInfo(opcode=124, opname='LOAD_FAST', arg=0, argval='i', argrepr='i', offset=200, starts_line=25, is_jump_target=True), + OpInfo(opcode=143, opname='SETUP_WITH', arg=17, argval=223, argrepr='to 223', offset=203, starts_line=None, is_jump_target=False), + OpInfo(opcode=125, opname='STORE_FAST', arg=1, argval='dodgy', argrepr='dodgy', offset=206, starts_line=None, is_jump_target=False), + OpInfo(opcode=116, opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print', offset=209, starts_line=26, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=9, argval='Never reach this', argrepr='Never reach this', offset=212, starts_line=None, is_jump_target=False), + OpInfo(opcode=131, opname='CALL_FUNCTION', arg=1, argval=1, argrepr='', offset=215, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=218, starts_line=None, is_jump_target=False), + OpInfo(opcode=87, opname='POP_BLOCK', arg=None, argval=None, argrepr='', offset=219, starts_line=None, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=0, argval=None, argrepr='None', offset=220, starts_line=None, is_jump_target=False), + OpInfo(opcode=81, opname='WITH_CLEANUP', arg=None, argval=None, argrepr='', offset=223, starts_line=None, is_jump_target=True), + OpInfo(opcode=88, opname='END_FINALLY', arg=None, argval=None, argrepr='', offset=224, starts_line=None, is_jump_target=False), + OpInfo(opcode=87, opname='POP_BLOCK', arg=None, argval=None, argrepr='', offset=225, starts_line=None, is_jump_target=True), + OpInfo(opcode=100, opname='LOAD_CONST', arg=0, argval=None, argrepr='None', offset=226, starts_line=None, is_jump_target=False), + OpInfo(opcode=116, opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print', offset=229, starts_line=28, is_jump_target=True), + OpInfo(opcode=100, opname='LOAD_CONST', arg=10, argval="OK, now we're done", argrepr="OK, now we're done", offset=232, starts_line=None, is_jump_target=False), + OpInfo(opcode=131, opname='CALL_FUNCTION', arg=1, argval=1, argrepr='', offset=235, starts_line=None, is_jump_target=False), + OpInfo(opcode=1, opname='POP_TOP', arg=None, argval=None, argrepr='', offset=238, starts_line=None, is_jump_target=False), + OpInfo(opcode=88, opname='END_FINALLY', arg=None, argval=None, argrepr='', offset=239, starts_line=None, is_jump_target=False), + OpInfo(opcode=100, opname='LOAD_CONST', arg=0, argval=None, argrepr='None', offset=240, starts_line=None, is_jump_target=False), + OpInfo(opcode=83, opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=243, starts_line=None, is_jump_target=False), +] + +class OpInfoTests(BytecodeTestCase): + def test_outer(self): + self.assertBytecodeExactlyMatches(outer, expected_opinfo_outer, + line_offset = expected_outer_offset) + + def test_nested(self): + with captured_stdout(): + f = outer() + self.assertBytecodeExactlyMatches(f, expected_opinfo_f, + line_offset = expected_outer_offset) + + def test_doubly_nested(self): + with captured_stdout(): + inner = outer()() + self.assertBytecodeExactlyMatches(inner, expected_opinfo_inner, + line_offset = expected_outer_offset) + + def test_jumpy(self): + self.assertBytecodeExactlyMatches(jumpy, expected_opinfo_jumpy, + line_offset = expected_jumpy_offset) + + def test_main(): - run_unittest(DisTests, CodeInfoTests) + run_unittest(DisTests, CodeInfoTests, OpInfoTests) if __name__ == "__main__": test_main() diff -r 101919aba8d6 -r 0d1d76f68750 Lib/test/test_generators.py --- a/Lib/test/test_generators.py Mon Oct 31 09:05:10 2011 +0100 +++ b/Lib/test/test_generators.py Wed Nov 09 20:56:37 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 101919aba8d6 -r 0d1d76f68750 Lib/test/test_grammar.py --- a/Lib/test/test_grammar.py Mon Oct 31 09:05:10 2011 +0100 +++ b/Lib/test/test_grammar.py Wed Nov 09 20:56:37 2011 +1000 @@ -458,7 +458,39 @@ check_syntax_error(self, "class foo:return 1") def test_yield(self): + # Allowed as standalone statement + def g(): yield 1 + def g(): yield from () + # Allowed as RHS of assignment + def g(): x = yield 1 + def g(): x = yield from () + # Ordinary yield accepts implicit tuples + def g(): yield 1, 1 + def g(): x = yield 1, 1 + # 'yield from' does not + check_syntax_error(self, "def g(): yield from (), 1") + check_syntax_error(self, "def g(): x = yield from (), 1") + # Requires parentheses as subexpression + def g(): 1, (yield 1) + def g(): 1, (yield from ()) + check_syntax_error(self, "def g(): 1, yield 1") + check_syntax_error(self, "def g(): 1, yield from ()") + # Requires parentheses as call argument + def g(): f((yield 1)) + def g(): f((yield 1), 1) + def g(): f((yield from ())) + def g(): f((yield from ()), 1) + check_syntax_error(self, "def g(): f(yield 1)") + check_syntax_error(self, "def g(): f(yield 1, 1)") + check_syntax_error(self, "def g(): f(yield from ())") + check_syntax_error(self, "def g(): f(yield from (), 1)") + # Not allowed at top level + check_syntax_error(self, "yield") + check_syntax_error(self, "yield from") + # Not allowed at class scope check_syntax_error(self, "class foo:yield 1") + check_syntax_error(self, "class foo:yield from ()") + def test_raise(self): # 'raise' test [',' test] diff -r 101919aba8d6 -r 0d1d76f68750 Lib/test/test_parser.py --- a/Lib/test/test_parser.py Mon Oct 31 09:05:10 2011 +0100 +++ b/Lib/test/test_parser.py Wed Nov 09 20:56:37 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") @@ -621,7 +625,6 @@ with self.assertRaises(TypeError): parser.expr("a", "b") - def test_main(): support.run_unittest( RoundtripLegalSyntaxTestCase, diff -r 101919aba8d6 -r 0d1d76f68750 Lib/test/test_peepholer.py --- a/Lib/test/test_peepholer.py Mon Oct 31 09:05:10 2011 +0100 +++ b/Lib/test/test_peepholer.py Wed Nov 09 20:56:37 2011 +1000 @@ -5,44 +5,28 @@ import unittest from math import copysign -def disassemble(func): - f = StringIO() - tmp = sys.stdout - sys.stdout = f - try: - dis.dis(func) - finally: - sys.stdout = tmp - result = f.getvalue() - f.close() - return result +from test.bytecode_helper import BytecodeTestCase -def dis_single(line): - return disassemble(compile(line, '', 'single')) - - -class TestTranforms(unittest.TestCase): +class TestTranforms(BytecodeTestCase): def test_unot(self): # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE' def unot(x): if not x == 2: del x - asm = disassemble(unot) - for elem in ('UNARY_NOT', 'POP_JUMP_IF_FALSE'): - self.assertNotIn(elem, asm) - for elem in ('POP_JUMP_IF_TRUE',): - self.assertIn(elem, asm) + self.assertNotInBytecode(unot, 'UNARY_NOT') + self.assertNotInBytecode(unot, 'POP_JUMP_IF_FALSE') + self.assertInBytecode(unot, 'POP_JUMP_IF_TRUE') def test_elim_inversion_of_is_or_in(self): - for line, elem in ( - ('not a is b', '(is not)',), - ('not a in b', '(not in)',), - ('not a is not b', '(is)',), - ('not a not in b', '(in)',), + for line, cmp_op in ( + ('not a is b', 'is not',), + ('not a in b', 'not in',), + ('not a is not b', 'is',), + ('not a not in b', 'in',), ): - asm = dis_single(line) - self.assertIn(elem, asm) + code = compile(line, '', 'single') + self.assertInBytecode(code, 'COMPARE_OP', cmp_op) def test_global_as_constant(self): # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False @@ -56,17 +40,14 @@ def h(x): False return x - for func, name in ((f, 'None'), (g, 'True'), (h, 'False')): - asm = disassemble(func) - for elem in ('LOAD_GLOBAL',): - self.assertNotIn(elem, asm) - for elem in ('LOAD_CONST', '('+name+')'): - self.assertIn(elem, asm) + for func, elem in ((f, None), (g, True), (h, False)): + self.assertNotInBytecode(func, 'LOAD_GLOBAL') + self.assertInBytecode(func, 'LOAD_CONST', elem) def f(): 'Adding a docstring made this test fail in Py2.5.0' return None - self.assertIn('LOAD_CONST', disassemble(f)) - self.assertNotIn('LOAD_GLOBAL', disassemble(f)) + self.assertNotInBytecode(f, 'LOAD_GLOBAL') + self.assertInBytecode(f, 'LOAD_CONST', None) def test_while_one(self): # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx @@ -74,11 +55,10 @@ while 1: pass return list - asm = disassemble(f) for elem in ('LOAD_CONST', 'POP_JUMP_IF_FALSE'): - self.assertNotIn(elem, asm) + self.assertNotInBytecode(f, elem) for elem in ('JUMP_ABSOLUTE',): - self.assertIn(elem, asm) + self.assertInBytecode(f, elem) def test_pack_unpack(self): for line, elem in ( @@ -86,28 +66,30 @@ ('a, b = a, b', 'ROT_TWO',), ('a, b, c = a, b, c', 'ROT_THREE',), ): - asm = dis_single(line) - self.assertIn(elem, asm) - self.assertNotIn('BUILD_TUPLE', asm) - self.assertNotIn('UNPACK_TUPLE', asm) + code = compile(line,'','single') + self.assertInBytecode(code, elem) + self.assertNotInBytecode(code, 'BUILD_TUPLE') + self.assertNotInBytecode(code, 'UNPACK_TUPLE') def test_folding_of_tuples_of_constants(self): for line, elem in ( - ('a = 1,2,3', '((1, 2, 3))'), - ('("a","b","c")', "(('a', 'b', 'c'))"), - ('a,b,c = 1,2,3', '((1, 2, 3))'), - ('(None, 1, None)', '((None, 1, None))'), - ('((1, 2), 3, 4)', '(((1, 2), 3, 4))'), + ('a = 1,2,3', (1, 2, 3)), + ('("a","b","c")', ('a', 'b', 'c')), + ('a,b,c = 1,2,3', (1, 2, 3)), + ('(None, 1, None)', (None, 1, None)), + ('((1, 2), 3, 4)', ((1, 2), 3, 4)), ): - asm = dis_single(line) - self.assertIn(elem, asm) - self.assertNotIn('BUILD_TUPLE', asm) + code = compile(line,'','single') + self.assertInBytecode(code, 'LOAD_CONST', elem) + self.assertNotInBytecode(code, 'BUILD_TUPLE') # Long tuples should be folded too. - asm = dis_single(repr(tuple(range(10000)))) + code = compile(repr(tuple(range(10000))),'','single') + self.assertNotInBytecode(code, 'BUILD_TUPLE') # One LOAD_CONST for the tuple, one for the None return value - self.assertEqual(asm.count('LOAD_CONST'), 2) - self.assertNotIn('BUILD_TUPLE', asm) + load_consts = [opinfo for opinfo in dis.get_opinfo(code) + if opinfo.opname == 'LOAD_CONST'] + self.assertEqual(len(load_consts), 2) # Bug 1053819: Tuple of constants misidentified when presented with: # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . . @@ -129,14 +111,14 @@ def test_folding_of_lists_of_constants(self): for line, elem in ( # in/not in constants with BUILD_LIST should be folded to a tuple: - ('a in [1,2,3]', '(1, 2, 3)'), - ('a not in ["a","b","c"]', "(('a', 'b', 'c'))"), - ('a in [None, 1, None]', '((None, 1, None))'), - ('a not in [(1, 2), 3, 4]', '(((1, 2), 3, 4))'), + ('a in [1,2,3]', (1, 2, 3)), + ('a not in ["a","b","c"]', ('a', 'b', 'c')), + ('a in [None, 1, None]', (None, 1, None)), + ('a not in [(1, 2), 3, 4]', ((1, 2), 3, 4)), ): - asm = dis_single(line) - self.assertIn(elem, asm) - self.assertNotIn('BUILD_LIST', asm) + code = compile(line, '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', elem) + self.assertNotInBytecode(code, 'BUILD_LIST') def test_folding_of_sets_of_constants(self): for line, elem in ( @@ -147,18 +129,9 @@ ('a not in {(1, 2), 3, 4}', frozenset({(1, 2), 3, 4})), ('a in {1, 2, 3, 3, 2, 1}', frozenset({1, 2, 3})), ): - asm = dis_single(line) - self.assertNotIn('BUILD_SET', asm) - - # Verify that the frozenset 'elem' is in the disassembly - # The ordering of the elements in repr( frozenset ) isn't - # guaranteed, so we jump through some hoops to ensure that we have - # the frozenset we expect: - self.assertIn('frozenset', asm) - # Extract the frozenset literal from the disassembly: - m = re.match(r'.*(frozenset\({.*}\)).*', asm, re.DOTALL) - self.assertTrue(m) - self.assertEqual(eval(m.group(1)), elem) + code = compile(line, '', 'single') + self.assertNotInBytecode(code, 'BUILD_SET') + self.assertInBytecode(code, 'LOAD_CONST', elem) # Ensure that the resulting code actually works: def f(a): @@ -176,95 +149,104 @@ def test_folding_of_binops_on_constants(self): for line, elem in ( - ('a = 2+3+4', '(9)'), # chained fold - ('"@"*4', "('@@@@')"), # check string ops - ('a="abc" + "def"', "('abcdef')"), # check string ops - ('a = 3**4', '(81)'), # binary power - ('a = 3*4', '(12)'), # binary multiply - ('a = 13//4', '(3)'), # binary floor divide - ('a = 14%4', '(2)'), # binary modulo - ('a = 2+3', '(5)'), # binary add - ('a = 13-4', '(9)'), # binary subtract - ('a = (12,13)[1]', '(13)'), # binary subscr - ('a = 13 << 2', '(52)'), # binary lshift - ('a = 13 >> 2', '(3)'), # binary rshift - ('a = 13 & 7', '(5)'), # binary and - ('a = 13 ^ 7', '(10)'), # binary xor - ('a = 13 | 7', '(15)'), # binary or + ('a = 2+3+4', 9), # chained fold + ('"@"*4', '@@@@'), # check string ops + ('a="abc" + "def"', 'abcdef'), # check string ops + ('a = 3**4', 81), # binary power + ('a = 3*4', 12), # binary multiply + ('a = 13//4', 3), # binary floor divide + ('a = 14%4', 2), # binary modulo + ('a = 2+3', 5), # binary add + ('a = 13-4', 9), # binary subtract + ('a = (12,13)[1]', 13), # binary subscr + ('a = 13 << 2', 52), # binary lshift + ('a = 13 >> 2', 3), # binary rshift + ('a = 13 & 7', 5), # binary and + ('a = 13 ^ 7', 10), # binary xor + ('a = 13 | 7', 15), # binary or ): - asm = dis_single(line) - self.assertIn(elem, asm, asm) - self.assertNotIn('BINARY_', asm) + code = compile(line, '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', elem) + for opinfo in dis.get_opinfo(code): + self.assertFalse(opinfo.opname.startswith('BINARY_')) # Verify that unfoldables are skipped - asm = dis_single('a=2+"b"') - self.assertIn('(2)', asm) - self.assertIn("('b')", asm) + code = compile('a=2+"b"', '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', 2) + self.assertInBytecode(code, 'LOAD_CONST', 'b') # Verify that large sequences do not result from folding - asm = dis_single('a="x"*1000') - self.assertIn('(1000)', asm) + code = compile('a="x"*1000', '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', 1000) def test_binary_subscr_on_unicode(self): # valid code get optimized - asm = dis_single('"foo"[0]') - self.assertIn("('f')", asm) - self.assertNotIn('BINARY_SUBSCR', asm) - asm = dis_single('"\u0061\uffff"[1]') - self.assertIn("('\\uffff')", asm) - self.assertNotIn('BINARY_SUBSCR', asm) + code = compile('"foo"[0]', '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', 'f') + self.assertNotInBytecode(code, 'BINARY_SUBSCR') + code = compile('"\u0061\uffff"[1]', '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', '\uffff') + self.assertNotInBytecode(code,'BINARY_SUBSCR') + + # With PEP 393, non-BMP char get optimized + code = compile('"\U00012345"[0]', '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', '\U00012345') + self.assertNotInBytecode(code, 'BINARY_SUBSCR') # invalid code doesn't get optimized # out of range - asm = dis_single('"fuu"[10]') - self.assertIn('BINARY_SUBSCR', asm) + code = compile('"fuu"[10]', '', 'single') + self.assertInBytecode(code, 'BINARY_SUBSCR') def test_folding_of_unaryops_on_constants(self): for line, elem in ( - ('-0.5', '(-0.5)'), # unary negative - ('-0.0', '(-0.0)'), # -0.0 - ('-(1.0-1.0)','(-0.0)'), # -0.0 after folding - ('-0', '(0)'), # -0 - ('~-2', '(1)'), # unary invert - ('+1', '(1)'), # unary positive + ('-0.5', -0.5), # unary negative + ('-0.0', -0.0), # -0.0 + ('-(1.0-1.0)',-0.0), # -0.0 after folding + ('-0', 0), # -0 + ('~-2', 1), # unary invert + ('+1', 1), # unary positive ): - asm = dis_single(line) - self.assertIn(elem, asm, asm) - self.assertNotIn('UNARY_', asm) + code = compile(line, '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', elem) + for opinfo in dis.get_opinfo(code): + self.assertFalse(opinfo.opname.startswith('UNARY_')) # Check that -0.0 works after marshaling def negzero(): return -(1.0-1.0) - self.assertNotIn('UNARY_', disassemble(negzero)) + for opinfo in dis.get_opinfo(code): + self.assertFalse(opinfo.opname.startswith('UNARY_')) self.assertTrue(copysign(1.0, negzero()) < 0) # Verify that unfoldables are skipped - for line, elem in ( - ('-"abc"', "('abc')"), # unary negative - ('~"abc"', "('abc')"), # unary invert + for line, elem, opname in ( + ('-"abc"', 'abc', 'UNARY_NEGATIVE'), + ('~"abc"', 'abc', 'UNARY_INVERT'), ): - asm = dis_single(line) - self.assertIn(elem, asm, asm) - self.assertIn('UNARY_', asm) + code = compile(line, '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', elem) + self.assertInBytecode(code, opname) def test_elim_extra_return(self): # RETURN LOAD_CONST None RETURN --> RETURN def f(x): return x - asm = disassemble(f) - self.assertNotIn('LOAD_CONST', asm) - self.assertNotIn('(None)', asm) - self.assertEqual(asm.split().count('RETURN_VALUE'), 1) + self.assertNotInBytecode(f, 'LOAD_CONST', None) + returns = [opinfo for opinfo in dis.get_opinfo(f) + if opinfo.opname == 'RETURN_VALUE'] + self.assertEqual(len(returns), 1) def test_elim_jump_to_return(self): # JUMP_FORWARD to RETURN --> RETURN def f(cond, true_value, false_value): return true_value if cond else false_value - asm = disassemble(f) - self.assertNotIn('JUMP_FORWARD', asm) - self.assertNotIn('JUMP_ABSOLUTE', asm) - self.assertEqual(asm.split().count('RETURN_VALUE'), 2) + self.assertNotInBytecode(f, 'JUMP_FORWARD') + self.assertNotInBytecode(f, 'JUMP_ABSOLUTE') + returns = [opinfo for opinfo in dis.get_opinfo(f) + if opinfo.opname == 'RETURN_VALUE'] + self.assertEqual(len(returns), 2) def test_elim_jump_after_return1(self): # Eliminate dead code: jumps immediately after returns can't be reached @@ -277,48 +259,53 @@ if cond1: return 4 return 5 return 6 - asm = disassemble(f) - self.assertNotIn('JUMP_FORWARD', asm) - self.assertNotIn('JUMP_ABSOLUTE', asm) - self.assertEqual(asm.split().count('RETURN_VALUE'), 6) + self.assertNotInBytecode(f, 'JUMP_FORWARD') + self.assertNotInBytecode(f, 'JUMP_ABSOLUTE') + returns = [opinfo for opinfo in dis.get_opinfo(f) + if opinfo.opname == 'RETURN_VALUE'] + self.assertEqual(len(returns), 6) def test_elim_jump_after_return2(self): # Eliminate dead code: jumps immediately after returns can't be reached def f(cond1, cond2): while 1: if cond1: return 4 - asm = disassemble(f) - self.assertNotIn('JUMP_FORWARD', asm) + self.assertNotInBytecode(f, 'JUMP_FORWARD') # There should be one jump for the while loop. - self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1) - self.assertEqual(asm.split().count('RETURN_VALUE'), 2) + returns = [opinfo for opinfo in dis.get_opinfo(f) + if opinfo.opname == 'JUMP_ABSOLUTE'] + self.assertEqual(len(returns), 1) + returns = [opinfo for opinfo in dis.get_opinfo(f) + if opinfo.opname == 'RETURN_VALUE'] + self.assertEqual(len(returns), 2) def test_make_function_doesnt_bail(self): def f(): def g()->1+1: pass return g - asm = disassemble(f) - self.assertNotIn('BINARY_ADD', asm) + self.assertNotInBytecode(f, 'BINARY_ADD') def test_constant_folding(self): # Issue #11244: aggressive constant folding. exprs = [ - "3 * -5", - "-3 * 5", - "2 * (3 * 4)", - "(2 * 3) * 4", - "(-1, 2, 3)", - "(1, -2, 3)", - "(1, 2, -3)", - "(1, 2, -3) * 6", - "lambda x: x in {(3 * -5) + (-1 - 6), (1, -2, 3) * 2, None}", + '3 * -5', + '-3 * 5', + '2 * (3 * 4)', + '(2 * 3) * 4', + '(-1, 2, 3)', + '(1, -2, 3)', + '(1, 2, -3)', + '(1, 2, -3) * 6', + 'lambda x: x in {(3 * -5) + (-1 - 6), (1, -2, 3) * 2, None}', ] for e in exprs: - asm = dis_single(e) - self.assertNotIn('UNARY_', asm, e) - self.assertNotIn('BINARY_', asm, e) - self.assertNotIn('BUILD_', asm, e) + code = compile(e, '', 'single') + for opinfo in dis.get_opinfo(code): + self.assertFalse(opinfo.opname.startswith('UNARY_')) + self.assertFalse(opinfo.opname.startswith('BINARY_')) + self.assertFalse(opinfo.opname.startswith('BUILD_')) + class TestBuglets(unittest.TestCase): @@ -340,7 +327,7 @@ support.run_unittest(*test_classes) # verify reference counting - if verbose and hasattr(sys, "gettotalrefcount"): + if verbose and hasattr(sys, 'gettotalrefcount'): import gc counts = [None] * 5 for i in range(len(counts)): @@ -349,5 +336,5 @@ counts[i] = sys.gettotalrefcount() print(counts) -if __name__ == "__main__": +if __name__ == '__main__': test_main(verbose=True) diff -r 101919aba8d6 -r 0d1d76f68750 Lib/test/test_pep380.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_pep380.py Wed Nov 09 20:56:37 2011 +1000 @@ -0,0 +1,904 @@ +# -*- 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 +import parser + +from test.bytecode_helper import BytecodeTestCase +from test.support import captured_stderr + + +class PEP380Test(unittest.TestCase): + pass + + +class TestPEP380Bytecode(BytecodeTestCase): + """ + Test code generation + """ + + def test_ordinary_yield(self): + def g(): + yield + yield 42 + self.assertBytecodeMatches(g, ( + ('LOAD_CONST', None), + ('YIELD_VALUE', None), + ('POP_TOP', None), + ('LOAD_CONST', 42), + ('YIELD_VALUE', None), + ('POP_TOP', None), + ('LOAD_CONST', None), + ('RETURN_VALUE', None), + )) + + def test_bare_yield_from(self): + def g(): + yield from x + self.assertBytecodeMatches(g, ( + ('LOAD_GLOBAL', 'x'), + ('YIELD_FROM', None), + ('POP_TOP', None), + ('LOAD_CONST', None), + ('RETURN_VALUE', None), + )) + + def test_yield_from_assigned_to_local(self): + def g(): + x = yield from x + self.assertBytecodeMatches(g, ( + ('LOAD_FAST', 'x'), + ('YIELD_FROM', None), + ('STORE_FAST', 'x'), + ('LOAD_CONST', None), + ('RETURN_VALUE', None), + )) + + def test_parsing_yield_from_as_function_argument(self): + def g(): + f(yield from x) + self.assertBytecodeMatches(g, ( + ('LOAD_GLOBAL', 'f'), + ('LOAD_GLOBAL', 'x'), + ('YIELD_FROM', None), + ('CALL_FUNCTION', 1), + ('POP_TOP', None), + ('LOAD_CONST', None), + ('RETURN_VALUE', None), + )) + + +class TestPEP380Operation(unittest.TestCase): + """ + Test semantics. + """ + + def test_delegation_of_initial_next_to_subgenerator(self): + """ + Test delegation of initial next() call to subgenerator + """ + trace = [] + def g1(): + trace.append("Starting g1") + yield from g2() + trace.append("Finishing g1") + def g2(): + trace.append("Starting g2") + yield 42 + trace.append("Finishing g2") + for x in g1(): + trace.append("Yielded %s" % (x,)) + self.assertEqual(trace,[ + "Starting g1", + "Starting g2", + "Yielded 42", + "Finishing g2", + "Finishing g1", + ]) + + def test_raising_exception_in_initial_next_call(self): + """ + Test raising exception in initial next() call + """ + trace = [] + def g1(): + try: + trace.append("Starting g1") + yield from g2() + finally: + trace.append("Finishing g1") + def g2(): + try: + trace.append("Starting g2") + raise ValueError("spanish inquisition occurred") + finally: + trace.append("Finishing g2") + try: + for x in g1(): + trace.append("Yielded %s" % (x,)) + except ValueError as e: + self.assertEqual(e.args[0], "spanish inquisition occurred") + else: + self.fail("subgenerator failed to raise ValueError") + self.assertEqual(trace,[ + "Starting g1", + "Starting g2", + "Finishing g2", + "Finishing g1", + ]) + + def test_delegation_of_next_call_to_subgenerator(self): + """ + Test delegation of next() call to subgenerator + """ + trace = [] + def g1(): + trace.append("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + trace.append("Finishing g1") + def g2(): + trace.append("Starting g2") + yield "g2 spam" + yield "g2 more spam" + trace.append("Finishing g2") + for x in g1(): + trace.append("Yielded %s" % (x,)) + self.assertEqual(trace,[ + "Starting g1", + "Yielded g1 ham", + "Starting g2", + "Yielded g2 spam", + "Yielded g2 more spam", + "Finishing g2", + "Yielded g1 eggs", + "Finishing g1", + ]) + + def test_raising_exception_in_delegated_next_call(self): + """ + Test raising exception in delegated next() call + """ + trace = [] + def g1(): + try: + trace.append("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + trace.append("Finishing g1") + def g2(): + try: + trace.append("Starting g2") + yield "g2 spam" + raise ValueError("hovercraft is full of eels") + yield "g2 more spam" + finally: + trace.append("Finishing g2") + try: + for x in g1(): + trace.append("Yielded %s" % (x,)) + except ValueError as e: + self.assertEqual(e.args[0], "hovercraft is full of eels") + else: + self.fail("subgenerator failed to raise ValueError") + self.assertEqual(trace,[ + "Starting g1", + "Yielded g1 ham", + "Starting g2", + "Yielded g2 spam", + "Finishing g2", + "Finishing g1", + ]) + + def test_delegation_of_send(self): + """ + Test delegation of send() + """ + trace = [] + def g1(): + trace.append("Starting g1") + x = yield "g1 ham" + trace.append("g1 received %s" % (x,)) + yield from g2() + x = yield "g1 eggs" + trace.append("g1 received %s" % (x,)) + trace.append("Finishing g1") + def g2(): + trace.append("Starting g2") + x = yield "g2 spam" + trace.append("g2 received %s" % (x,)) + x = yield "g2 more spam" + trace.append("g2 received %s" % (x,)) + trace.append("Finishing g2") + g = g1() + y = next(g) + x = 1 + try: + while 1: + y = g.send(x) + trace.append("Yielded %s" % (y,)) + x += 1 + except StopIteration: + pass + self.assertEqual(trace,[ + "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", + ]) + + def test_handling_exception_while_delegating_send(self): + """ + Test handling exception while delegating 'send' + """ + trace = [] + def g1(): + trace.append("Starting g1") + x = yield "g1 ham" + trace.append("g1 received %s" % (x,)) + yield from g2() + x = yield "g1 eggs" + trace.append("g1 received %s" % (x,)) + trace.append("Finishing g1") + def g2(): + trace.append("Starting g2") + x = yield "g2 spam" + trace.append("g2 received %s" % (x,)) + raise ValueError("hovercraft is full of eels") + x = yield "g2 more spam" + trace.append("g2 received %s" % (x,)) + trace.append("Finishing g2") + def run(): + g = g1() + y = next(g) + x = 1 + try: + while 1: + y = g.send(x) + trace.append("Yielded %s" % (y,)) + x += 1 + except StopIteration: + trace.append("StopIteration") + self.assertRaises(ValueError,run) + self.assertEqual(trace,[ + "Starting g1", + "g1 received 1", + "Starting g2", + "Yielded g2 spam", + "g2 received 2", + ]) + + def test_delegating_close(self): + """ + Test delegating 'close' + """ + trace = [] + def g1(): + try: + trace.append("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + trace.append("Finishing g1") + def g2(): + try: + trace.append("Starting g2") + yield "g2 spam" + yield "g2 more spam" + finally: + trace.append("Finishing g2") + g = g1() + for i in range(2): + x = next(g) + trace.append("Yielded %s" % (x,)) + g.close() + self.assertEqual(trace,[ + "Starting g1", + "Yielded g1 ham", + "Starting g2", + "Yielded g2 spam", + "Finishing g2", + "Finishing g1" + ]) + + def test_handing_exception_while_delegating_close(self): + """ + Test handling exception while delegating 'close' + """ + trace = [] + def g1(): + try: + trace.append("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + trace.append("Finishing g1") + def g2(): + try: + trace.append("Starting g2") + yield "g2 spam" + yield "g2 more spam" + finally: + trace.append("Finishing g2") + raise ValueError("nybbles have exploded with delight") + try: + g = g1() + for i in range(2): + x = next(g) + trace.append("Yielded %s" % (x,)) + g.close() + except ValueError as e: + self.assertEqual(e.args[0], "nybbles have exploded with delight") + self.assertIsInstance(e.__context__, GeneratorExit) + else: + self.fail("subgenerator failed to raise ValueError") + self.assertEqual(trace,[ + "Starting g1", + "Yielded g1 ham", + "Starting g2", + "Yielded g2 spam", + "Finishing g2", + "Finishing g1", + ]) + + def test_delegating_throw(self): + """ + Test delegating 'throw' + """ + trace = [] + def g1(): + try: + trace.append("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + trace.append("Finishing g1") + def g2(): + try: + trace.append("Starting g2") + yield "g2 spam" + yield "g2 more spam" + finally: + trace.append("Finishing g2") + try: + g = g1() + for i in range(2): + x = next(g) + trace.append("Yielded %s" % (x,)) + e = ValueError("tomato ejected") + g.throw(e) + except ValueError as e: + self.assertEqual(e.args[0], "tomato ejected") + else: + self.fail("subgenerator failed to raise ValueError") + self.assertEqual(trace,[ + "Starting g1", + "Yielded g1 ham", + "Starting g2", + "Yielded g2 spam", + "Finishing g2", + "Finishing g1", + ]) + + def test_value_attribute_of_StopIteration_exception(self): + """ + Test 'value' attribute of StopIteration exception + """ + trace = [] + def pex(e): + trace.append("%s: %s" % (e.__class__.__name__, e)) + trace.append("value = %s" % (e.value,)) + e = StopIteration() + pex(e) + e = StopIteration("spam") + pex(e) + e.value = "eggs" + pex(e) + self.assertEqual(trace,[ + "StopIteration: ", + "value = None", + "StopIteration: spam", + "value = spam", + "StopIteration: spam", + "value = eggs", + ]) + + + def test_generator_return_value(self): + """ + Test generator return value + """ + trace = [] + def g1(): + trace.append("Starting g1") + yield "g1 ham" + ret = yield from g2() + trace.append("g2 returned %s" % (ret,)) + ret = yield from g2(42) + trace.append("g2 returned %s" % (ret,)) + yield "g1 eggs" + trace.append("Finishing g1") + def g2(v = None): + trace.append("Starting g2") + yield "g2 spam" + yield "g2 more spam" + trace.append("Finishing g2") + if v: + return v + for x in g1(): + trace.append("Yielded %s" % (x,)) + self.assertEqual(trace,[ + "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", + ]) + + def test_delegation_of_next_to_non_generator(self): + """ + Test delegation of next() to non-generator + """ + trace = [] + def g(): + yield from range(3) + for x in g(): + trace.append("Yielded %s" % (x,)) + self.assertEqual(trace,[ + "Yielded 0", + "Yielded 1", + "Yielded 2", + ]) + + + def test_conversion_of_sendNone_to_next(self): + """ + Test conversion of send(None) to next() + """ + trace = [] + def g(): + yield from range(3) + gi = g() + for x in range(3): + y = gi.send(None) + trace.append("Yielded: %s" % (y,)) + self.assertEqual(trace,[ + "Yielded: 0", + "Yielded: 1", + "Yielded: 2", + ]) + + def test_delegation_of_close_to_non_generator(self): + """ + Test delegation of close() to non-generator + """ + trace = [] + def g(): + try: + trace.append("starting g") + yield from range(3) + trace.append("g should not be here") + finally: + trace.append("finishing g") + gi = g() + next(gi) + with captured_stderr() as output: + gi.close() + self.assertEqual(output.getvalue(), '') + self.assertEqual(trace,[ + "starting g", + "finishing g", + ]) + + def test_delegating_throw_to_non_generator(self): + """ + Test delegating 'throw' to non-generator + """ + trace = [] + def g(): + try: + trace.append("Starting g") + yield from range(10) + finally: + trace.append("Finishing g") + try: + gi = g() + for i in range(5): + x = next(gi) + trace.append("Yielded %s" % (x,)) + e = ValueError("tomato ejected") + gi.throw(e) + except ValueError as e: + self.assertEqual(e.args[0],"tomato ejected") + else: + self.fail("subgenerator failed to raise ValueError") + self.assertEqual(trace,[ + "Starting g", + "Yielded 0", + "Yielded 1", + "Yielded 2", + "Yielded 3", + "Yielded 4", + "Finishing g", + ]) + + def test_attempting_to_send_to_non_generator(self): + """ + Test attempting to send to non-generator + """ + trace = [] + def g(): + try: + trace.append("starting g") + yield from range(3) + trace.append("g should not be here") + finally: + trace.append("finishing g") + try: + gi = g() + next(gi) + for x in range(3): + y = gi.send(42) + trace.append("Should not have yielded:", y) + except AttributeError as e: + self.assertIn("send", e.args[0]) + else: + self.fail("was able to send into non-generator") + self.assertEqual(trace,[ + "starting g", + "finishing g", + ]) + + def test_broken_getattr_handling(self): + """ + Test subiterator with a broken getattr implementation + """ + class Broken: + def __iter__(self): + return self + def __next__(self): + return 1 + def __getattr__(self, attr): + 1/0 + + def g(): + yield from Broken() + + with self.assertRaises(ZeroDivisionError): + gi = g() + self.assertEqual(next(gi), 1) + gi.send(1) + + with self.assertRaises(ZeroDivisionError): + gi = g() + self.assertEqual(next(gi), 1) + gi.throw(AttributeError) + + with captured_stderr() as output: + gi = g() + self.assertEqual(next(gi), 1) + gi.close() + self.assertIn('ZeroDivisionError', output.getvalue()) + + def test_exception_in_initial_next_call(self): + """ + Test exception in initial next() call + """ + trace = [] + def g1(): + trace.append("g1 about to yield from g2") + yield from g2() + trace.append("g1 should not be here") + def g2(): + yield 1/0 + def run(): + gi = g1() + next(gi) + self.assertRaises(ZeroDivisionError,run) + self.assertEqual(trace,[ + "g1 about to yield from g2" + ]) + + def test_attempted_yield_from_loop(self): + """ + Test attempted yield-from loop + """ + trace = [] + def g1(): + trace.append("g1: starting") + yield "y1" + trace.append("g1: about to yield from g2") + yield from g2() + trace.append("g1 should not be here") + + def g2(): + trace.append("g2: starting") + yield "y2" + trace.append("g2: about to yield from g1") + yield from gi + trace.append("g2 should not be here") + try: + gi = g1() + for y in gi: + trace.append("Yielded: %s" % (y,)) + except ValueError as e: + self.assertEqual(e.args[0],"generator already executing") + else: + self.fail("subgenerator didn't raise ValueError") + self.assertEqual(trace,[ + "g1: starting", + "Yielded: y1", + "g1: about to yield from g2", + "g2: starting", + "Yielded: y2", + "g2: about to yield from g1", + ]) + + def test_returning_value_from_delegated_throw(self): + """ + Test returning value from delegated 'throw' + """ + trace = [] + def g1(): + try: + trace.append("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + trace.append("Finishing g1") + def g2(): + try: + trace.append("Starting g2") + yield "g2 spam" + yield "g2 more spam" + except LunchError: + trace.append("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) + trace.append("Yielded %s" % (x,)) + e = LunchError("tomato ejected") + g.throw(e) + for x in g: + trace.append("Yielded %s" % (x,)) + self.assertEqual(trace,[ + "Starting g1", + "Yielded g1 ham", + "Starting g2", + "Yielded g2 spam", + "Caught LunchError in g2", + "Yielded g2 yet more spam", + "Yielded g1 eggs", + "Finishing g1", + ]) + + def test_next_and_return_with_value(self): + """ + Test next and return with value + """ + trace = [] + def f(r): + gi = g(r) + next(gi) + try: + trace.append("f resuming g") + next(gi) + trace.append("f SHOULD NOT BE HERE") + except StopIteration as e: + trace.append("f caught %s" % (repr(e),)) + def g(r): + trace.append("g starting") + yield + trace.append("g returning %s" % (r,)) + return r + f(None) + f(42) + self.assertEqual(trace,[ + "g starting", + "f resuming g", + "g returning None", + "f caught StopIteration()", + "g starting", + "f resuming g", + "g returning 42", + "f caught StopIteration(42,)", + ]) + + def test_send_and_return_with_value(self): + """ + Test send and return with value + """ + trace = [] + def f(r): + gi = g(r) + next(gi) + try: + trace.append("f sending spam to g") + gi.send("spam") + trace.append("f SHOULD NOT BE HERE") + except StopIteration as e: + trace.append("f caught %r" % (e,)) + def g(r): + trace.append("g starting") + x = yield + trace.append("g received %s" % (x,)) + trace.append("g returning %s" % (r,)) + return r + f(None) + f(42) + self.assertEqual(trace,[ + "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,)", + ]) + + def test_catching_exception_from_subgen_and_returning(self): + """ + Test catching an exception thrown into a + subgenerator and returning a value + """ + trace = [] + def inner(): + try: + yield 1 + except ValueError: + trace.append("inner caught ValueError") + return 2 + + def outer(): + v = yield from inner() + trace.append("inner returned %r to outer" % v) + yield v + g = outer() + trace.append(next(g)) + trace.append(g.throw(ValueError)) + self.assertEqual(trace,[ + 1, + "inner caught ValueError", + "inner returned 2 to outer", + 2, + ]) + + def test_throwing_GeneratorExit_into_subgen_that_returns(self): + """ + Test throwing GeneratorExit into a subgenerator that + catches it and returns normally. + """ + trace = [] + def f(): + try: + trace.append("Enter f") + yield + trace.append("Exit f") + except GeneratorExit: + return + def g(): + trace.append("Enter g") + yield from f() + trace.append("Exit g") + try: + gi = g() + next(gi) + gi.throw(GeneratorExit) + except GeneratorExit: + pass + else: + self.fail("subgenerator failed to raise GeneratorExit") + self.assertEqual(trace,[ + "Enter g", + "Enter f", + ]) + + def test_throwing_GeneratorExit_into_subgenerator_that_yields(self): + """ + Test throwing GeneratorExit into a subgenerator that + catches it and yields. + """ + trace = [] + def f(): + try: + trace.append("Enter f") + yield + trace.append("Exit f") + except GeneratorExit: + yield + def g(): + trace.append("Enter g") + yield from f() + trace.append("Exit g") + try: + gi = g() + next(gi) + gi.throw(GeneratorExit) + except RuntimeError as e: + self.assertEqual(e.args[0], "generator ignored GeneratorExit") + else: + self.fail("subgenerator failed to raise GeneratorExit") + self.assertEqual(trace,[ + "Enter g", + "Enter f", + ]) + + def test_throwing_GeneratorExit_into_subgen_that_raises(self): + """ + Test throwing GeneratorExit into a subgenerator that + catches it and raises a different exception. + """ + trace = [] + def f(): + try: + trace.append("Enter f") + yield + trace.append("Exit f") + except GeneratorExit: + raise ValueError("Vorpal bunny encountered") + def g(): + trace.append("Enter g") + yield from f() + trace.append("Exit g") + try: + gi = g() + next(gi) + gi.throw(GeneratorExit) + except ValueError as e: + self.assertEqual(e.args[0], "Vorpal bunny encountered") + self.assertIsInstance(e.__context__, GeneratorExit) + else: + self.fail("subgenerator failed to raise ValueError") + self.assertEqual(trace,[ + "Enter g", + "Enter f", + ]) + + +def test_main(): + from test import support + test_classes = [TestPEP380Bytecode, TestPEP380Operation] + support.run_unittest(*test_classes) + + +if __name__ == '__main__': + test_main() diff -r 101919aba8d6 -r 0d1d76f68750 Lib/test/test_sys.py --- a/Lib/test/test_sys.py Mon Oct 31 09:05:10 2011 +0100 +++ b/Lib/test/test_sys.py Wed Nov 09 20:56:37 2011 +1000 @@ -727,7 +727,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 101919aba8d6 -r 0d1d76f68750 Misc/ACKS --- a/Misc/ACKS Mon Oct 31 09:05:10 2011 +0100 +++ b/Misc/ACKS Wed Nov 09 20:56:37 2011 +1000 @@ -95,6 +95,7 @@ Philippe Biondi Stuart Bishop Roy Bixler +Renaud Blanch Mike Bland Martin Bless Pablo Bleyer diff -r 101919aba8d6 -r 0d1d76f68750 Misc/NEWS --- a/Misc/NEWS Mon Oct 31 09:05:10 2011 +0100 +++ b/Misc/NEWS Wed Nov 09 20:56:37 2011 +1000 @@ -10,6 +10,10 @@ Core and Builtins ----------------- +- PEP 380, Issue #11682: Add "yield from " to support easy delegation to + subgenerators (initial patch by Greg Ewing, integration into 3.3 by + Renaud Blanch, Ryan Kelly and Nick Coghlan) + - Issue #10519: Avoid unnecessary recursive function calls in setobject.c. diff -r 101919aba8d6 -r 0d1d76f68750 Modules/parsermodule.c --- a/Modules/parsermodule.c Mon Oct 31 09:05:10 2011 +0100 +++ b/Modules/parsermodule.c Wed Nov 09 20:56:37 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 101919aba8d6 -r 0d1d76f68750 Objects/abstract.c --- a/Objects/abstract.c Mon Oct 31 09:05:10 2011 +0100 +++ b/Objects/abstract.c Wed Nov 09 20:56:37 2011 +1000 @@ -2267,7 +2267,6 @@ func = PyObject_GetAttrString(o, name); if (func == NULL) { - PyErr_SetString(PyExc_AttributeError, name); return 0; } @@ -2311,7 +2310,6 @@ func = PyObject_GetAttrString(o, name); if (func == NULL) { - PyErr_SetString(PyExc_AttributeError, name); return 0; } va_start(va, format); diff -r 101919aba8d6 -r 0d1d76f68750 Objects/exceptions.c --- a/Objects/exceptions.c Mon Oct 31 09:05:10 2011 +0100 +++ b/Objects/exceptions.c Wed Nov 09 20:56:37 2011 +1000 @@ -487,8 +487,73 @@ /* * 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_Create(PyObject *value) +{ + return PyObject_CallFunctionObjArgs(PyExc_StopIteration, value, NULL); +} + +ComplexExtendsException( + PyExc_Exception, /* base */ + StopIteration, /* name */ + StopIteration, /* prefix for *_init, etc */ + 0, /* new */ + 0, /* methods */ + StopIteration_members, /* members */ + 0, /* getset */ + 0, /* str */ + "Signal the end from iterator.__next__()." +); /* diff -r 101919aba8d6 -r 0d1d76f68750 Objects/frameobject.c --- a/Objects/frameobject.c Mon Oct 31 09:05:10 2011 +0100 +++ b/Objects/frameobject.c Wed Nov 09 20:56:37 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 101919aba8d6 -r 0d1d76f68750 Objects/genobject.c --- a/Objects/genobject.c Mon Oct 31 09:05:10 2011 +0100 +++ b/Objects/genobject.c Wed Nov 09 20:56:37 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 *gen); + static int gen_traverse(PyGenObject *gen, visitproc visit, void *arg) { @@ -90,12 +93,18 @@ /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ - if (result == Py_None && f->f_stacktop == NULL) { - Py_DECREF(result); - result = NULL; - /* Set exception if not called by gen_iternext() */ - if (arg) + if (result && f->f_stacktop == NULL) { + if (result == Py_None) { + /* Delay exception instantiation if we can */ PyErr_SetNone(PyExc_StopIteration); + } else { + PyObject *e = PyStopIteration_Create(result); + if (e != NULL) { + PyErr_SetObject(PyExc_StopIteration, e); + Py_DECREF(e); + } + } + Py_CLEAR(result); } if (!result || f->f_stacktop == NULL) { @@ -111,8 +120,8 @@ Py_XDECREF(t); Py_XDECREF(v); Py_XDECREF(tb); + gen->gi_frame = NULL; Py_DECREF(f); - gen->gi_frame = NULL; } return result; @@ -125,17 +134,91 @@ 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; + /* XXX (ncoghlan): Are the incref/decref on arg and yf strictly needed? + * Or would it be valid to rely on borrowed references? + */ + 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_CLEAR(arg); + 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 == NULL) { + return -1; + } + } else { + PyObject *meth = PyObject_GetAttrString(yf, "close"); + if (meth == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_WriteUnraisable(yf); + } + PyErr_Clear(); + } else { + 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); @@ -196,7 +279,7 @@ _Py_NewReference(self); self->ob_refcnt = refcnt; } - assert(PyType_IS_GC(self->ob_type) && + assert(PyType_IS_GC(Py_TYPE(self)) && _Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED); /* If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so @@ -209,8 +292,8 @@ * undone. */ #ifdef COUNT_ALLOCS - --self->ob_type->tp_frees; - --self->ob_type->tp_allocs; + --(Py_TYPE(self)->tp_frees); + --(Py_TYPE(self)->tp_allocs); #endif } @@ -226,10 +309,55 @@ 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 == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { + Py_DECREF(yf); + return NULL; + } + 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) { @@ -272,7 +400,7 @@ PyErr_Format(PyExc_TypeError, "exceptions must be classes or instances " "deriving from BaseException, not %s", - typ->ob_type->tp_name); + Py_TYPE(typ)->tp_name); goto failed_throw; } @@ -291,9 +419,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 = Py_TYPE(yf)->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_DECREF(ev); + } + } else if (PyErr_Occurred()) { + return -1; + } + if (value == NULL) { + value = Py_None; + } + Py_INCREF(value); + *pvalue = value; + return 0; +} static PyObject * gen_repr(PyGenObject *gen) diff -r 101919aba8d6 -r 0d1d76f68750 Parser/Python.asdl --- a/Parser/Python.asdl Mon Oct 31 09:05:10 2011 +0100 +++ b/Parser/Python.asdl Wed Nov 09 20:56:37 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 101919aba8d6 -r 0d1d76f68750 Python/Python-ast.c --- a/Python/Python-ast.c Mon Oct 31 09:05:10 2011 +0100 +++ b/Python/Python-ast.c Wed Nov 09 20:56:37 2011 +1000 @@ -231,7 +231,9 @@ "generators", }; static PyTypeObject *Yield_type; +_Py_IDENTIFIER(is_from); static char *Yield_fields[]={ + "is_from", "value", }; static PyTypeObject *Compare_type; @@ -814,7 +816,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; @@ -1751,13 +1753,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; @@ -2799,6 +2802,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_SetAttrId(result, &PyId_value, value) == -1) @@ -5341,8 +5349,21 @@ return 1; } if (isinstance) { + int is_from; expr_ty value; + if (_PyObject_HasAttrId(obj, &PyId_is_from)) { + int res; + tmp = _PyObject_GetAttrId(obj, &PyId_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_HasAttrId(obj, &PyId_value)) { int res; tmp = _PyObject_GetAttrId(obj, &PyId_value); @@ -5354,7 +5375,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 101919aba8d6 -r 0d1d76f68750 Python/ast.c --- a/Python/ast.c Mon Oct 31 09:05:10 2011 +0100 +++ b/Python/ast.c Wed Nov 09 20:56:37 2011 +1000 @@ -2369,13 +2369,24 @@ } 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 factor: if (NCH(n) == 1) { @@ -2399,7 +2410,7 @@ /* arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test) - argument: [test '='] test [comp_for] # Really [keyword '='] test + argument: [test '='] (test) [comp_for] # Really [keyword '='] test */ int i, nargs, nkeywords, ngens; @@ -2693,7 +2704,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 101919aba8d6 -r 0d1d76f68750 Python/ceval.c --- a/Python/ceval.c Mon Oct 31 09:05:10 2011 +0100 +++ b/Python/ceval.c Wed Nov 09 20:56:37 2011 +1000 @@ -1828,6 +1828,52 @@ why = WHY_RETURN; goto fast_block_end; + TARGET(YIELD_FROM) + u = POP(); + x = PyObject_GetIter(u); + Py_DECREF(u); + if (x == NULL) + break; + /* x is now the iterator, make the first next() call */ + retval = (*Py_TYPE(x)->tp_iternext)(x); + if (!retval) { + /* iter may be exhausted */ + Py_CLEAR(x); + if (!PyErr_ExceptionMatches(PyExc_StopIteration)) { + /* some other exception */ + break; + } + /* try to get return value from exception */ + PyObject *et, *ev, *tb; + PyErr_Fetch(&et, &ev, &tb); + Py_XDECREF(et); + Py_XDECREF(tb); + /* u is return value */ + u = NULL; + if (ev) { + u = PyObject_GetAttrString(ev, "value"); + Py_DECREF(ev); + if (u == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { + /* some other exception */ + break; + } + PyErr_Clear(); + } + } + if (u == NULL) { + u = Py_None; + Py_INCREF(u); + } + PUSH(u); + continue; + } + /* x is iterator, retval is value to be yielded */ + f->f_yieldfrom = x; + f->f_stacktop = stack_pointer; + why = WHY_YIELD; + goto fast_yield; + TARGET(YIELD_VALUE) retval = POP(); f->f_stacktop = stack_pointer; diff -r 101919aba8d6 -r 0d1d76f68750 Python/compile.c --- a/Python/compile.c Mon Oct 31 09:05:10 2011 +0100 +++ b/Python/compile.c Wed Nov 09 20:56:37 2011 +1000 @@ -775,6 +775,7 @@ case IMPORT_STAR: return -1; case YIELD_VALUE: + case YIELD_FROM: return 0; case POP_BLOCK: @@ -3217,7 +3218,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 101919aba8d6 -r 0d1d76f68750 Python/graminit.c --- a/Python/graminit.c Mon Oct 31 09:05:10 2011 +0100 +++ b/Python/graminit.c Wed Nov 09 20:56:37 2011 +1000 @@ -1791,7 +1791,7 @@ {167, 1}, }; static arc arcs_80_1[2] = { - {9, 2}, + {168, 2}, {0, 1}, }; static arc arcs_80_2[1] = { @@ -1802,171 +1802,188 @@ {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 dfa dfas[82] = { {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\204\000"}, {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\204\000"}, {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\204\000"}, {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\200\000"}, {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\200\000"}, {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\200\000"}, {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\200\000"}, {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\200\000"}, {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"}, + "\000\040\040\200\001\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\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\000\000\000\000\000\000\200\000\000\000\200\020\000\000\206\120\076\000\000"}, {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\200\000"}, + {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"}, }; -static label labels[168] = { +static label labels[169] = { {0, "EMPTY"}, {256, 0}, {4, 0}, @@ -2135,10 +2152,11 @@ {334, 0}, {335, 0}, {1, "yield"}, + {337, 0}, }; grammar _PyParser_Grammar = { - 81, + 82, dfas, - {168, labels}, + {169, labels}, 256 }; diff -r 101919aba8d6 -r 0d1d76f68750 Python/opcode_targets.h --- a/Python/opcode_targets.h Mon Oct 31 09:05:10 2011 +0100 +++ b/Python/opcode_targets.h Wed Nov 09 20:56:37 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 101919aba8d6 -r 0d1d76f68750 Python/symtable.c --- a/Python/symtable.c Mon Oct 31 09:05:10 2011 +0100 +++ b/Python/symtable.c Wed Nov 09 20:56:37 2011 +1000 @@ -19,10 +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, void *key, int lineno, int col_offset) @@ -1133,14 +1129,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 +1330,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);