diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py index d5d68aa29ef0..e3667b594d3b 100644 --- a/Lib/compiler/pycodegen.py +++ b/Lib/compiler/pycodegen.py @@ -382,16 +382,7 @@ def _visitFuncOrLambda(self, node, isLambda=0): self.set_lineno(node) for default in node.defaults: self.visit(default) - frees = gen.scope.get_free_vars() - if frees: - for name in frees: - self.emit('LOAD_CLOSURE', name) - self.emit('LOAD_CONST', gen) - self.emit('MAKE_CLOSURE', len(node.defaults)) - else: - self.emit('LOAD_CONST', gen) - self.emit('MAKE_FUNCTION', len(node.defaults)) - + self._makeClosure(gen, len(node.defaults)) for i in range(ndecorators): self.emit('CALL_FUNCTION', 1) @@ -405,14 +396,7 @@ def visitClass(self, node): for base in node.bases: self.visit(base) self.emit('BUILD_TUPLE', len(node.bases)) - frees = gen.scope.get_free_vars() - for name in frees: - self.emit('LOAD_CLOSURE', name) - self.emit('LOAD_CONST', gen) - if frees: - self.emit('MAKE_CLOSURE', 0) - else: - self.emit('MAKE_FUNCTION', 0) + self._makeClosure(gen, 0) self.emit('CALL_FUNCTION', 0) self.emit('BUILD_CLASS') self.storeName(node.name) @@ -644,22 +628,25 @@ def visitListCompIf(self, node, branch): self.newBlock() self.emit('POP_TOP') + def _makeClosure(self, gen, args): + frees = gen.scope.get_free_vars() + if frees: + for name in frees: + self.emit('LOAD_CLOSURE', name) + self.emit('BUILD_TUPLE', len(frees)) + self.emit('LOAD_CONST', gen) + self.emit('MAKE_CLOSURE', args) + else: + self.emit('LOAD_CONST', gen) + self.emit('MAKE_FUNCTION', args) + def visitGenExpr(self, node): gen = GenExprCodeGenerator(node, self.scopes, self.class_name, self.get_module()) walk(node.code, gen) gen.finish() self.set_lineno(node) - frees = gen.scope.get_free_vars() - if frees: - for name in frees: - self.emit('LOAD_CLOSURE', name) - self.emit('LOAD_CONST', gen) - self.emit('MAKE_CLOSURE', 0) - else: - self.emit('LOAD_CONST', gen) - self.emit('MAKE_FUNCTION', 0) - + self._makeClosure(gen, 0) # precomputation of outmost iterable self.visit(node.code.quals[0].iter) self.emit('GET_ITER') diff --git a/Lib/test/test_compiler.py b/Lib/test/test_compiler.py index 929a12bb6f29..9dff71e299c0 100644 --- a/Lib/test/test_compiler.py +++ b/Lib/test/test_compiler.py @@ -104,6 +104,19 @@ def testFlatten(self): self.assertEquals(flatten([1, [2]]), [1, 2]) self.assertEquals(flatten((1, (2,))), [1, 2]) + def testNestedScope(self): + c = compiler.compile('def g():\n' + ' a = 1\n' + ' def f(): return a + 2\n' + ' return f()\n' + 'result = g()', + '', + 'exec') + dct = {} + exec c in dct + self.assertEquals(dct.get('result'), 3) + + NOLINENO = (compiler.ast.Module, compiler.ast.Stmt, compiler.ast.Discard) ############################################################################### diff --git a/Misc/NEWS b/Misc/NEWS index c4904da76409..3c1ba7f1de1a 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -28,6 +28,9 @@ Library - Bug #1531405, format_exception no longer raises an exception if str(exception) raised an exception. +- Fix a bug in the ``compiler`` package that caused invalid code to be + generated for nested functions. + Extension Modules -----------------