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