• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import unittest
2import weakref
3
4from test.support import check_syntax_error, cpython_only
5from test.support import gc_collect
6
7
8class ScopeTests(unittest.TestCase):
9
10    def testSimpleNesting(self):
11
12        def make_adder(x):
13            def adder(y):
14                return x + y
15            return adder
16
17        inc = make_adder(1)
18        plus10 = make_adder(10)
19
20        self.assertEqual(inc(1), 2)
21        self.assertEqual(plus10(-2), 8)
22
23    def testExtraNesting(self):
24
25        def make_adder2(x):
26            def extra(): # check freevars passing through non-use scopes
27                def adder(y):
28                    return x + y
29                return adder
30            return extra()
31
32        inc = make_adder2(1)
33        plus10 = make_adder2(10)
34
35        self.assertEqual(inc(1), 2)
36        self.assertEqual(plus10(-2), 8)
37
38    def testSimpleAndRebinding(self):
39
40        def make_adder3(x):
41            def adder(y):
42                return x + y
43            x = x + 1 # check tracking of assignment to x in defining scope
44            return adder
45
46        inc = make_adder3(0)
47        plus10 = make_adder3(9)
48
49        self.assertEqual(inc(1), 2)
50        self.assertEqual(plus10(-2), 8)
51
52    def testNestingGlobalNoFree(self):
53
54        def make_adder4(): # XXX add exta level of indirection
55            def nest():
56                def nest():
57                    def adder(y):
58                        return global_x + y # check that plain old globals work
59                    return adder
60                return nest()
61            return nest()
62
63        global_x = 1
64        adder = make_adder4()
65        self.assertEqual(adder(1), 2)
66
67        global_x = 10
68        self.assertEqual(adder(-2), 8)
69
70    def testNestingThroughClass(self):
71
72        def make_adder5(x):
73            class Adder:
74                def __call__(self, y):
75                    return x + y
76            return Adder()
77
78        inc = make_adder5(1)
79        plus10 = make_adder5(10)
80
81        self.assertEqual(inc(1), 2)
82        self.assertEqual(plus10(-2), 8)
83
84    def testNestingPlusFreeRefToGlobal(self):
85
86        def make_adder6(x):
87            global global_nest_x
88            def adder(y):
89                return global_nest_x + y
90            global_nest_x = x
91            return adder
92
93        inc = make_adder6(1)
94        plus10 = make_adder6(10)
95
96        self.assertEqual(inc(1), 11) # there's only one global
97        self.assertEqual(plus10(-2), 8)
98
99    def testNearestEnclosingScope(self):
100
101        def f(x):
102            def g(y):
103                x = 42 # check that this masks binding in f()
104                def h(z):
105                    return x + z
106                return h
107            return g(2)
108
109        test_func = f(10)
110        self.assertEqual(test_func(5), 47)
111
112    def testMixedFreevarsAndCellvars(self):
113
114        def identity(x):
115            return x
116
117        def f(x, y, z):
118            def g(a, b, c):
119                a = a + x # 3
120                def h():
121                    # z * (4 + 9)
122                    # 3 * 13
123                    return identity(z * (b + y))
124                y = c + z # 9
125                return h
126            return g
127
128        g = f(1, 2, 3)
129        h = g(2, 4, 6)
130        self.assertEqual(h(), 39)
131
132    def testFreeVarInMethod(self):
133
134        def test():
135            method_and_var = "var"
136            class Test:
137                def method_and_var(self):
138                    return "method"
139                def test(self):
140                    return method_and_var
141                def actual_global(self):
142                    return str("global")
143                def str(self):
144                    return str(self)
145            return Test()
146
147        t = test()
148        self.assertEqual(t.test(), "var")
149        self.assertEqual(t.method_and_var(), "method")
150        self.assertEqual(t.actual_global(), "global")
151
152        method_and_var = "var"
153        class Test:
154            # this class is not nested, so the rules are different
155            def method_and_var(self):
156                return "method"
157            def test(self):
158                return method_and_var
159            def actual_global(self):
160                return str("global")
161            def str(self):
162                return str(self)
163
164        t = Test()
165        self.assertEqual(t.test(), "var")
166        self.assertEqual(t.method_and_var(), "method")
167        self.assertEqual(t.actual_global(), "global")
168
169    def testCellIsKwonlyArg(self):
170        # Issue 1409: Initialisation of a cell value,
171        # when it comes from a keyword-only parameter
172        def foo(*, a=17):
173            def bar():
174                return a + 5
175            return bar() + 3
176
177        self.assertEqual(foo(a=42), 50)
178        self.assertEqual(foo(), 25)
179
180    def testRecursion(self):
181
182        def f(x):
183            def fact(n):
184                if n == 0:
185                    return 1
186                else:
187                    return n * fact(n - 1)
188            if x >= 0:
189                return fact(x)
190            else:
191                raise ValueError("x must be >= 0")
192
193        self.assertEqual(f(6), 720)
194
195
196    def testUnoptimizedNamespaces(self):
197
198        check_syntax_error(self, """if 1:
199            def unoptimized_clash1(strip):
200                def f(s):
201                    from sys import *
202                    return getrefcount(s) # ambiguity: free or local
203                return f
204            """)
205
206        check_syntax_error(self, """if 1:
207            def unoptimized_clash2():
208                from sys import *
209                def f(s):
210                    return getrefcount(s) # ambiguity: global or local
211                return f
212            """)
213
214        check_syntax_error(self, """if 1:
215            def unoptimized_clash2():
216                from sys import *
217                def g():
218                    def f(s):
219                        return getrefcount(s) # ambiguity: global or local
220                    return f
221            """)
222
223        check_syntax_error(self, """if 1:
224            def f():
225                def g():
226                    from sys import *
227                    return getrefcount # global or local?
228            """)
229
230    def testLambdas(self):
231
232        f1 = lambda x: lambda y: x + y
233        inc = f1(1)
234        plus10 = f1(10)
235        self.assertEqual(inc(1), 2)
236        self.assertEqual(plus10(5), 15)
237
238        f2 = lambda x: (lambda : lambda y: x + y)()
239        inc = f2(1)
240        plus10 = f2(10)
241        self.assertEqual(inc(1), 2)
242        self.assertEqual(plus10(5), 15)
243
244        f3 = lambda x: lambda y: global_x + y
245        global_x = 1
246        inc = f3(None)
247        self.assertEqual(inc(2), 3)
248
249        f8 = lambda x, y, z: lambda a, b, c: lambda : z * (b + y)
250        g = f8(1, 2, 3)
251        h = g(2, 4, 6)
252        self.assertEqual(h(), 18)
253
254    def testUnboundLocal(self):
255
256        def errorInOuter():
257            print(y)
258            def inner():
259                return y
260            y = 1
261
262        def errorInInner():
263            def inner():
264                return y
265            inner()
266            y = 1
267
268        self.assertRaises(UnboundLocalError, errorInOuter)
269        self.assertRaises(NameError, errorInInner)
270
271    def testUnboundLocal_AfterDel(self):
272        # #4617: It is now legal to delete a cell variable.
273        # The following functions must obviously compile,
274        # and give the correct error when accessing the deleted name.
275        def errorInOuter():
276            y = 1
277            del y
278            print(y)
279            def inner():
280                return y
281
282        def errorInInner():
283            def inner():
284                return y
285            y = 1
286            del y
287            inner()
288
289        self.assertRaises(UnboundLocalError, errorInOuter)
290        self.assertRaises(NameError, errorInInner)
291
292    def testUnboundLocal_AugAssign(self):
293        # test for bug #1501934: incorrect LOAD/STORE_GLOBAL generation
294        exec("""if 1:
295            global_x = 1
296            def f():
297                global_x += 1
298            try:
299                f()
300            except UnboundLocalError:
301                pass
302            else:
303                fail('scope of global_x not correctly determined')
304            """, {'fail': self.fail})
305
306    def testComplexDefinitions(self):
307
308        def makeReturner(*lst):
309            def returner():
310                return lst
311            return returner
312
313        self.assertEqual(makeReturner(1,2,3)(), (1,2,3))
314
315        def makeReturner2(**kwargs):
316            def returner():
317                return kwargs
318            return returner
319
320        self.assertEqual(makeReturner2(a=11)()['a'], 11)
321
322    def testScopeOfGlobalStmt(self):
323        # Examples posted by Samuele Pedroni to python-dev on 3/1/2001
324
325        exec("""if 1:
326            # I
327            x = 7
328            def f():
329                x = 1
330                def g():
331                    global x
332                    def i():
333                        def h():
334                            return x
335                        return h()
336                    return i()
337                return g()
338            self.assertEqual(f(), 7)
339            self.assertEqual(x, 7)
340
341            # II
342            x = 7
343            def f():
344                x = 1
345                def g():
346                    x = 2
347                    def i():
348                        def h():
349                            return x
350                        return h()
351                    return i()
352                return g()
353            self.assertEqual(f(), 2)
354            self.assertEqual(x, 7)
355
356            # III
357            x = 7
358            def f():
359                x = 1
360                def g():
361                    global x
362                    x = 2
363                    def i():
364                        def h():
365                            return x
366                        return h()
367                    return i()
368                return g()
369            self.assertEqual(f(), 2)
370            self.assertEqual(x, 2)
371
372            # IV
373            x = 7
374            def f():
375                x = 3
376                def g():
377                    global x
378                    x = 2
379                    def i():
380                        def h():
381                            return x
382                        return h()
383                    return i()
384                return g()
385            self.assertEqual(f(), 2)
386            self.assertEqual(x, 2)
387
388            # XXX what about global statements in class blocks?
389            # do they affect methods?
390
391            x = 12
392            class Global:
393                global x
394                x = 13
395                def set(self, val):
396                    x = val
397                def get(self):
398                    return x
399
400            g = Global()
401            self.assertEqual(g.get(), 13)
402            g.set(15)
403            self.assertEqual(g.get(), 13)
404            """)
405
406    def testLeaks(self):
407
408        class Foo:
409            count = 0
410
411            def __init__(self):
412                Foo.count += 1
413
414            def __del__(self):
415                Foo.count -= 1
416
417        def f1():
418            x = Foo()
419            def f2():
420                return x
421            f2()
422
423        for i in range(100):
424            f1()
425
426        gc_collect()  # For PyPy or other GCs.
427        self.assertEqual(Foo.count, 0)
428
429    def testClassAndGlobal(self):
430
431        exec("""if 1:
432            def test(x):
433                class Foo:
434                    global x
435                    def __call__(self, y):
436                        return x + y
437                return Foo()
438
439            x = 0
440            self.assertEqual(test(6)(2), 8)
441            x = -1
442            self.assertEqual(test(3)(2), 5)
443
444            looked_up_by_load_name = False
445            class X:
446                # Implicit globals inside classes are be looked up by LOAD_NAME, not
447                # LOAD_GLOBAL.
448                locals()['looked_up_by_load_name'] = True
449                passed = looked_up_by_load_name
450
451            self.assertTrue(X.passed)
452            """)
453
454    def testLocalsFunction(self):
455
456        def f(x):
457            def g(y):
458                def h(z):
459                    return y + z
460                w = x + y
461                y += 3
462                return locals()
463            return g
464
465        d = f(2)(4)
466        self.assertIn('h', d)
467        del d['h']
468        self.assertEqual(d, {'x': 2, 'y': 7, 'w': 6})
469
470    def testLocalsClass(self):
471        # This test verifies that calling locals() does not pollute
472        # the local namespace of the class with free variables.  Old
473        # versions of Python had a bug, where a free variable being
474        # passed through a class namespace would be inserted into
475        # locals() by locals() or exec or a trace function.
476        #
477        # The real bug lies in frame code that copies variables
478        # between fast locals and the locals dict, e.g. when executing
479        # a trace function.
480
481        def f(x):
482            class C:
483                x = 12
484                def m(self):
485                    return x
486                locals()
487            return C
488
489        self.assertEqual(f(1).x, 12)
490
491        def f(x):
492            class C:
493                y = x
494                def m(self):
495                    return x
496                z = list(locals())
497            return C
498
499        varnames = f(1).z
500        self.assertNotIn("x", varnames)
501        self.assertIn("y", varnames)
502
503    @cpython_only
504    def testLocalsClass_WithTrace(self):
505        # Issue23728: after the trace function returns, the locals()
506        # dictionary is used to update all variables, this used to
507        # include free variables. But in class statements, free
508        # variables are not inserted...
509        import sys
510        self.addCleanup(sys.settrace, sys.gettrace())
511        sys.settrace(lambda a,b,c:None)
512        x = 12
513
514        class C:
515            def f(self):
516                return x
517
518        self.assertEqual(x, 12) # Used to raise UnboundLocalError
519
520    def testBoundAndFree(self):
521        # var is bound and free in class
522
523        def f(x):
524            class C:
525                def m(self):
526                    return x
527                a = x
528            return C
529
530        inst = f(3)()
531        self.assertEqual(inst.a, inst.m())
532
533    @cpython_only
534    def testInteractionWithTraceFunc(self):
535
536        import sys
537        def tracer(a,b,c):
538            return tracer
539
540        def adaptgetter(name, klass, getter):
541            kind, des = getter
542            if kind == 1:       # AV happens when stepping from this line to next
543                if des == "":
544                    des = "_%s__%s" % (klass.__name__, name)
545                return lambda obj: getattr(obj, des)
546
547        class TestClass:
548            pass
549
550        self.addCleanup(sys.settrace, sys.gettrace())
551        sys.settrace(tracer)
552        adaptgetter("foo", TestClass, (1, ""))
553        sys.settrace(None)
554
555        self.assertRaises(TypeError, sys.settrace)
556
557    def testEvalExecFreeVars(self):
558
559        def f(x):
560            return lambda: x + 1
561
562        g = f(3)
563        self.assertRaises(TypeError, eval, g.__code__)
564
565        try:
566            exec(g.__code__, {})
567        except TypeError:
568            pass
569        else:
570            self.fail("exec should have failed, because code contained free vars")
571
572    def testListCompLocalVars(self):
573
574        try:
575            print(bad)
576        except NameError:
577            pass
578        else:
579            print("bad should not be defined")
580
581        def x():
582            [bad for s in 'a b' for bad in s.split()]
583
584        x()
585        try:
586            print(bad)
587        except NameError:
588            pass
589
590    def testEvalFreeVars(self):
591
592        def f(x):
593            def g():
594                x
595                eval("x + 1")
596            return g
597
598        f(4)()
599
600    def testFreeingCell(self):
601        # Test what happens when a finalizer accesses
602        # the cell where the object was stored.
603        class Special:
604            def __del__(self):
605                nestedcell_get()
606
607    def testNonLocalFunction(self):
608
609        def f(x):
610            def inc():
611                nonlocal x
612                x += 1
613                return x
614            def dec():
615                nonlocal x
616                x -= 1
617                return x
618            return inc, dec
619
620        inc, dec = f(0)
621        self.assertEqual(inc(), 1)
622        self.assertEqual(inc(), 2)
623        self.assertEqual(dec(), 1)
624        self.assertEqual(dec(), 0)
625
626    def testNonLocalMethod(self):
627        def f(x):
628            class c:
629                def inc(self):
630                    nonlocal x
631                    x += 1
632                    return x
633                def dec(self):
634                    nonlocal x
635                    x -= 1
636                    return x
637            return c()
638        c = f(0)
639        self.assertEqual(c.inc(), 1)
640        self.assertEqual(c.inc(), 2)
641        self.assertEqual(c.dec(), 1)
642        self.assertEqual(c.dec(), 0)
643
644    def testGlobalInParallelNestedFunctions(self):
645        # A symbol table bug leaked the global statement from one
646        # function to other nested functions in the same block.
647        # This test verifies that a global statement in the first
648        # function does not affect the second function.
649        local_ns = {}
650        global_ns = {}
651        exec("""if 1:
652            def f():
653                y = 1
654                def g():
655                    global y
656                    return y
657                def h():
658                    return y + 1
659                return g, h
660            y = 9
661            g, h = f()
662            result9 = g()
663            result2 = h()
664            """, local_ns, global_ns)
665        self.assertEqual(2, global_ns["result2"])
666        self.assertEqual(9, global_ns["result9"])
667
668    def testNonLocalClass(self):
669
670        def f(x):
671            class c:
672                nonlocal x
673                x += 1
674                def get(self):
675                    return x
676            return c()
677
678        c = f(0)
679        self.assertEqual(c.get(), 1)
680        self.assertNotIn("x", c.__class__.__dict__)
681
682
683    def testNonLocalGenerator(self):
684
685        def f(x):
686            def g(y):
687                nonlocal x
688                for i in range(y):
689                    x += 1
690                    yield x
691            return g
692
693        g = f(0)
694        self.assertEqual(list(g(5)), [1, 2, 3, 4, 5])
695
696    def testNestedNonLocal(self):
697
698        def f(x):
699            def g():
700                nonlocal x
701                x -= 2
702                def h():
703                    nonlocal x
704                    x += 4
705                    return x
706                return h
707            return g
708
709        g = f(1)
710        h = g()
711        self.assertEqual(h(), 3)
712
713    def testTopIsNotSignificant(self):
714        # See #9997.
715        def top(a):
716            pass
717        def b():
718            global a
719
720    def testClassNamespaceOverridesClosure(self):
721        # See #17853.
722        x = 42
723        class X:
724            locals()["x"] = 43
725            y = x
726        self.assertEqual(X.y, 43)
727        class X:
728            locals()["x"] = 43
729            del x
730        self.assertFalse(hasattr(X, "x"))
731        self.assertEqual(x, 42)
732
733    @cpython_only
734    def testCellLeak(self):
735        # Issue 17927.
736        #
737        # The issue was that if self was part of a cycle involving the
738        # frame of a method call, *and* the method contained a nested
739        # function referencing self, thereby forcing 'self' into a
740        # cell, setting self to None would not be enough to break the
741        # frame -- the frame had another reference to the instance,
742        # which could not be cleared by the code running in the frame
743        # (though it will be cleared when the frame is collected).
744        # Without the lambda, setting self to None is enough to break
745        # the cycle.
746        class Tester:
747            def dig(self):
748                if 0:
749                    lambda: self
750                try:
751                    1/0
752                except Exception as exc:
753                    self.exc = exc
754                self = None  # Break the cycle
755        tester = Tester()
756        tester.dig()
757        ref = weakref.ref(tester)
758        del tester
759        gc_collect()  # For PyPy or other GCs.
760        self.assertIsNone(ref())
761
762
763if __name__ == '__main__':
764    unittest.main()
765