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