From 06ded09d4048bf44ce9b61c82494cf0a0825412c Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Fri, 4 Aug 2006 16:20:30 +0000 Subject: Fix the 'compiler' package to generate correct code for MAKE_CLOSURE. In the 2.5 development cycle, MAKE_CLOSURE as changed to take free variables as a tuple rather than as individual items on the stack. Closes patch #1534084. --- Lib/compiler/pycodegen.py | 39 +++++++++++++-------------------------- Lib/test/test_compiler.py | 13 +++++++++++++ Misc/NEWS | 3 +++ 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py index d5d68aa..e3667b5 100644 --- a/Lib/compiler/pycodegen.py +++ b/Lib/compiler/pycodegen.py @@ -382,16 +382,7 @@ class CodeGenerator: 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 @@ class CodeGenerator: 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 @@ class CodeGenerator: self.newBlock() self.emit('POP_TOP') - 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) + 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', 0) + self.emit('MAKE_CLOSURE', args) else: self.emit('LOAD_CONST', gen) - self.emit('MAKE_FUNCTION', 0) + 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) + 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 929a12b..9dff71e 100644 --- a/Lib/test/test_compiler.py +++ b/Lib/test/test_compiler.py @@ -104,6 +104,19 @@ class CompilerTest(unittest.TestCase): 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 c4904da..3c1ba7f 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 ----------------- -- cgit v0.12