test_scope.py revision 33d2689fc900a814f0a7d2f846abe0c34024ae17
1import unittest
2from test.test_support import check_syntax_error, run_unittest
3
4import warnings
5warnings.filterwarnings("ignore", r"import \*", SyntaxWarning, "<test string>")
6warnings.filterwarnings("ignore", r"import \*", SyntaxWarning, "<string>")
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 testRecursion(self):
170
171        def f(x):
172            def fact(n):
173                if n == 0:
174                    return 1
175                else:
176                    return n * fact(n - 1)
177            if x >= 0:
178                return fact(x)
179            else:
180                raise ValueError, "x must be >= 0"
181
182        self.assertEqual(f(6), 720)
183
184
185    def testUnoptimizedNamespaces(self):
186
187        check_syntax_error(self, """\
188def unoptimized_clash1(strip):
189    def f(s):
190        from sys import *
191        return getrefcount(s) # ambiguity: free or local
192    return f
193""")
194
195        check_syntax_error(self, """\
196def unoptimized_clash2():
197    from sys import *
198    def f(s):
199        return getrefcount(s) # ambiguity: global or local
200    return f
201""")
202
203        check_syntax_error(self, """\
204def unoptimized_clash2():
205    from sys import *
206    def g():
207        def f(s):
208            return getrefcount(s) # ambiguity: global or local
209        return f
210""")
211
212        check_syntax_error(self, """\
213def f(x):
214    def g():
215        return x
216    del x # can't del name
217""")
218
219        check_syntax_error(self, """\
220def f():
221    def g():
222        from sys import *
223        return getrefcount # global or local?
224""")
225
226    def testLambdas(self):
227
228        f1 = lambda x: lambda y: x + y
229        inc = f1(1)
230        plus10 = f1(10)
231        self.assertEqual(inc(1), 2)
232        self.assertEqual(plus10(5), 15)
233
234        f2 = lambda x: (lambda : lambda y: x + y)()
235        inc = f2(1)
236        plus10 = f2(10)
237        self.assertEqual(inc(1), 2)
238        self.assertEqual(plus10(5), 15)
239
240        f3 = lambda x: lambda y: global_x + y
241        global_x = 1
242        inc = f3(None)
243        self.assertEqual(inc(2), 3)
244
245        f8 = lambda x, y, z: lambda a, b, c: lambda : z * (b + y)
246        g = f8(1, 2, 3)
247        h = g(2, 4, 6)
248        self.assertEqual(h(), 18)
249
250    def testUnboundLocal(self):
251
252        def errorInOuter():
253            print(y)
254            def inner():
255                return y
256            y = 1
257
258        def errorInInner():
259            def inner():
260                return y
261            inner()
262            y = 1
263
264        try:
265            errorInOuter()
266        except UnboundLocalError:
267            pass
268        else:
269            self.fail()
270
271        try:
272            errorInInner()
273        except NameError:
274            pass
275        else:
276            self.fail()
277
278        # test for bug #1501934: incorrect LOAD/STORE_GLOBAL generation
279        exec("""
280global_x = 1
281def f():
282    global_x += 1
283try:
284    f()
285except UnboundLocalError:
286    pass
287else:
288    fail('scope of global_x not correctly determined')
289""", {'fail': self.fail})
290
291    def testComplexDefinitions(self):
292
293        def makeReturner(*lst):
294            def returner():
295                return lst
296            return returner
297
298        self.assertEqual(makeReturner(1,2,3)(), (1,2,3))
299
300        def makeReturner2(**kwargs):
301            def returner():
302                return kwargs
303            return returner
304
305        self.assertEqual(makeReturner2(a=11)()['a'], 11)
306
307    def testScopeOfGlobalStmt(self):
308# Examples posted by Samuele Pedroni to python-dev on 3/1/2001
309
310        exec("""\
311# I
312x = 7
313def f():
314    x = 1
315    def g():
316        global x
317        def i():
318            def h():
319                return x
320            return h()
321        return i()
322    return g()
323self.assertEqual(f(), 7)
324self.assertEqual(x, 7)
325
326# II
327x = 7
328def f():
329    x = 1
330    def g():
331        x = 2
332        def i():
333            def h():
334                return x
335            return h()
336        return i()
337    return g()
338self.assertEqual(f(), 2)
339self.assertEqual(x, 7)
340
341# III
342x = 7
343def f():
344    x = 1
345    def g():
346        global x
347        x = 2
348        def i():
349            def h():
350                return x
351            return h()
352        return i()
353    return g()
354self.assertEqual(f(), 2)
355self.assertEqual(x, 2)
356
357# IV
358x = 7
359def f():
360    x = 3
361    def g():
362        global x
363        x = 2
364        def i():
365            def h():
366                return x
367            return h()
368        return i()
369    return g()
370self.assertEqual(f(), 2)
371self.assertEqual(x, 2)
372
373# XXX what about global statements in class blocks?
374# do they affect methods?
375
376x = 12
377class Global:
378    global x
379    x = 13
380    def set(self, val):
381        x = val
382    def get(self):
383        return x
384
385g = Global()
386self.assertEqual(g.get(), 13)
387g.set(15)
388self.assertEqual(g.get(), 13)
389""")
390
391    def testLeaks(self):
392
393        class Foo:
394            count = 0
395
396            def __init__(self):
397                Foo.count += 1
398
399            def __del__(self):
400                Foo.count -= 1
401
402        def f1():
403            x = Foo()
404            def f2():
405                return x
406            f2()
407
408        for i in range(100):
409            f1()
410
411        self.assertEqual(Foo.count, 0)
412
413    def testClassAndGlobal(self):
414
415        exec("""\
416def test(x):
417    class Foo:
418        global x
419        def __call__(self, y):
420            return x + y
421    return Foo()
422
423x = 0
424self.assertEqual(test(6)(2), 8)
425x = -1
426self.assertEqual(test(3)(2), 5)
427
428looked_up_by_load_name = False
429class X:
430    # Implicit globals inside classes are be looked up by LOAD_NAME, not
431    # LOAD_GLOBAL.
432    locals()['looked_up_by_load_name'] = True
433    passed = looked_up_by_load_name
434
435self.assert_(X.passed)
436""")
437
438    def testLocalsFunction(self):
439
440        def f(x):
441            def g(y):
442                def h(z):
443                    return y + z
444                w = x + y
445                y += 3
446                return locals()
447            return g
448
449        d = f(2)(4)
450        self.assert_('h' in d)
451        del d['h']
452        self.assertEqual(d, {'x': 2, 'y': 7, 'w': 6})
453
454    def testLocalsClass(self):
455        # This test verifies that calling locals() does not pollute
456        # the local namespace of the class with free variables.  Old
457        # versions of Python had a bug, where a free variable being
458        # passed through a class namespace would be inserted into
459        # locals() by locals() or exec or a trace function.
460        #
461        # The real bug lies in frame code that copies variables
462        # between fast locals and the locals dict, e.g. when executing
463        # a trace function.
464
465        def f(x):
466            class C:
467                x = 12
468                def m(self):
469                    return x
470                locals()
471            return C
472
473        self.assertEqual(f(1).x, 12)
474
475        def f(x):
476            class C:
477                y = x
478                def m(self):
479                    return x
480                z = list(locals())
481            return C
482
483        varnames = f(1).z
484        self.assert_("x" not in varnames)
485        self.assert_("y" in varnames)
486
487    def testBoundAndFree(self):
488        # var is bound and free in class
489
490        def f(x):
491            class C:
492                def m(self):
493                    return x
494                a = x
495            return C
496
497        inst = f(3)()
498        self.assertEqual(inst.a, inst.m())
499
500    def testInteractionWithTraceFunc(self):
501
502        import sys
503        def tracer(a,b,c):
504            return tracer
505
506        def adaptgetter(name, klass, getter):
507            kind, des = getter
508            if kind == 1:       # AV happens when stepping from this line to next
509                if des == "":
510                    des = "_%s__%s" % (klass.__name__, name)
511                return lambda obj: getattr(obj, des)
512
513        class TestClass:
514            pass
515
516        sys.settrace(tracer)
517        adaptgetter("foo", TestClass, (1, ""))
518        sys.settrace(None)
519
520        self.assertRaises(TypeError, sys.settrace)
521
522    def testEvalExecFreeVars(self):
523
524        def f(x):
525            return lambda: x + 1
526
527        g = f(3)
528        self.assertRaises(TypeError, eval, g.__code__)
529
530        try:
531            exec(g.__code__, {})
532        except TypeError:
533            pass
534        else:
535            self.fail("exec should have failed, because code contained free vars")
536
537    def testListCompLocalVars(self):
538
539        try:
540            print(bad)
541        except NameError:
542            pass
543        else:
544            print("bad should not be defined")
545
546        def x():
547            [bad for s in 'a b' for bad in s.split()]
548
549        x()
550        try:
551            print(bad)
552        except NameError:
553            pass
554
555    def testEvalFreeVars(self):
556
557        def f(x):
558            def g():
559                x
560                eval("x + 1")
561            return g
562
563        f(4)()
564
565    def testNonLocalFunction(self):
566
567        def f(x):
568            def inc():
569                nonlocal x
570                x += 1
571                return x
572            def dec():
573                nonlocal x
574                x -= 1
575                return x
576            return inc, dec
577
578        inc, dec = f(0)
579        self.assertEqual(inc(), 1)
580        self.assertEqual(inc(), 2)
581        self.assertEqual(dec(), 1)
582        self.assertEqual(dec(), 0)
583
584    def testNonLocalMethod(self):
585
586        def f(x):
587            class c:
588                def inc(self):
589                    nonlocal x
590                    x += 1
591                    return x
592                def dec(self):
593                    nonlocal x
594                    x -= 1
595                    return x
596            return c()
597
598        c = f(0)
599        self.assertEqual(c.inc(), 1)
600        self.assertEqual(c.inc(), 2)
601        self.assertEqual(c.dec(), 1)
602        self.assertEqual(c.dec(), 0)
603
604    def testNonLocalClass(self):
605
606        def f(x):
607            class c:
608                nonlocal x
609                x += 1
610                def get(self):
611                    return x
612            return c()
613
614        c = f(0)
615        self.assertEqual(c.get(), 1)
616        self.assert_("x" not in c.__class__.__dict__)
617
618
619    def testNonLocalGenerator(self):
620
621        def f(x):
622            def g(y):
623                nonlocal x
624                for i in range(y):
625                    x += 1
626                    yield x
627            return g
628
629        g = f(0)
630        self.assertEqual(list(g(5)), [1, 2, 3, 4, 5])
631
632    def testNestedNonLocal(self):
633
634        def f(x):
635            def g():
636                nonlocal x
637                x -= 2
638                def h():
639                    nonlocal x
640                    x += 4
641                    return x
642                return h
643            return g
644
645        g = f(1)
646        h = g()
647        self.assertEqual(h(), 3)
648
649
650def test_main():
651    run_unittest(ScopeTests)
652
653if __name__ == '__main__':
654    test_main()
655