| import unittest | 
 | import weakref | 
 |  | 
 | from test.support import check_syntax_error, cpython_only, run_unittest | 
 |  | 
 |  | 
 | class ScopeTests(unittest.TestCase): | 
 |  | 
 |     def testSimpleNesting(self): | 
 |  | 
 |         def make_adder(x): | 
 |             def adder(y): | 
 |                 return x + y | 
 |             return adder | 
 |  | 
 |         inc = make_adder(1) | 
 |         plus10 = make_adder(10) | 
 |  | 
 |         self.assertEqual(inc(1), 2) | 
 |         self.assertEqual(plus10(-2), 8) | 
 |  | 
 |     def testExtraNesting(self): | 
 |  | 
 |         def make_adder2(x): | 
 |             def extra(): # check freevars passing through non-use scopes | 
 |                 def adder(y): | 
 |                     return x + y | 
 |                 return adder | 
 |             return extra() | 
 |  | 
 |         inc = make_adder2(1) | 
 |         plus10 = make_adder2(10) | 
 |  | 
 |         self.assertEqual(inc(1), 2) | 
 |         self.assertEqual(plus10(-2), 8) | 
 |  | 
 |     def testSimpleAndRebinding(self): | 
 |  | 
 |         def make_adder3(x): | 
 |             def adder(y): | 
 |                 return x + y | 
 |             x = x + 1 # check tracking of assignment to x in defining scope | 
 |             return adder | 
 |  | 
 |         inc = make_adder3(0) | 
 |         plus10 = make_adder3(9) | 
 |  | 
 |         self.assertEqual(inc(1), 2) | 
 |         self.assertEqual(plus10(-2), 8) | 
 |  | 
 |     def testNestingGlobalNoFree(self): | 
 |  | 
 |         def make_adder4(): # XXX add exta level of indirection | 
 |             def nest(): | 
 |                 def nest(): | 
 |                     def adder(y): | 
 |                         return global_x + y # check that plain old globals work | 
 |                     return adder | 
 |                 return nest() | 
 |             return nest() | 
 |  | 
 |         global_x = 1 | 
 |         adder = make_adder4() | 
 |         self.assertEqual(adder(1), 2) | 
 |  | 
 |         global_x = 10 | 
 |         self.assertEqual(adder(-2), 8) | 
 |  | 
 |     def testNestingThroughClass(self): | 
 |  | 
 |         def make_adder5(x): | 
 |             class Adder: | 
 |                 def __call__(self, y): | 
 |                     return x + y | 
 |             return Adder() | 
 |  | 
 |         inc = make_adder5(1) | 
 |         plus10 = make_adder5(10) | 
 |  | 
 |         self.assertEqual(inc(1), 2) | 
 |         self.assertEqual(plus10(-2), 8) | 
 |  | 
 |     def testNestingPlusFreeRefToGlobal(self): | 
 |  | 
 |         def make_adder6(x): | 
 |             global global_nest_x | 
 |             def adder(y): | 
 |                 return global_nest_x + y | 
 |             global_nest_x = x | 
 |             return adder | 
 |  | 
 |         inc = make_adder6(1) | 
 |         plus10 = make_adder6(10) | 
 |  | 
 |         self.assertEqual(inc(1), 11) # there's only one global | 
 |         self.assertEqual(plus10(-2), 8) | 
 |  | 
 |     def testNearestEnclosingScope(self): | 
 |  | 
 |         def f(x): | 
 |             def g(y): | 
 |                 x = 42 # check that this masks binding in f() | 
 |                 def h(z): | 
 |                     return x + z | 
 |                 return h | 
 |             return g(2) | 
 |  | 
 |         test_func = f(10) | 
 |         self.assertEqual(test_func(5), 47) | 
 |  | 
 |     def testMixedFreevarsAndCellvars(self): | 
 |  | 
 |         def identity(x): | 
 |             return x | 
 |  | 
 |         def f(x, y, z): | 
 |             def g(a, b, c): | 
 |                 a = a + x # 3 | 
 |                 def h(): | 
 |                     # z * (4 + 9) | 
 |                     # 3 * 13 | 
 |                     return identity(z * (b + y)) | 
 |                 y = c + z # 9 | 
 |                 return h | 
 |             return g | 
 |  | 
 |         g = f(1, 2, 3) | 
 |         h = g(2, 4, 6) | 
 |         self.assertEqual(h(), 39) | 
 |  | 
 |     def testFreeVarInMethod(self): | 
 |  | 
 |         def test(): | 
 |             method_and_var = "var" | 
 |             class Test: | 
 |                 def method_and_var(self): | 
 |                     return "method" | 
 |                 def test(self): | 
 |                     return method_and_var | 
 |                 def actual_global(self): | 
 |                     return str("global") | 
 |                 def str(self): | 
 |                     return str(self) | 
 |             return Test() | 
 |  | 
 |         t = test() | 
 |         self.assertEqual(t.test(), "var") | 
 |         self.assertEqual(t.method_and_var(), "method") | 
 |         self.assertEqual(t.actual_global(), "global") | 
 |  | 
 |         method_and_var = "var" | 
 |         class Test: | 
 |             # this class is not nested, so the rules are different | 
 |             def method_and_var(self): | 
 |                 return "method" | 
 |             def test(self): | 
 |                 return method_and_var | 
 |             def actual_global(self): | 
 |                 return str("global") | 
 |             def str(self): | 
 |                 return str(self) | 
 |  | 
 |         t = Test() | 
 |         self.assertEqual(t.test(), "var") | 
 |         self.assertEqual(t.method_and_var(), "method") | 
 |         self.assertEqual(t.actual_global(), "global") | 
 |  | 
 |     def testCellIsKwonlyArg(self): | 
 |         # Issue 1409: Initialisation of a cell value, | 
 |         # when it comes from a keyword-only parameter | 
 |         def foo(*, a=17): | 
 |             def bar(): | 
 |                 return a + 5 | 
 |             return bar() + 3 | 
 |  | 
 |         self.assertEqual(foo(a=42), 50) | 
 |         self.assertEqual(foo(), 25) | 
 |  | 
 |     def testRecursion(self): | 
 |  | 
 |         def f(x): | 
 |             def fact(n): | 
 |                 if n == 0: | 
 |                     return 1 | 
 |                 else: | 
 |                     return n * fact(n - 1) | 
 |             if x >= 0: | 
 |                 return fact(x) | 
 |             else: | 
 |                 raise ValueError("x must be >= 0") | 
 |  | 
 |         self.assertEqual(f(6), 720) | 
 |  | 
 |  | 
 |     def testUnoptimizedNamespaces(self): | 
 |  | 
 |         check_syntax_error(self, """if 1: | 
 |             def unoptimized_clash1(strip): | 
 |                 def f(s): | 
 |                     from sys import * | 
 |                     return getrefcount(s) # ambiguity: free or local | 
 |                 return f | 
 |             """) | 
 |  | 
 |         check_syntax_error(self, """if 1: | 
 |             def unoptimized_clash2(): | 
 |                 from sys import * | 
 |                 def f(s): | 
 |                     return getrefcount(s) # ambiguity: global or local | 
 |                 return f | 
 |             """) | 
 |  | 
 |         check_syntax_error(self, """if 1: | 
 |             def unoptimized_clash2(): | 
 |                 from sys import * | 
 |                 def g(): | 
 |                     def f(s): | 
 |                         return getrefcount(s) # ambiguity: global or local | 
 |                     return f | 
 |             """) | 
 |  | 
 |         check_syntax_error(self, """if 1: | 
 |             def f(): | 
 |                 def g(): | 
 |                     from sys import * | 
 |                     return getrefcount # global or local? | 
 |             """) | 
 |  | 
 |     def testLambdas(self): | 
 |  | 
 |         f1 = lambda x: lambda y: x + y | 
 |         inc = f1(1) | 
 |         plus10 = f1(10) | 
 |         self.assertEqual(inc(1), 2) | 
 |         self.assertEqual(plus10(5), 15) | 
 |  | 
 |         f2 = lambda x: (lambda : lambda y: x + y)() | 
 |         inc = f2(1) | 
 |         plus10 = f2(10) | 
 |         self.assertEqual(inc(1), 2) | 
 |         self.assertEqual(plus10(5), 15) | 
 |  | 
 |         f3 = lambda x: lambda y: global_x + y | 
 |         global_x = 1 | 
 |         inc = f3(None) | 
 |         self.assertEqual(inc(2), 3) | 
 |  | 
 |         f8 = lambda x, y, z: lambda a, b, c: lambda : z * (b + y) | 
 |         g = f8(1, 2, 3) | 
 |         h = g(2, 4, 6) | 
 |         self.assertEqual(h(), 18) | 
 |  | 
 |     def testUnboundLocal(self): | 
 |  | 
 |         def errorInOuter(): | 
 |             print(y) | 
 |             def inner(): | 
 |                 return y | 
 |             y = 1 | 
 |  | 
 |         def errorInInner(): | 
 |             def inner(): | 
 |                 return y | 
 |             inner() | 
 |             y = 1 | 
 |  | 
 |         self.assertRaises(UnboundLocalError, errorInOuter) | 
 |         self.assertRaises(NameError, errorInInner) | 
 |  | 
 |     def testUnboundLocal_AfterDel(self): | 
 |         # #4617: It is now legal to delete a cell variable. | 
 |         # The following functions must obviously compile, | 
 |         # and give the correct error when accessing the deleted name. | 
 |         def errorInOuter(): | 
 |             y = 1 | 
 |             del y | 
 |             print(y) | 
 |             def inner(): | 
 |                 return y | 
 |  | 
 |         def errorInInner(): | 
 |             def inner(): | 
 |                 return y | 
 |             y = 1 | 
 |             del y | 
 |             inner() | 
 |  | 
 |         self.assertRaises(UnboundLocalError, errorInOuter) | 
 |         self.assertRaises(NameError, errorInInner) | 
 |  | 
 |     def testUnboundLocal_AugAssign(self): | 
 |         # test for bug #1501934: incorrect LOAD/STORE_GLOBAL generation | 
 |         exec("""if 1: | 
 |             global_x = 1 | 
 |             def f(): | 
 |                 global_x += 1 | 
 |             try: | 
 |                 f() | 
 |             except UnboundLocalError: | 
 |                 pass | 
 |             else: | 
 |                 fail('scope of global_x not correctly determined') | 
 |             """, {'fail': self.fail}) | 
 |  | 
 |     def testComplexDefinitions(self): | 
 |  | 
 |         def makeReturner(*lst): | 
 |             def returner(): | 
 |                 return lst | 
 |             return returner | 
 |  | 
 |         self.assertEqual(makeReturner(1,2,3)(), (1,2,3)) | 
 |  | 
 |         def makeReturner2(**kwargs): | 
 |             def returner(): | 
 |                 return kwargs | 
 |             return returner | 
 |  | 
 |         self.assertEqual(makeReturner2(a=11)()['a'], 11) | 
 |  | 
 |     def testScopeOfGlobalStmt(self): | 
 |         # Examples posted by Samuele Pedroni to python-dev on 3/1/2001 | 
 |  | 
 |         exec("""if 1: | 
 |             # I | 
 |             x = 7 | 
 |             def f(): | 
 |                 x = 1 | 
 |                 def g(): | 
 |                     global x | 
 |                     def i(): | 
 |                         def h(): | 
 |                             return x | 
 |                         return h() | 
 |                     return i() | 
 |                 return g() | 
 |             self.assertEqual(f(), 7) | 
 |             self.assertEqual(x, 7) | 
 |  | 
 |             # II | 
 |             x = 7 | 
 |             def f(): | 
 |                 x = 1 | 
 |                 def g(): | 
 |                     x = 2 | 
 |                     def i(): | 
 |                         def h(): | 
 |                             return x | 
 |                         return h() | 
 |                     return i() | 
 |                 return g() | 
 |             self.assertEqual(f(), 2) | 
 |             self.assertEqual(x, 7) | 
 |  | 
 |             # III | 
 |             x = 7 | 
 |             def f(): | 
 |                 x = 1 | 
 |                 def g(): | 
 |                     global x | 
 |                     x = 2 | 
 |                     def i(): | 
 |                         def h(): | 
 |                             return x | 
 |                         return h() | 
 |                     return i() | 
 |                 return g() | 
 |             self.assertEqual(f(), 2) | 
 |             self.assertEqual(x, 2) | 
 |  | 
 |             # IV | 
 |             x = 7 | 
 |             def f(): | 
 |                 x = 3 | 
 |                 def g(): | 
 |                     global x | 
 |                     x = 2 | 
 |                     def i(): | 
 |                         def h(): | 
 |                             return x | 
 |                         return h() | 
 |                     return i() | 
 |                 return g() | 
 |             self.assertEqual(f(), 2) | 
 |             self.assertEqual(x, 2) | 
 |  | 
 |             # XXX what about global statements in class blocks? | 
 |             # do they affect methods? | 
 |  | 
 |             x = 12 | 
 |             class Global: | 
 |                 global x | 
 |                 x = 13 | 
 |                 def set(self, val): | 
 |                     x = val | 
 |                 def get(self): | 
 |                     return x | 
 |  | 
 |             g = Global() | 
 |             self.assertEqual(g.get(), 13) | 
 |             g.set(15) | 
 |             self.assertEqual(g.get(), 13) | 
 |             """) | 
 |  | 
 |     def testLeaks(self): | 
 |  | 
 |         class Foo: | 
 |             count = 0 | 
 |  | 
 |             def __init__(self): | 
 |                 Foo.count += 1 | 
 |  | 
 |             def __del__(self): | 
 |                 Foo.count -= 1 | 
 |  | 
 |         def f1(): | 
 |             x = Foo() | 
 |             def f2(): | 
 |                 return x | 
 |             f2() | 
 |  | 
 |         for i in range(100): | 
 |             f1() | 
 |  | 
 |         self.assertEqual(Foo.count, 0) | 
 |  | 
 |     def testClassAndGlobal(self): | 
 |  | 
 |         exec("""if 1: | 
 |             def test(x): | 
 |                 class Foo: | 
 |                     global x | 
 |                     def __call__(self, y): | 
 |                         return x + y | 
 |                 return Foo() | 
 |  | 
 |             x = 0 | 
 |             self.assertEqual(test(6)(2), 8) | 
 |             x = -1 | 
 |             self.assertEqual(test(3)(2), 5) | 
 |  | 
 |             looked_up_by_load_name = False | 
 |             class X: | 
 |                 # Implicit globals inside classes are be looked up by LOAD_NAME, not | 
 |                 # LOAD_GLOBAL. | 
 |                 locals()['looked_up_by_load_name'] = True | 
 |                 passed = looked_up_by_load_name | 
 |  | 
 |             self.assertTrue(X.passed) | 
 |             """) | 
 |  | 
 |     def testLocalsFunction(self): | 
 |  | 
 |         def f(x): | 
 |             def g(y): | 
 |                 def h(z): | 
 |                     return y + z | 
 |                 w = x + y | 
 |                 y += 3 | 
 |                 return locals() | 
 |             return g | 
 |  | 
 |         d = f(2)(4) | 
 |         self.assertIn('h', d) | 
 |         del d['h'] | 
 |         self.assertEqual(d, {'x': 2, 'y': 7, 'w': 6}) | 
 |  | 
 |     def testLocalsClass(self): | 
 |         # This test verifies that calling locals() does not pollute | 
 |         # the local namespace of the class with free variables.  Old | 
 |         # versions of Python had a bug, where a free variable being | 
 |         # passed through a class namespace would be inserted into | 
 |         # locals() by locals() or exec or a trace function. | 
 |         # | 
 |         # The real bug lies in frame code that copies variables | 
 |         # between fast locals and the locals dict, e.g. when executing | 
 |         # a trace function. | 
 |  | 
 |         def f(x): | 
 |             class C: | 
 |                 x = 12 | 
 |                 def m(self): | 
 |                     return x | 
 |                 locals() | 
 |             return C | 
 |  | 
 |         self.assertEqual(f(1).x, 12) | 
 |  | 
 |         def f(x): | 
 |             class C: | 
 |                 y = x | 
 |                 def m(self): | 
 |                     return x | 
 |                 z = list(locals()) | 
 |             return C | 
 |  | 
 |         varnames = f(1).z | 
 |         self.assertNotIn("x", varnames) | 
 |         self.assertIn("y", varnames) | 
 |  | 
 |     @cpython_only | 
 |     def testLocalsClass_WithTrace(self): | 
 |         # Issue23728: after the trace function returns, the locals() | 
 |         # dictionary is used to update all variables, this used to | 
 |         # include free variables. But in class statements, free | 
 |         # variables are not inserted... | 
 |         import sys | 
 |         self.addCleanup(sys.settrace, sys.gettrace()) | 
 |         sys.settrace(lambda a,b,c:None) | 
 |         x = 12 | 
 |  | 
 |         class C: | 
 |             def f(self): | 
 |                 return x | 
 |  | 
 |         self.assertEqual(x, 12) # Used to raise UnboundLocalError | 
 |  | 
 |     def testBoundAndFree(self): | 
 |         # var is bound and free in class | 
 |  | 
 |         def f(x): | 
 |             class C: | 
 |                 def m(self): | 
 |                     return x | 
 |                 a = x | 
 |             return C | 
 |  | 
 |         inst = f(3)() | 
 |         self.assertEqual(inst.a, inst.m()) | 
 |  | 
 |     @cpython_only | 
 |     def testInteractionWithTraceFunc(self): | 
 |  | 
 |         import sys | 
 |         def tracer(a,b,c): | 
 |             return tracer | 
 |  | 
 |         def adaptgetter(name, klass, getter): | 
 |             kind, des = getter | 
 |             if kind == 1:       # AV happens when stepping from this line to next | 
 |                 if des == "": | 
 |                     des = "_%s__%s" % (klass.__name__, name) | 
 |                 return lambda obj: getattr(obj, des) | 
 |  | 
 |         class TestClass: | 
 |             pass | 
 |  | 
 |         self.addCleanup(sys.settrace, sys.gettrace()) | 
 |         sys.settrace(tracer) | 
 |         adaptgetter("foo", TestClass, (1, "")) | 
 |         sys.settrace(None) | 
 |  | 
 |         self.assertRaises(TypeError, sys.settrace) | 
 |  | 
 |     def testEvalExecFreeVars(self): | 
 |  | 
 |         def f(x): | 
 |             return lambda: x + 1 | 
 |  | 
 |         g = f(3) | 
 |         self.assertRaises(TypeError, eval, g.__code__) | 
 |  | 
 |         try: | 
 |             exec(g.__code__, {}) | 
 |         except TypeError: | 
 |             pass | 
 |         else: | 
 |             self.fail("exec should have failed, because code contained free vars") | 
 |  | 
 |     def testListCompLocalVars(self): | 
 |  | 
 |         try: | 
 |             print(bad) | 
 |         except NameError: | 
 |             pass | 
 |         else: | 
 |             print("bad should not be defined") | 
 |  | 
 |         def x(): | 
 |             [bad for s in 'a b' for bad in s.split()] | 
 |  | 
 |         x() | 
 |         try: | 
 |             print(bad) | 
 |         except NameError: | 
 |             pass | 
 |  | 
 |     def testEvalFreeVars(self): | 
 |  | 
 |         def f(x): | 
 |             def g(): | 
 |                 x | 
 |                 eval("x + 1") | 
 |             return g | 
 |  | 
 |         f(4)() | 
 |  | 
 |     def testFreeingCell(self): | 
 |         # Test what happens when a finalizer accesses | 
 |         # the cell where the object was stored. | 
 |         class Special: | 
 |             def __del__(self): | 
 |                 nestedcell_get() | 
 |  | 
 |     def testNonLocalFunction(self): | 
 |  | 
 |         def f(x): | 
 |             def inc(): | 
 |                 nonlocal x | 
 |                 x += 1 | 
 |                 return x | 
 |             def dec(): | 
 |                 nonlocal x | 
 |                 x -= 1 | 
 |                 return x | 
 |             return inc, dec | 
 |  | 
 |         inc, dec = f(0) | 
 |         self.assertEqual(inc(), 1) | 
 |         self.assertEqual(inc(), 2) | 
 |         self.assertEqual(dec(), 1) | 
 |         self.assertEqual(dec(), 0) | 
 |  | 
 |     def testNonLocalMethod(self): | 
 |         def f(x): | 
 |             class c: | 
 |                 def inc(self): | 
 |                     nonlocal x | 
 |                     x += 1 | 
 |                     return x | 
 |                 def dec(self): | 
 |                     nonlocal x | 
 |                     x -= 1 | 
 |                     return x | 
 |             return c() | 
 |         c = f(0) | 
 |         self.assertEqual(c.inc(), 1) | 
 |         self.assertEqual(c.inc(), 2) | 
 |         self.assertEqual(c.dec(), 1) | 
 |         self.assertEqual(c.dec(), 0) | 
 |  | 
 |     def testGlobalInParallelNestedFunctions(self): | 
 |         # A symbol table bug leaked the global statement from one | 
 |         # function to other nested functions in the same block. | 
 |         # This test verifies that a global statement in the first | 
 |         # function does not affect the second function. | 
 |         local_ns = {} | 
 |         global_ns = {} | 
 |         exec("""if 1: | 
 |             def f(): | 
 |                 y = 1 | 
 |                 def g(): | 
 |                     global y | 
 |                     return y | 
 |                 def h(): | 
 |                     return y + 1 | 
 |                 return g, h | 
 |             y = 9 | 
 |             g, h = f() | 
 |             result9 = g() | 
 |             result2 = h() | 
 |             """, local_ns, global_ns) | 
 |         self.assertEqual(2, global_ns["result2"]) | 
 |         self.assertEqual(9, global_ns["result9"]) | 
 |  | 
 |     def testNonLocalClass(self): | 
 |  | 
 |         def f(x): | 
 |             class c: | 
 |                 nonlocal x | 
 |                 x += 1 | 
 |                 def get(self): | 
 |                     return x | 
 |             return c() | 
 |  | 
 |         c = f(0) | 
 |         self.assertEqual(c.get(), 1) | 
 |         self.assertNotIn("x", c.__class__.__dict__) | 
 |  | 
 |  | 
 |     def testNonLocalGenerator(self): | 
 |  | 
 |         def f(x): | 
 |             def g(y): | 
 |                 nonlocal x | 
 |                 for i in range(y): | 
 |                     x += 1 | 
 |                     yield x | 
 |             return g | 
 |  | 
 |         g = f(0) | 
 |         self.assertEqual(list(g(5)), [1, 2, 3, 4, 5]) | 
 |  | 
 |     def testNestedNonLocal(self): | 
 |  | 
 |         def f(x): | 
 |             def g(): | 
 |                 nonlocal x | 
 |                 x -= 2 | 
 |                 def h(): | 
 |                     nonlocal x | 
 |                     x += 4 | 
 |                     return x | 
 |                 return h | 
 |             return g | 
 |  | 
 |         g = f(1) | 
 |         h = g() | 
 |         self.assertEqual(h(), 3) | 
 |  | 
 |     def testTopIsNotSignificant(self): | 
 |         # See #9997. | 
 |         def top(a): | 
 |             pass | 
 |         def b(): | 
 |             global a | 
 |  | 
 |     def testClassNamespaceOverridesClosure(self): | 
 |         # See #17853. | 
 |         x = 42 | 
 |         class X: | 
 |             locals()["x"] = 43 | 
 |             y = x | 
 |         self.assertEqual(X.y, 43) | 
 |         class X: | 
 |             locals()["x"] = 43 | 
 |             del x | 
 |         self.assertFalse(hasattr(X, "x")) | 
 |         self.assertEqual(x, 42) | 
 |  | 
 |     @cpython_only | 
 |     def testCellLeak(self): | 
 |         # Issue 17927. | 
 |         # | 
 |         # The issue was that if self was part of a cycle involving the | 
 |         # frame of a method call, *and* the method contained a nested | 
 |         # function referencing self, thereby forcing 'self' into a | 
 |         # cell, setting self to None would not be enough to break the | 
 |         # frame -- the frame had another reference to the instance, | 
 |         # which could not be cleared by the code running in the frame | 
 |         # (though it will be cleared when the frame is collected). | 
 |         # Without the lambda, setting self to None is enough to break | 
 |         # the cycle. | 
 |         class Tester: | 
 |             def dig(self): | 
 |                 if 0: | 
 |                     lambda: self | 
 |                 try: | 
 |                     1/0 | 
 |                 except Exception as exc: | 
 |                     self.exc = exc | 
 |                 self = None  # Break the cycle | 
 |         tester = Tester() | 
 |         tester.dig() | 
 |         ref = weakref.ref(tester) | 
 |         del tester | 
 |         self.assertIsNone(ref()) | 
 |  | 
 |  | 
 | def test_main(): | 
 |     run_unittest(ScopeTests) | 
 |  | 
 | if __name__ == '__main__': | 
 |     test_main() |