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