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