python (3.12.0)
1 import dis
2 import math
3 import os
4 import unittest
5 import sys
6 import ast
7 import _ast
8 import tempfile
9 import types
10 import textwrap
11 import warnings
12 from test import support
13 from test.support import (script_helper, requires_debug_ranges,
14 requires_specialization, C_RECURSION_LIMIT)
15 from test.support.os_helper import FakePath
16
17 class ESC[4;38;5;81mTestSpecifics(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
18
19 def compile_single(self, source):
20 compile(source, "<single>", "single")
21
22 def assertInvalidSingle(self, source):
23 self.assertRaises(SyntaxError, self.compile_single, source)
24
25 def test_no_ending_newline(self):
26 compile("hi", "<test>", "exec")
27 compile("hi\r", "<test>", "exec")
28
29 def test_empty(self):
30 compile("", "<test>", "exec")
31
32 def test_other_newlines(self):
33 compile("\r\n", "<test>", "exec")
34 compile("\r", "<test>", "exec")
35 compile("hi\r\nstuff\r\ndef f():\n pass\r", "<test>", "exec")
36 compile("this_is\rreally_old_mac\rdef f():\n pass", "<test>", "exec")
37
38 def test_debug_assignment(self):
39 # catch assignments to __debug__
40 self.assertRaises(SyntaxError, compile, '__debug__ = 1', '?', 'single')
41 import builtins
42 prev = builtins.__debug__
43 setattr(builtins, '__debug__', 'sure')
44 self.assertEqual(__debug__, prev)
45 setattr(builtins, '__debug__', prev)
46
47 def test_argument_handling(self):
48 # detect duplicate positional and keyword arguments
49 self.assertRaises(SyntaxError, eval, 'lambda a,a:0')
50 self.assertRaises(SyntaxError, eval, 'lambda a,a=1:0')
51 self.assertRaises(SyntaxError, eval, 'lambda a=1,a=1:0')
52 self.assertRaises(SyntaxError, exec, 'def f(a, a): pass')
53 self.assertRaises(SyntaxError, exec, 'def f(a = 0, a = 1): pass')
54 self.assertRaises(SyntaxError, exec, 'def f(a): global a; a = 1')
55
56 def test_syntax_error(self):
57 self.assertRaises(SyntaxError, compile, "1+*3", "filename", "exec")
58
59 def test_none_keyword_arg(self):
60 self.assertRaises(SyntaxError, compile, "f(None=1)", "<string>", "exec")
61
62 def test_duplicate_global_local(self):
63 self.assertRaises(SyntaxError, exec, 'def f(a): global a; a = 1')
64
65 def test_exec_with_general_mapping_for_locals(self):
66
67 class ESC[4;38;5;81mM:
68 "Test mapping interface versus possible calls from eval()."
69 def __getitem__(self, key):
70 if key == 'a':
71 return 12
72 raise KeyError
73 def __setitem__(self, key, value):
74 self.results = (key, value)
75 def keys(self):
76 return list('xyz')
77
78 m = M()
79 g = globals()
80 exec('z = a', g, m)
81 self.assertEqual(m.results, ('z', 12))
82 try:
83 exec('z = b', g, m)
84 except NameError:
85 pass
86 else:
87 self.fail('Did not detect a KeyError')
88 exec('z = dir()', g, m)
89 self.assertEqual(m.results, ('z', list('xyz')))
90 exec('z = globals()', g, m)
91 self.assertEqual(m.results, ('z', g))
92 exec('z = locals()', g, m)
93 self.assertEqual(m.results, ('z', m))
94 self.assertRaises(TypeError, exec, 'z = b', m)
95
96 class ESC[4;38;5;81mA:
97 "Non-mapping"
98 pass
99 m = A()
100 self.assertRaises(TypeError, exec, 'z = a', g, m)
101
102 # Verify that dict subclasses work as well
103 class ESC[4;38;5;81mD(ESC[4;38;5;149mdict):
104 def __getitem__(self, key):
105 if key == 'a':
106 return 12
107 return dict.__getitem__(self, key)
108 d = D()
109 exec('z = a', g, d)
110 self.assertEqual(d['z'], 12)
111
112 @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
113 def test_extended_arg(self):
114 repeat = int(C_RECURSION_LIMIT * 0.9)
115 longexpr = 'x = x or ' + '-x' * repeat
116 g = {}
117 code = textwrap.dedent('''
118 def f(x):
119 %s
120 %s
121 %s
122 %s
123 %s
124 %s
125 %s
126 %s
127 %s
128 %s
129 # the expressions above have no effect, x == argument
130 while x:
131 x -= 1
132 # EXTENDED_ARG/JUMP_ABSOLUTE here
133 return x
134 ''' % ((longexpr,)*10))
135 exec(code, g)
136 self.assertEqual(g['f'](5), 0)
137
138 def test_argument_order(self):
139 self.assertRaises(SyntaxError, exec, 'def f(a=1, b): pass')
140
141 def test_float_literals(self):
142 # testing bad float literals
143 self.assertRaises(SyntaxError, eval, "2e")
144 self.assertRaises(SyntaxError, eval, "2.0e+")
145 self.assertRaises(SyntaxError, eval, "1e-")
146 self.assertRaises(SyntaxError, eval, "3-4e/21")
147
148 def test_indentation(self):
149 # testing compile() of indented block w/o trailing newline"
150 s = textwrap.dedent("""
151 if 1:
152 if 2:
153 pass
154 """)
155 compile(s, "<string>", "exec")
156
157 # This test is probably specific to CPython and may not generalize
158 # to other implementations. We are trying to ensure that when
159 # the first line of code starts after 256, correct line numbers
160 # in tracebacks are still produced.
161 def test_leading_newlines(self):
162 s256 = "".join(["\n"] * 256 + ["spam"])
163 co = compile(s256, 'fn', 'exec')
164 self.assertEqual(co.co_firstlineno, 1)
165 lines = [line for _, _, line in co.co_lines()]
166 self.assertEqual(lines, [0, 257])
167
168 def test_literals_with_leading_zeroes(self):
169 for arg in ["077787", "0xj", "0x.", "0e", "090000000000000",
170 "080000000000000", "000000000000009", "000000000000008",
171 "0b42", "0BADCAFE", "0o123456789", "0b1.1", "0o4.2",
172 "0b101j", "0o153j", "0b100e1", "0o777e1", "0777",
173 "000777", "000000000000007"]:
174 self.assertRaises(SyntaxError, eval, arg)
175
176 self.assertEqual(eval("0xff"), 255)
177 self.assertEqual(eval("0777."), 777)
178 self.assertEqual(eval("0777.0"), 777)
179 self.assertEqual(eval("000000000000000000000000000000000000000000000000000777e0"), 777)
180 self.assertEqual(eval("0777e1"), 7770)
181 self.assertEqual(eval("0e0"), 0)
182 self.assertEqual(eval("0000e-012"), 0)
183 self.assertEqual(eval("09.5"), 9.5)
184 self.assertEqual(eval("0777j"), 777j)
185 self.assertEqual(eval("000"), 0)
186 self.assertEqual(eval("00j"), 0j)
187 self.assertEqual(eval("00.0"), 0)
188 self.assertEqual(eval("0e3"), 0)
189 self.assertEqual(eval("090000000000000."), 90000000000000.)
190 self.assertEqual(eval("090000000000000.0000000000000000000000"), 90000000000000.)
191 self.assertEqual(eval("090000000000000e0"), 90000000000000.)
192 self.assertEqual(eval("090000000000000e-0"), 90000000000000.)
193 self.assertEqual(eval("090000000000000j"), 90000000000000j)
194 self.assertEqual(eval("000000000000008."), 8.)
195 self.assertEqual(eval("000000000000009."), 9.)
196 self.assertEqual(eval("0b101010"), 42)
197 self.assertEqual(eval("-0b000000000010"), -2)
198 self.assertEqual(eval("0o777"), 511)
199 self.assertEqual(eval("-0o0000010"), -8)
200
201 def test_int_literals_too_long(self):
202 n = 3000
203 source = f"a = 1\nb = 2\nc = {'3'*n}\nd = 4"
204 with support.adjust_int_max_str_digits(n):
205 compile(source, "<long_int_pass>", "exec") # no errors.
206 with support.adjust_int_max_str_digits(n-1):
207 with self.assertRaises(SyntaxError) as err_ctx:
208 compile(source, "<long_int_fail>", "exec")
209 exc = err_ctx.exception
210 self.assertEqual(exc.lineno, 3)
211 self.assertIn('Exceeds the limit ', str(exc))
212 self.assertIn(' Consider hexadecimal ', str(exc))
213
214 def test_unary_minus(self):
215 # Verify treatment of unary minus on negative numbers SF bug #660455
216 if sys.maxsize == 2147483647:
217 # 32-bit machine
218 all_one_bits = '0xffffffff'
219 self.assertEqual(eval(all_one_bits), 4294967295)
220 self.assertEqual(eval("-" + all_one_bits), -4294967295)
221 elif sys.maxsize == 9223372036854775807:
222 # 64-bit machine
223 all_one_bits = '0xffffffffffffffff'
224 self.assertEqual(eval(all_one_bits), 18446744073709551615)
225 self.assertEqual(eval("-" + all_one_bits), -18446744073709551615)
226 else:
227 self.fail("How many bits *does* this machine have???")
228 # Verify treatment of constant folding on -(sys.maxsize+1)
229 # i.e. -2147483648 on 32 bit platforms. Should return int.
230 self.assertIsInstance(eval("%s" % (-sys.maxsize - 1)), int)
231 self.assertIsInstance(eval("%s" % (-sys.maxsize - 2)), int)
232
233 if sys.maxsize == 9223372036854775807:
234 def test_32_63_bit_values(self):
235 a = +4294967296 # 1 << 32
236 b = -4294967296 # 1 << 32
237 c = +281474976710656 # 1 << 48
238 d = -281474976710656 # 1 << 48
239 e = +4611686018427387904 # 1 << 62
240 f = -4611686018427387904 # 1 << 62
241 g = +9223372036854775807 # 1 << 63 - 1
242 h = -9223372036854775807 # 1 << 63 - 1
243
244 for variable in self.test_32_63_bit_values.__code__.co_consts:
245 if variable is not None:
246 self.assertIsInstance(variable, int)
247
248 def test_sequence_unpacking_error(self):
249 # Verify sequence packing/unpacking with "or". SF bug #757818
250 i,j = (1, -1) or (-1, 1)
251 self.assertEqual(i, 1)
252 self.assertEqual(j, -1)
253
254 def test_none_assignment(self):
255 stmts = [
256 'None = 0',
257 'None += 0',
258 '__builtins__.None = 0',
259 'def None(): pass',
260 'class None: pass',
261 '(a, None) = 0, 0',
262 'for None in range(10): pass',
263 'def f(None): pass',
264 'import None',
265 'import x as None',
266 'from x import None',
267 'from x import y as None'
268 ]
269 for stmt in stmts:
270 stmt += "\n"
271 self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'single')
272 self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'exec')
273
274 def test_import(self):
275 succeed = [
276 'import sys',
277 'import os, sys',
278 'import os as bar',
279 'import os.path as bar',
280 'from __future__ import nested_scopes, generators',
281 'from __future__ import (nested_scopes,\ngenerators)',
282 'from __future__ import (nested_scopes,\ngenerators,)',
283 'from sys import stdin, stderr, stdout',
284 'from sys import (stdin, stderr,\nstdout)',
285 'from sys import (stdin, stderr,\nstdout,)',
286 'from sys import (stdin\n, stderr, stdout)',
287 'from sys import (stdin\n, stderr, stdout,)',
288 'from sys import stdin as si, stdout as so, stderr as se',
289 'from sys import (stdin as si, stdout as so, stderr as se)',
290 'from sys import (stdin as si, stdout as so, stderr as se,)',
291 ]
292 fail = [
293 'import (os, sys)',
294 'import (os), (sys)',
295 'import ((os), (sys))',
296 'import (sys',
297 'import sys)',
298 'import (os,)',
299 'import os As bar',
300 'import os.path a bar',
301 'from sys import stdin As stdout',
302 'from sys import stdin a stdout',
303 'from (sys) import stdin',
304 'from __future__ import (nested_scopes',
305 'from __future__ import nested_scopes)',
306 'from __future__ import nested_scopes,\ngenerators',
307 'from sys import (stdin',
308 'from sys import stdin)',
309 'from sys import stdin, stdout,\nstderr',
310 'from sys import stdin si',
311 'from sys import stdin,',
312 'from sys import (*)',
313 'from sys import (stdin,, stdout, stderr)',
314 'from sys import (stdin, stdout),',
315 ]
316 for stmt in succeed:
317 compile(stmt, 'tmp', 'exec')
318 for stmt in fail:
319 self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'exec')
320
321 def test_for_distinct_code_objects(self):
322 # SF bug 1048870
323 def f():
324 f1 = lambda x=1: x
325 f2 = lambda x=2: x
326 return f1, f2
327 f1, f2 = f()
328 self.assertNotEqual(id(f1.__code__), id(f2.__code__))
329
330 def test_lambda_doc(self):
331 l = lambda: "foo"
332 self.assertIsNone(l.__doc__)
333
334 def test_encoding(self):
335 code = b'# -*- coding: badencoding -*-\npass\n'
336 self.assertRaises(SyntaxError, compile, code, 'tmp', 'exec')
337 code = '# -*- coding: badencoding -*-\n"\xc2\xa4"\n'
338 compile(code, 'tmp', 'exec')
339 self.assertEqual(eval(code), '\xc2\xa4')
340 code = '"\xc2\xa4"\n'
341 self.assertEqual(eval(code), '\xc2\xa4')
342 code = b'"\xc2\xa4"\n'
343 self.assertEqual(eval(code), '\xa4')
344 code = b'# -*- coding: latin1 -*-\n"\xc2\xa4"\n'
345 self.assertEqual(eval(code), '\xc2\xa4')
346 code = b'# -*- coding: utf-8 -*-\n"\xc2\xa4"\n'
347 self.assertEqual(eval(code), '\xa4')
348 code = b'# -*- coding: iso8859-15 -*-\n"\xc2\xa4"\n'
349 self.assertEqual(eval(code), '\xc2\u20ac')
350 code = '"""\\\n# -*- coding: iso8859-15 -*-\n\xc2\xa4"""\n'
351 self.assertEqual(eval(code), '# -*- coding: iso8859-15 -*-\n\xc2\xa4')
352 code = b'"""\\\n# -*- coding: iso8859-15 -*-\n\xc2\xa4"""\n'
353 self.assertEqual(eval(code), '# -*- coding: iso8859-15 -*-\n\xa4')
354
355 def test_subscripts(self):
356 # SF bug 1448804
357 # Class to make testing subscript results easy
358 class ESC[4;38;5;81mstr_map(ESC[4;38;5;149mobject):
359 def __init__(self):
360 self.data = {}
361 def __getitem__(self, key):
362 return self.data[str(key)]
363 def __setitem__(self, key, value):
364 self.data[str(key)] = value
365 def __delitem__(self, key):
366 del self.data[str(key)]
367 def __contains__(self, key):
368 return str(key) in self.data
369 d = str_map()
370 # Index
371 d[1] = 1
372 self.assertEqual(d[1], 1)
373 d[1] += 1
374 self.assertEqual(d[1], 2)
375 del d[1]
376 self.assertNotIn(1, d)
377 # Tuple of indices
378 d[1, 1] = 1
379 self.assertEqual(d[1, 1], 1)
380 d[1, 1] += 1
381 self.assertEqual(d[1, 1], 2)
382 del d[1, 1]
383 self.assertNotIn((1, 1), d)
384 # Simple slice
385 d[1:2] = 1
386 self.assertEqual(d[1:2], 1)
387 d[1:2] += 1
388 self.assertEqual(d[1:2], 2)
389 del d[1:2]
390 self.assertNotIn(slice(1, 2), d)
391 # Tuple of simple slices
392 d[1:2, 1:2] = 1
393 self.assertEqual(d[1:2, 1:2], 1)
394 d[1:2, 1:2] += 1
395 self.assertEqual(d[1:2, 1:2], 2)
396 del d[1:2, 1:2]
397 self.assertNotIn((slice(1, 2), slice(1, 2)), d)
398 # Extended slice
399 d[1:2:3] = 1
400 self.assertEqual(d[1:2:3], 1)
401 d[1:2:3] += 1
402 self.assertEqual(d[1:2:3], 2)
403 del d[1:2:3]
404 self.assertNotIn(slice(1, 2, 3), d)
405 # Tuple of extended slices
406 d[1:2:3, 1:2:3] = 1
407 self.assertEqual(d[1:2:3, 1:2:3], 1)
408 d[1:2:3, 1:2:3] += 1
409 self.assertEqual(d[1:2:3, 1:2:3], 2)
410 del d[1:2:3, 1:2:3]
411 self.assertNotIn((slice(1, 2, 3), slice(1, 2, 3)), d)
412 # Ellipsis
413 d[...] = 1
414 self.assertEqual(d[...], 1)
415 d[...] += 1
416 self.assertEqual(d[...], 2)
417 del d[...]
418 self.assertNotIn(Ellipsis, d)
419 # Tuple of Ellipses
420 d[..., ...] = 1
421 self.assertEqual(d[..., ...], 1)
422 d[..., ...] += 1
423 self.assertEqual(d[..., ...], 2)
424 del d[..., ...]
425 self.assertNotIn((Ellipsis, Ellipsis), d)
426
427 def test_annotation_limit(self):
428 # more than 255 annotations, should compile ok
429 s = "def f(%s): pass"
430 s %= ', '.join('a%d:%d' % (i,i) for i in range(300))
431 compile(s, '?', 'exec')
432
433 def test_mangling(self):
434 class ESC[4;38;5;81mA:
435 def f():
436 __mangled = 1
437 __not_mangled__ = 2
438 import __mangled_mod
439 import __package__.module
440
441 self.assertIn("_A__mangled", A.f.__code__.co_varnames)
442 self.assertIn("__not_mangled__", A.f.__code__.co_varnames)
443 self.assertIn("_A__mangled_mod", A.f.__code__.co_varnames)
444 self.assertIn("__package__", A.f.__code__.co_varnames)
445
446 def test_compile_ast(self):
447 fname = __file__
448 if fname.lower().endswith('pyc'):
449 fname = fname[:-1]
450 with open(fname, encoding='utf-8') as f:
451 fcontents = f.read()
452 sample_code = [
453 ['<assign>', 'x = 5'],
454 ['<ifblock>', """if True:\n pass\n"""],
455 ['<forblock>', """for n in [1, 2, 3]:\n print(n)\n"""],
456 ['<deffunc>', """def foo():\n pass\nfoo()\n"""],
457 [fname, fcontents],
458 ]
459
460 for fname, code in sample_code:
461 co1 = compile(code, '%s1' % fname, 'exec')
462 ast = compile(code, '%s2' % fname, 'exec', _ast.PyCF_ONLY_AST)
463 self.assertTrue(type(ast) == _ast.Module)
464 co2 = compile(ast, '%s3' % fname, 'exec')
465 self.assertEqual(co1, co2)
466 # the code object's filename comes from the second compilation step
467 self.assertEqual(co2.co_filename, '%s3' % fname)
468
469 # raise exception when node type doesn't match with compile mode
470 co1 = compile('print(1)', '<string>', 'exec', _ast.PyCF_ONLY_AST)
471 self.assertRaises(TypeError, compile, co1, '<ast>', 'eval')
472
473 # raise exception when node type is no start node
474 self.assertRaises(TypeError, compile, _ast.If(), '<ast>', 'exec')
475
476 # raise exception when node has invalid children
477 ast = _ast.Module()
478 ast.body = [_ast.BoolOp()]
479 self.assertRaises(TypeError, compile, ast, '<ast>', 'exec')
480
481 def test_compile_invalid_typealias(self):
482 # gh-109341
483 m = ast.Module(
484 body=[
485 ast.TypeAlias(
486 name=ast.Subscript(
487 value=ast.Name(id="foo", ctx=ast.Load()),
488 slice=ast.Constant(value="x"),
489 ctx=ast.Store(),
490 ),
491 type_params=[],
492 value=ast.Name(id="Callable", ctx=ast.Load()),
493 )
494 ],
495 type_ignores=[],
496 )
497
498 with self.assertRaisesRegex(TypeError, "TypeAlias with non-Name name"):
499 compile(ast.fix_missing_locations(m), "<file>", "exec")
500
501 def test_dict_evaluation_order(self):
502 i = 0
503
504 def f():
505 nonlocal i
506 i += 1
507 return i
508
509 d = {f(): f(), f(): f()}
510 self.assertEqual(d, {1: 2, 3: 4})
511
512 def test_compile_filename(self):
513 for filename in 'file.py', b'file.py':
514 code = compile('pass', filename, 'exec')
515 self.assertEqual(code.co_filename, 'file.py')
516 for filename in bytearray(b'file.py'), memoryview(b'file.py'):
517 with self.assertRaises(TypeError):
518 compile('pass', filename, 'exec')
519 self.assertRaises(TypeError, compile, 'pass', list(b'file.py'), 'exec')
520
521 @support.cpython_only
522 def test_same_filename_used(self):
523 s = """def f(): pass\ndef g(): pass"""
524 c = compile(s, "myfile", "exec")
525 for obj in c.co_consts:
526 if isinstance(obj, types.CodeType):
527 self.assertIs(obj.co_filename, c.co_filename)
528
529 def test_single_statement(self):
530 self.compile_single("1 + 2")
531 self.compile_single("\n1 + 2")
532 self.compile_single("1 + 2\n")
533 self.compile_single("1 + 2\n\n")
534 self.compile_single("1 + 2\t\t\n")
535 self.compile_single("1 + 2\t\t\n ")
536 self.compile_single("1 + 2 # one plus two")
537 self.compile_single("1; 2")
538 self.compile_single("import sys; sys")
539 self.compile_single("def f():\n pass")
540 self.compile_single("while False:\n pass")
541 self.compile_single("if x:\n f(x)")
542 self.compile_single("if x:\n f(x)\nelse:\n g(x)")
543 self.compile_single("class T:\n pass")
544 self.compile_single("c = '''\na=1\nb=2\nc=3\n'''")
545
546 def test_bad_single_statement(self):
547 self.assertInvalidSingle('1\n2')
548 self.assertInvalidSingle('def f(): pass')
549 self.assertInvalidSingle('a = 13\nb = 187')
550 self.assertInvalidSingle('del x\ndel y')
551 self.assertInvalidSingle('f()\ng()')
552 self.assertInvalidSingle('f()\n# blah\nblah()')
553 self.assertInvalidSingle('f()\nxy # blah\nblah()')
554 self.assertInvalidSingle('x = 5 # comment\nx = 6\n')
555 self.assertInvalidSingle("c = '''\nd=1\n'''\na = 1\n\nb = 2\n")
556
557 def test_particularly_evil_undecodable(self):
558 # Issue 24022
559 src = b'0000\x00\n00000000000\n\x00\n\x9e\n'
560 with tempfile.TemporaryDirectory() as tmpd:
561 fn = os.path.join(tmpd, "bad.py")
562 with open(fn, "wb") as fp:
563 fp.write(src)
564 res = script_helper.run_python_until_end(fn)[0]
565 self.assertIn(b"source code cannot contain null bytes", res.err)
566
567 def test_yet_more_evil_still_undecodable(self):
568 # Issue #25388
569 src = b"#\x00\n#\xfd\n"
570 with tempfile.TemporaryDirectory() as tmpd:
571 fn = os.path.join(tmpd, "bad.py")
572 with open(fn, "wb") as fp:
573 fp.write(src)
574 res = script_helper.run_python_until_end(fn)[0]
575 self.assertIn(b"source code cannot contain null bytes", res.err)
576
577 @support.cpython_only
578 @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
579 def test_compiler_recursion_limit(self):
580 # Expected limit is C_RECURSION_LIMIT * 2
581 # Duplicating the limit here is a little ugly.
582 # Perhaps it should be exposed somewhere...
583 fail_depth = C_RECURSION_LIMIT * 2 + 1
584 crash_depth = C_RECURSION_LIMIT * 100
585 success_depth = int(C_RECURSION_LIMIT * 1.8)
586
587 def check_limit(prefix, repeated, mode="single"):
588 expect_ok = prefix + repeated * success_depth
589 compile(expect_ok, '<test>', mode)
590 for depth in (fail_depth, crash_depth):
591 broken = prefix + repeated * depth
592 details = "Compiling ({!r} + {!r} * {})".format(
593 prefix, repeated, depth)
594 with self.assertRaises(RecursionError, msg=details):
595 compile(broken, '<test>', mode)
596
597 check_limit("a", "()")
598 check_limit("a", ".b")
599 check_limit("a", "[0]")
600 check_limit("a", "*a")
601 # XXX Crashes in the parser.
602 # check_limit("a", " if a else a")
603 # check_limit("if a: pass", "\nelif a: pass", mode="exec")
604
605 def test_null_terminated(self):
606 # The source code is null-terminated internally, but bytes-like
607 # objects are accepted, which could be not terminated.
608 with self.assertRaisesRegex(SyntaxError, "cannot contain null"):
609 compile("123\x00", "<dummy>", "eval")
610 with self.assertRaisesRegex(SyntaxError, "cannot contain null"):
611 compile(memoryview(b"123\x00"), "<dummy>", "eval")
612 code = compile(memoryview(b"123\x00")[1:-1], "<dummy>", "eval")
613 self.assertEqual(eval(code), 23)
614 code = compile(memoryview(b"1234")[1:-1], "<dummy>", "eval")
615 self.assertEqual(eval(code), 23)
616 code = compile(memoryview(b"$23$")[1:-1], "<dummy>", "eval")
617 self.assertEqual(eval(code), 23)
618
619 # Also test when eval() and exec() do the compilation step
620 self.assertEqual(eval(memoryview(b"1234")[1:-1]), 23)
621 namespace = dict()
622 exec(memoryview(b"ax = 123")[1:-1], namespace)
623 self.assertEqual(namespace['x'], 12)
624
625 def check_constant(self, func, expected):
626 for const in func.__code__.co_consts:
627 if repr(const) == repr(expected):
628 break
629 else:
630 self.fail("unable to find constant %r in %r"
631 % (expected, func.__code__.co_consts))
632
633 # Merging equal constants is not a strict requirement for the Python
634 # semantics, it's a more an implementation detail.
635 @support.cpython_only
636 def test_merge_constants(self):
637 # Issue #25843: compile() must merge constants which are equal
638 # and have the same type.
639
640 def check_same_constant(const):
641 ns = {}
642 code = "f1, f2 = lambda: %r, lambda: %r" % (const, const)
643 exec(code, ns)
644 f1 = ns['f1']
645 f2 = ns['f2']
646 self.assertIs(f1.__code__.co_consts, f2.__code__.co_consts)
647 self.check_constant(f1, const)
648 self.assertEqual(repr(f1()), repr(const))
649
650 check_same_constant(None)
651 check_same_constant(0)
652 check_same_constant(0.0)
653 check_same_constant(b'abc')
654 check_same_constant('abc')
655
656 # Note: "lambda: ..." emits "LOAD_CONST Ellipsis",
657 # whereas "lambda: Ellipsis" emits "LOAD_GLOBAL Ellipsis"
658 f1, f2 = lambda: ..., lambda: ...
659 self.assertIs(f1.__code__.co_consts, f2.__code__.co_consts)
660 self.check_constant(f1, Ellipsis)
661 self.assertEqual(repr(f1()), repr(Ellipsis))
662
663 # Merge constants in tuple or frozenset
664 f1, f2 = lambda: "not a name", lambda: ("not a name",)
665 f3 = lambda x: x in {("not a name",)}
666 self.assertIs(f1.__code__.co_consts[1],
667 f2.__code__.co_consts[1][0])
668 self.assertIs(next(iter(f3.__code__.co_consts[1])),
669 f2.__code__.co_consts[1])
670
671 # {0} is converted to a constant frozenset({0}) by the peephole
672 # optimizer
673 f1, f2 = lambda x: x in {0}, lambda x: x in {0}
674 self.assertIs(f1.__code__.co_consts, f2.__code__.co_consts)
675 self.check_constant(f1, frozenset({0}))
676 self.assertTrue(f1(0))
677
678 # Merging equal co_linetable is not a strict requirement
679 # for the Python semantics, it's a more an implementation detail.
680 @support.cpython_only
681 def test_merge_code_attrs(self):
682 # See https://bugs.python.org/issue42217
683 f1 = lambda x: x.y.z
684 f2 = lambda a: a.b.c
685
686 self.assertIs(f1.__code__.co_linetable, f2.__code__.co_linetable)
687
688 @support.cpython_only
689 def test_remove_unused_consts(self):
690 def f():
691 "docstring"
692 if True:
693 return "used"
694 else:
695 return "unused"
696
697 self.assertEqual(f.__code__.co_consts,
698 ("docstring", "used"))
699
700 @support.cpython_only
701 def test_remove_unused_consts_no_docstring(self):
702 # the first item (None for no docstring in this case) is
703 # always retained.
704 def f():
705 if True:
706 return "used"
707 else:
708 return "unused"
709
710 self.assertEqual(f.__code__.co_consts,
711 (None, "used"))
712
713 @support.cpython_only
714 def test_remove_unused_consts_extended_args(self):
715 N = 1000
716 code = ["def f():\n"]
717 code.append("\ts = ''\n")
718 code.append("\tfor i in range(1):\n")
719 for i in range(N):
720 code.append(f"\t\tif True: s += 't{i}'\n")
721 code.append(f"\t\tif False: s += 'f{i}'\n")
722 code.append("\treturn s\n")
723
724 code = "".join(code)
725 g = {}
726 eval(compile(code, "file.py", "exec"), g)
727 exec(code, g)
728 f = g['f']
729 expected = tuple([None, '', 1] + [f't{i}' for i in range(N)])
730 self.assertEqual(f.__code__.co_consts, expected)
731 expected = "".join(expected[3:])
732 self.assertEqual(expected, f())
733
734 # Stripping unused constants is not a strict requirement for the
735 # Python semantics, it's a more an implementation detail.
736 @support.cpython_only
737 def test_strip_unused_None(self):
738 # Python 3.10rc1 appended None to co_consts when None is not used
739 # at all. See bpo-45056.
740 def f1():
741 "docstring"
742 return 42
743 self.assertEqual(f1.__code__.co_consts, ("docstring", 42))
744
745 # This is a regression test for a CPython specific peephole optimizer
746 # implementation bug present in a few releases. It's assertion verifies
747 # that peephole optimization was actually done though that isn't an
748 # indication of the bugs presence or not (crashing is).
749 @support.cpython_only
750 def test_peephole_opt_unreachable_code_array_access_in_bounds(self):
751 """Regression test for issue35193 when run under clang msan."""
752 def unused_code_at_end():
753 return 3
754 raise RuntimeError("unreachable")
755 # The above function definition will trigger the out of bounds
756 # bug in the peephole optimizer as it scans opcodes past the
757 # RETURN_VALUE opcode. This does not always crash an interpreter.
758 # When you build with the clang memory sanitizer it reliably aborts.
759 self.assertEqual(
760 'RETURN_CONST',
761 list(dis.get_instructions(unused_code_at_end))[-1].opname)
762
763 def test_dont_merge_constants(self):
764 # Issue #25843: compile() must not merge constants which are equal
765 # but have a different type.
766
767 def check_different_constants(const1, const2):
768 ns = {}
769 exec("f1, f2 = lambda: %r, lambda: %r" % (const1, const2), ns)
770 f1 = ns['f1']
771 f2 = ns['f2']
772 self.assertIsNot(f1.__code__, f2.__code__)
773 self.assertNotEqual(f1.__code__, f2.__code__)
774 self.check_constant(f1, const1)
775 self.check_constant(f2, const2)
776 self.assertEqual(repr(f1()), repr(const1))
777 self.assertEqual(repr(f2()), repr(const2))
778
779 check_different_constants(0, 0.0)
780 check_different_constants(+0.0, -0.0)
781 check_different_constants((0,), (0.0,))
782 check_different_constants('a', b'a')
783 check_different_constants(('a',), (b'a',))
784
785 # check_different_constants() cannot be used because repr(-0j) is
786 # '(-0-0j)', but when '(-0-0j)' is evaluated to 0j: we loose the sign.
787 f1, f2 = lambda: +0.0j, lambda: -0.0j
788 self.assertIsNot(f1.__code__, f2.__code__)
789 self.check_constant(f1, +0.0j)
790 self.check_constant(f2, -0.0j)
791 self.assertEqual(repr(f1()), repr(+0.0j))
792 self.assertEqual(repr(f2()), repr(-0.0j))
793
794 # {0} is converted to a constant frozenset({0}) by the peephole
795 # optimizer
796 f1, f2 = lambda x: x in {0}, lambda x: x in {0.0}
797 self.assertIsNot(f1.__code__, f2.__code__)
798 self.check_constant(f1, frozenset({0}))
799 self.check_constant(f2, frozenset({0.0}))
800 self.assertTrue(f1(0))
801 self.assertTrue(f2(0.0))
802
803 def test_path_like_objects(self):
804 # An implicit test for PyUnicode_FSDecoder().
805 compile("42", FakePath("test_compile_pathlike"), "single")
806
807 @support.requires_resource('cpu')
808 def test_stack_overflow(self):
809 # bpo-31113: Stack overflow when compile a long sequence of
810 # complex statements.
811 compile("if a: b\n" * 200000, "<dummy>", "exec")
812
813 # Multiple users rely on the fact that CPython does not generate
814 # bytecode for dead code blocks. See bpo-37500 for more context.
815 @support.cpython_only
816 def test_dead_blocks_do_not_generate_bytecode(self):
817 def unused_block_if():
818 if 0:
819 return 42
820
821 def unused_block_while():
822 while 0:
823 return 42
824
825 def unused_block_if_else():
826 if 1:
827 return None
828 else:
829 return 42
830
831 def unused_block_while_else():
832 while 1:
833 return None
834 else:
835 return 42
836
837 funcs = [unused_block_if, unused_block_while,
838 unused_block_if_else, unused_block_while_else]
839
840 for func in funcs:
841 opcodes = list(dis.get_instructions(func))
842 self.assertLessEqual(len(opcodes), 3)
843 self.assertEqual('RETURN_CONST', opcodes[-1].opname)
844 self.assertEqual(None, opcodes[-1].argval)
845
846 def test_false_while_loop(self):
847 def break_in_while():
848 while False:
849 break
850
851 def continue_in_while():
852 while False:
853 continue
854
855 funcs = [break_in_while, continue_in_while]
856
857 # Check that we did not raise but we also don't generate bytecode
858 for func in funcs:
859 opcodes = list(dis.get_instructions(func))
860 self.assertEqual(2, len(opcodes))
861 self.assertEqual('RETURN_CONST', opcodes[1].opname)
862 self.assertEqual(None, opcodes[1].argval)
863
864 def test_consts_in_conditionals(self):
865 def and_true(x):
866 return True and x
867
868 def and_false(x):
869 return False and x
870
871 def or_true(x):
872 return True or x
873
874 def or_false(x):
875 return False or x
876
877 funcs = [and_true, and_false, or_true, or_false]
878
879 # Check that condition is removed.
880 for func in funcs:
881 with self.subTest(func=func):
882 opcodes = list(dis.get_instructions(func))
883 self.assertLessEqual(len(opcodes), 3)
884 self.assertIn('LOAD_', opcodes[-2].opname)
885 self.assertEqual('RETURN_VALUE', opcodes[-1].opname)
886
887 def test_imported_load_method(self):
888 sources = [
889 """\
890 import os
891 def foo():
892 return os.uname()
893 """,
894 """\
895 import os as operating_system
896 def foo():
897 return operating_system.uname()
898 """,
899 """\
900 from os import path
901 def foo(x):
902 return path.join(x)
903 """,
904 """\
905 from os import path as os_path
906 def foo(x):
907 return os_path.join(x)
908 """
909 ]
910 for source in sources:
911 namespace = {}
912 exec(textwrap.dedent(source), namespace)
913 func = namespace['foo']
914 with self.subTest(func=func.__name__):
915 opcodes = list(dis.get_instructions(func))
916 instructions = [opcode.opname for opcode in opcodes]
917 self.assertNotIn('LOAD_METHOD', instructions)
918 self.assertIn('LOAD_ATTR', instructions)
919 self.assertIn('CALL', instructions)
920
921 def test_lineno_procedure_call(self):
922 def call():
923 (
924 print()
925 )
926 line1 = call.__code__.co_firstlineno + 1
927 assert line1 not in [line for (_, _, line) in call.__code__.co_lines()]
928
929 def test_lineno_after_implicit_return(self):
930 TRUE = True
931 # Don't use constant True or False, as compiler will remove test
932 def if1(x):
933 x()
934 if TRUE:
935 pass
936 def if2(x):
937 x()
938 if TRUE:
939 pass
940 else:
941 pass
942 def if3(x):
943 x()
944 if TRUE:
945 pass
946 else:
947 return None
948 def if4(x):
949 x()
950 if not TRUE:
951 pass
952 funcs = [ if1, if2, if3, if4]
953 lastlines = [ 3, 3, 3, 2]
954 frame = None
955 def save_caller_frame():
956 nonlocal frame
957 frame = sys._getframe(1)
958 for func, lastline in zip(funcs, lastlines, strict=True):
959 with self.subTest(func=func):
960 func(save_caller_frame)
961 self.assertEqual(frame.f_lineno-frame.f_code.co_firstlineno, lastline)
962
963 def test_lineno_after_no_code(self):
964 def no_code1():
965 "doc string"
966
967 def no_code2():
968 a: int
969
970 for func in (no_code1, no_code2):
971 with self.subTest(func=func):
972 code = func.__code__
973 [(start, end, line)] = code.co_lines()
974 self.assertEqual(start, 0)
975 self.assertEqual(end, len(code.co_code))
976 self.assertEqual(line, code.co_firstlineno)
977
978 def get_code_lines(self, code):
979 last_line = -2
980 res = []
981 for _, _, line in code.co_lines():
982 if line is not None and line != last_line:
983 res.append(line - code.co_firstlineno)
984 last_line = line
985 return res
986
987 def test_lineno_attribute(self):
988 def load_attr():
989 return (
990 o.
991 a
992 )
993 load_attr_lines = [ 0, 2, 3, 1 ]
994
995 def load_method():
996 return (
997 o.
998 m(
999 0
1000 )
1001 )
1002 load_method_lines = [ 0, 2, 3, 4, 3, 1 ]
1003
1004 def store_attr():
1005 (
1006 o.
1007 a
1008 ) = (
1009 v
1010 )
1011 store_attr_lines = [ 0, 5, 2, 3 ]
1012
1013 def aug_store_attr():
1014 (
1015 o.
1016 a
1017 ) += (
1018 v
1019 )
1020 aug_store_attr_lines = [ 0, 2, 3, 5, 1, 3 ]
1021
1022 funcs = [ load_attr, load_method, store_attr, aug_store_attr]
1023 func_lines = [ load_attr_lines, load_method_lines,
1024 store_attr_lines, aug_store_attr_lines]
1025
1026 for func, lines in zip(funcs, func_lines, strict=True):
1027 with self.subTest(func=func):
1028 code_lines = self.get_code_lines(func.__code__)
1029 self.assertEqual(lines, code_lines)
1030
1031 def test_line_number_genexp(self):
1032
1033 def return_genexp():
1034 return (1
1035 for
1036 x
1037 in
1038 y)
1039 genexp_lines = [0, 2, 0]
1040
1041 genexp_code = return_genexp.__code__.co_consts[1]
1042 code_lines = self.get_code_lines(genexp_code)
1043 self.assertEqual(genexp_lines, code_lines)
1044
1045 def test_line_number_implicit_return_after_async_for(self):
1046
1047 async def test(aseq):
1048 async for i in aseq:
1049 body
1050
1051 expected_lines = [0, 1, 2, 1]
1052 code_lines = self.get_code_lines(test.__code__)
1053 self.assertEqual(expected_lines, code_lines)
1054
1055 def test_lineno_of_backward_jump(self):
1056 # Issue gh-107901
1057 def f():
1058 for i in x:
1059 if y:
1060 pass
1061
1062 linenos = list(inst.positions.lineno
1063 for inst in dis.get_instructions(f.__code__)
1064 if inst.opname == 'JUMP_BACKWARD')
1065
1066 self.assertTrue(len(linenos) > 0)
1067 self.assertTrue(all(l is not None for l in linenos))
1068
1069 def test_big_dict_literal(self):
1070 # The compiler has a flushing point in "compiler_dict" that calls compiles
1071 # a portion of the dictionary literal when the loop that iterates over the items
1072 # reaches 0xFFFF elements but the code was not including the boundary element,
1073 # dropping the key at position 0xFFFF. See bpo-41531 for more information
1074
1075 dict_size = 0xFFFF + 1
1076 the_dict = "{" + ",".join(f"{x}:{x}" for x in range(dict_size)) + "}"
1077 self.assertEqual(len(eval(the_dict)), dict_size)
1078
1079 def test_redundant_jump_in_if_else_break(self):
1080 # Check if bytecode containing jumps that simply point to the next line
1081 # is generated around if-else-break style structures. See bpo-42615.
1082
1083 def if_else_break():
1084 val = 1
1085 while True:
1086 if val > 0:
1087 val -= 1
1088 else:
1089 break
1090 val = -1
1091
1092 INSTR_SIZE = 2
1093 HANDLED_JUMPS = (
1094 'POP_JUMP_IF_FALSE',
1095 'POP_JUMP_IF_TRUE',
1096 'JUMP_ABSOLUTE',
1097 'JUMP_FORWARD',
1098 )
1099
1100 for line, instr in enumerate(
1101 dis.Bytecode(if_else_break, show_caches=True)
1102 ):
1103 if instr.opname == 'JUMP_FORWARD':
1104 self.assertNotEqual(instr.arg, 0)
1105 elif instr.opname in HANDLED_JUMPS:
1106 self.assertNotEqual(instr.arg, (line + 1)*INSTR_SIZE)
1107
1108 def test_no_wraparound_jump(self):
1109 # See https://bugs.python.org/issue46724
1110
1111 def while_not_chained(a, b, c):
1112 while not (a < b < c):
1113 pass
1114
1115 for instr in dis.Bytecode(while_not_chained):
1116 self.assertNotEqual(instr.opname, "EXTENDED_ARG")
1117
1118 @support.cpython_only
1119 def test_uses_slice_instructions(self):
1120
1121 def check_op_count(func, op, expected):
1122 actual = 0
1123 for instr in dis.Bytecode(func):
1124 if instr.opname == op:
1125 actual += 1
1126 self.assertEqual(actual, expected)
1127
1128 def load():
1129 return x[a:b] + x [a:] + x[:b] + x[:]
1130
1131 def store():
1132 x[a:b] = y
1133 x [a:] = y
1134 x[:b] = y
1135 x[:] = y
1136
1137 def long_slice():
1138 return x[a:b:c]
1139
1140 def aug():
1141 x[a:b] += y
1142
1143 check_op_count(load, "BINARY_SLICE", 4)
1144 check_op_count(load, "BUILD_SLICE", 0)
1145 check_op_count(store, "STORE_SLICE", 4)
1146 check_op_count(store, "BUILD_SLICE", 0)
1147 check_op_count(long_slice, "BUILD_SLICE", 1)
1148 check_op_count(long_slice, "BINARY_SLICE", 0)
1149 check_op_count(aug, "BINARY_SLICE", 1)
1150 check_op_count(aug, "STORE_SLICE", 1)
1151 check_op_count(aug, "BUILD_SLICE", 0)
1152
1153 def test_compare_positions(self):
1154 for opname_prefix, op in [
1155 ("COMPARE_", "<"),
1156 ("COMPARE_", "<="),
1157 ("COMPARE_", ">"),
1158 ("COMPARE_", ">="),
1159 ("CONTAINS_OP", "in"),
1160 ("CONTAINS_OP", "not in"),
1161 ("IS_OP", "is"),
1162 ("IS_OP", "is not"),
1163 ]:
1164 expr = f'a {op} b {op} c'
1165 expected_positions = 2 * [(2, 2, 0, len(expr))]
1166 for source in [
1167 f"\\\n{expr}", f'if \\\n{expr}: x', f"x if \\\n{expr} else y"
1168 ]:
1169 code = compile(source, "<test>", "exec")
1170 actual_positions = [
1171 instruction.positions
1172 for instruction in dis.get_instructions(code)
1173 if instruction.opname.startswith(opname_prefix)
1174 ]
1175 with self.subTest(source):
1176 self.assertEqual(actual_positions, expected_positions)
1177
1178 def test_if_expression_expression_empty_block(self):
1179 # See regression in gh-99708
1180 exprs = [
1181 "assert (False if 1 else True)",
1182 "def f():\n\tif not (False if 1 else True): raise AssertionError",
1183 "def f():\n\tif not (False if 1 else True): return 12",
1184 ]
1185 for expr in exprs:
1186 with self.subTest(expr=expr):
1187 compile(expr, "<single>", "exec")
1188
1189 def test_multi_line_lambda_as_argument(self):
1190 # See gh-101928
1191 code = textwrap.dedent("""
1192 def foo(param, lambda_exp):
1193 pass
1194
1195 foo(param=0,
1196 lambda_exp=lambda:
1197 1)
1198 """)
1199 compile(code, "<test>", "exec")
1200
1201 def test_apply_static_swaps(self):
1202 def f(x, y):
1203 a, a = x, y
1204 return a
1205 self.assertEqual(f("x", "y"), "y")
1206
1207 def test_apply_static_swaps_2(self):
1208 def f(x, y, z):
1209 a, b, a = x, y, z
1210 return a
1211 self.assertEqual(f("x", "y", "z"), "z")
1212
1213 def test_apply_static_swaps_3(self):
1214 def f(x, y, z):
1215 a, a, b = x, y, z
1216 return a
1217 self.assertEqual(f("x", "y", "z"), "y")
1218
1219 def test_duplicated_small_exit_block(self):
1220 # See gh-109627
1221 def f():
1222 while element and something:
1223 try:
1224 return something
1225 except:
1226 pass
1227
1228 def test_cold_block_moved_to_end(self):
1229 # See gh-109719
1230 def f():
1231 while name:
1232 try:
1233 break
1234 except:
1235 pass
1236 else:
1237 1 if 1 else 1
1238
1239 def test_remove_empty_basic_block_with_jump_target_label(self):
1240 # See gh-109823
1241 def f(x):
1242 while x:
1243 0 if 1 else 0
1244
1245 @requires_debug_ranges()
1246 class ESC[4;38;5;81mTestSourcePositions(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1247 # Ensure that compiled code snippets have correct line and column numbers
1248 # in `co_positions()`.
1249
1250 def check_positions_against_ast(self, snippet):
1251 # Basic check that makes sure each line and column is at least present
1252 # in one of the AST nodes of the source code.
1253 code = compile(snippet, 'test_compile.py', 'exec')
1254 ast_tree = compile(snippet, 'test_compile.py', 'exec', _ast.PyCF_ONLY_AST)
1255 self.assertTrue(type(ast_tree) == _ast.Module)
1256
1257 # Use an AST visitor that notes all the offsets.
1258 lines, end_lines, columns, end_columns = set(), set(), set(), set()
1259 class ESC[4;38;5;81mSourceOffsetVisitor(ESC[4;38;5;149mastESC[4;38;5;149m.ESC[4;38;5;149mNodeVisitor):
1260 def generic_visit(self, node):
1261 super().generic_visit(node)
1262 if not isinstance(node, (ast.expr, ast.stmt, ast.pattern)):
1263 return
1264 lines.add(node.lineno)
1265 end_lines.add(node.end_lineno)
1266 columns.add(node.col_offset)
1267 end_columns.add(node.end_col_offset)
1268
1269 SourceOffsetVisitor().visit(ast_tree)
1270
1271 # Check against the positions in the code object.
1272 for (line, end_line, col, end_col) in code.co_positions():
1273 if line == 0:
1274 continue # This is an artificial module-start line
1275 # If the offset is not None (indicating missing data), ensure that
1276 # it was part of one of the AST nodes.
1277 if line is not None:
1278 self.assertIn(line, lines)
1279 if end_line is not None:
1280 self.assertIn(end_line, end_lines)
1281 if col is not None:
1282 self.assertIn(col, columns)
1283 if end_col is not None:
1284 self.assertIn(end_col, end_columns)
1285
1286 return code, ast_tree
1287
1288 def assertOpcodeSourcePositionIs(self, code, opcode,
1289 line, end_line, column, end_column, occurrence=1):
1290
1291 for instr, position in zip(
1292 dis.Bytecode(code, show_caches=True), code.co_positions(), strict=True
1293 ):
1294 if instr.opname == opcode:
1295 occurrence -= 1
1296 if not occurrence:
1297 self.assertEqual(position[0], line)
1298 self.assertEqual(position[1], end_line)
1299 self.assertEqual(position[2], column)
1300 self.assertEqual(position[3], end_column)
1301 return
1302
1303 self.fail(f"Opcode {opcode} not found in code")
1304
1305 def test_simple_assignment(self):
1306 snippet = "x = 1"
1307 self.check_positions_against_ast(snippet)
1308
1309 def test_compiles_to_extended_op_arg(self):
1310 # Make sure we still have valid positions when the code compiles to an
1311 # EXTENDED_ARG by performing a loop which needs a JUMP_ABSOLUTE after
1312 # a bunch of opcodes.
1313 snippet = "x = x\n" * 10_000
1314 snippet += ("while x != 0:\n"
1315 " x -= 1\n"
1316 "while x != 0:\n"
1317 " x += 1\n"
1318 )
1319
1320 compiled_code, _ = self.check_positions_against_ast(snippet)
1321
1322 self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP',
1323 line=10_000 + 2, end_line=10_000 + 2,
1324 column=2, end_column=8, occurrence=1)
1325 self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP',
1326 line=10_000 + 4, end_line=10_000 + 4,
1327 column=2, end_column=9, occurrence=2)
1328
1329 def test_multiline_expression(self):
1330 snippet = textwrap.dedent("""\
1331 f(
1332 1, 2, 3, 4
1333 )
1334 """)
1335 compiled_code, _ = self.check_positions_against_ast(snippet)
1336 self.assertOpcodeSourcePositionIs(compiled_code, 'CALL',
1337 line=1, end_line=3, column=0, end_column=1)
1338
1339 @requires_specialization
1340 def test_multiline_boolean_expression(self):
1341 snippet = textwrap.dedent("""\
1342 if (a or
1343 (b and not c) or
1344 not (
1345 d > 0)):
1346 x = 42
1347 """)
1348 compiled_code, _ = self.check_positions_against_ast(snippet)
1349 # jump if a is true:
1350 self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_TRUE',
1351 line=1, end_line=1, column=4, end_column=5, occurrence=1)
1352 # jump if b is false:
1353 self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_FALSE',
1354 line=2, end_line=2, column=5, end_column=6, occurrence=1)
1355 # jump if c is false:
1356 self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_FALSE',
1357 line=2, end_line=2, column=15, end_column=16, occurrence=2)
1358 # compare d and 0
1359 self.assertOpcodeSourcePositionIs(compiled_code, 'COMPARE_OP',
1360 line=4, end_line=4, column=8, end_column=13, occurrence=1)
1361 # jump if comparison it True
1362 self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_TRUE',
1363 line=4, end_line=4, column=8, end_column=13, occurrence=2)
1364
1365 def test_multiline_assert(self):
1366 snippet = textwrap.dedent("""\
1367 assert (a > 0 and
1368 bb > 0 and
1369 ccc == 4), "error msg"
1370 """)
1371 compiled_code, _ = self.check_positions_against_ast(snippet)
1372 self.assertOpcodeSourcePositionIs(compiled_code, 'LOAD_ASSERTION_ERROR',
1373 line=1, end_line=3, column=0, end_column=30, occurrence=1)
1374 # The "error msg":
1375 self.assertOpcodeSourcePositionIs(compiled_code, 'LOAD_CONST',
1376 line=3, end_line=3, column=19, end_column=30, occurrence=4)
1377 self.assertOpcodeSourcePositionIs(compiled_code, 'CALL',
1378 line=1, end_line=3, column=0, end_column=30, occurrence=1)
1379 self.assertOpcodeSourcePositionIs(compiled_code, 'RAISE_VARARGS',
1380 line=1, end_line=3, column=0, end_column=30, occurrence=1)
1381
1382 def test_multiline_generator_expression(self):
1383 snippet = textwrap.dedent("""\
1384 ((x,
1385 2*x)
1386 for x
1387 in [1,2,3] if (x > 0
1388 and x < 100
1389 and x != 50))
1390 """)
1391 compiled_code, _ = self.check_positions_against_ast(snippet)
1392 compiled_code = compiled_code.co_consts[0]
1393 self.assertIsInstance(compiled_code, types.CodeType)
1394 self.assertOpcodeSourcePositionIs(compiled_code, 'YIELD_VALUE',
1395 line=1, end_line=2, column=1, end_column=8, occurrence=1)
1396 self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1397 line=1, end_line=2, column=1, end_column=8, occurrence=1)
1398 self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST',
1399 line=1, end_line=6, column=0, end_column=32, occurrence=1)
1400
1401 def test_multiline_async_generator_expression(self):
1402 snippet = textwrap.dedent("""\
1403 ((x,
1404 2*x)
1405 async for x
1406 in [1,2,3] if (x > 0
1407 and x < 100
1408 and x != 50))
1409 """)
1410 compiled_code, _ = self.check_positions_against_ast(snippet)
1411 compiled_code = compiled_code.co_consts[0]
1412 self.assertIsInstance(compiled_code, types.CodeType)
1413 self.assertOpcodeSourcePositionIs(compiled_code, 'YIELD_VALUE',
1414 line=1, end_line=2, column=1, end_column=8, occurrence=2)
1415 self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST',
1416 line=1, end_line=6, column=0, end_column=32, occurrence=1)
1417
1418 def test_multiline_list_comprehension(self):
1419 snippet = textwrap.dedent("""\
1420 [(x,
1421 2*x)
1422 for x
1423 in [1,2,3] if (x > 0
1424 and x < 100
1425 and x != 50)]
1426 """)
1427 compiled_code, _ = self.check_positions_against_ast(snippet)
1428 self.assertIsInstance(compiled_code, types.CodeType)
1429 self.assertOpcodeSourcePositionIs(compiled_code, 'LIST_APPEND',
1430 line=1, end_line=2, column=1, end_column=8, occurrence=1)
1431 self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1432 line=1, end_line=2, column=1, end_column=8, occurrence=1)
1433
1434 def test_multiline_async_list_comprehension(self):
1435 snippet = textwrap.dedent("""\
1436 async def f():
1437 [(x,
1438 2*x)
1439 async for x
1440 in [1,2,3] if (x > 0
1441 and x < 100
1442 and x != 50)]
1443 """)
1444 compiled_code, _ = self.check_positions_against_ast(snippet)
1445 g = {}
1446 eval(compiled_code, g)
1447 compiled_code = g['f'].__code__
1448 self.assertIsInstance(compiled_code, types.CodeType)
1449 self.assertOpcodeSourcePositionIs(compiled_code, 'LIST_APPEND',
1450 line=2, end_line=3, column=5, end_column=12, occurrence=1)
1451 self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1452 line=2, end_line=3, column=5, end_column=12, occurrence=1)
1453 self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST',
1454 line=2, end_line=7, column=4, end_column=36, occurrence=1)
1455
1456 def test_multiline_set_comprehension(self):
1457 snippet = textwrap.dedent("""\
1458 {(x,
1459 2*x)
1460 for x
1461 in [1,2,3] if (x > 0
1462 and x < 100
1463 and x != 50)}
1464 """)
1465 compiled_code, _ = self.check_positions_against_ast(snippet)
1466 self.assertIsInstance(compiled_code, types.CodeType)
1467 self.assertOpcodeSourcePositionIs(compiled_code, 'SET_ADD',
1468 line=1, end_line=2, column=1, end_column=8, occurrence=1)
1469 self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1470 line=1, end_line=2, column=1, end_column=8, occurrence=1)
1471
1472 def test_multiline_async_set_comprehension(self):
1473 snippet = textwrap.dedent("""\
1474 async def f():
1475 {(x,
1476 2*x)
1477 async for x
1478 in [1,2,3] if (x > 0
1479 and x < 100
1480 and x != 50)}
1481 """)
1482 compiled_code, _ = self.check_positions_against_ast(snippet)
1483 g = {}
1484 eval(compiled_code, g)
1485 compiled_code = g['f'].__code__
1486 self.assertIsInstance(compiled_code, types.CodeType)
1487 self.assertOpcodeSourcePositionIs(compiled_code, 'SET_ADD',
1488 line=2, end_line=3, column=5, end_column=12, occurrence=1)
1489 self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1490 line=2, end_line=3, column=5, end_column=12, occurrence=1)
1491 self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST',
1492 line=2, end_line=7, column=4, end_column=36, occurrence=1)
1493
1494 def test_multiline_dict_comprehension(self):
1495 snippet = textwrap.dedent("""\
1496 {x:
1497 2*x
1498 for x
1499 in [1,2,3] if (x > 0
1500 and x < 100
1501 and x != 50)}
1502 """)
1503 compiled_code, _ = self.check_positions_against_ast(snippet)
1504 self.assertIsInstance(compiled_code, types.CodeType)
1505 self.assertOpcodeSourcePositionIs(compiled_code, 'MAP_ADD',
1506 line=1, end_line=2, column=1, end_column=7, occurrence=1)
1507 self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1508 line=1, end_line=2, column=1, end_column=7, occurrence=1)
1509
1510 def test_multiline_async_dict_comprehension(self):
1511 snippet = textwrap.dedent("""\
1512 async def f():
1513 {x:
1514 2*x
1515 async for x
1516 in [1,2,3] if (x > 0
1517 and x < 100
1518 and x != 50)}
1519 """)
1520 compiled_code, _ = self.check_positions_against_ast(snippet)
1521 g = {}
1522 eval(compiled_code, g)
1523 compiled_code = g['f'].__code__
1524 self.assertIsInstance(compiled_code, types.CodeType)
1525 self.assertOpcodeSourcePositionIs(compiled_code, 'MAP_ADD',
1526 line=2, end_line=3, column=5, end_column=11, occurrence=1)
1527 self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
1528 line=2, end_line=3, column=5, end_column=11, occurrence=1)
1529 self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST',
1530 line=2, end_line=7, column=4, end_column=36, occurrence=1)
1531
1532 def test_matchcase_sequence(self):
1533 snippet = textwrap.dedent("""\
1534 match x:
1535 case a, b:
1536 pass
1537 """)
1538 compiled_code, _ = self.check_positions_against_ast(snippet)
1539 self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_SEQUENCE',
1540 line=2, end_line=2, column=9, end_column=13, occurrence=1)
1541 self.assertOpcodeSourcePositionIs(compiled_code, 'UNPACK_SEQUENCE',
1542 line=2, end_line=2, column=9, end_column=13, occurrence=1)
1543 self.assertOpcodeSourcePositionIs(compiled_code, 'STORE_NAME',
1544 line=2, end_line=2, column=9, end_column=13, occurrence=1)
1545 self.assertOpcodeSourcePositionIs(compiled_code, 'STORE_NAME',
1546 line=2, end_line=2, column=9, end_column=13, occurrence=2)
1547
1548 def test_matchcase_sequence_wildcard(self):
1549 snippet = textwrap.dedent("""\
1550 match x:
1551 case a, *b, c:
1552 pass
1553 """)
1554 compiled_code, _ = self.check_positions_against_ast(snippet)
1555 self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_SEQUENCE',
1556 line=2, end_line=2, column=9, end_column=17, occurrence=1)
1557 self.assertOpcodeSourcePositionIs(compiled_code, 'UNPACK_EX',
1558 line=2, end_line=2, column=9, end_column=17, occurrence=1)
1559 self.assertOpcodeSourcePositionIs(compiled_code, 'STORE_NAME',
1560 line=2, end_line=2, column=9, end_column=17, occurrence=1)
1561 self.assertOpcodeSourcePositionIs(compiled_code, 'STORE_NAME',
1562 line=2, end_line=2, column=9, end_column=17, occurrence=2)
1563 self.assertOpcodeSourcePositionIs(compiled_code, 'STORE_NAME',
1564 line=2, end_line=2, column=9, end_column=17, occurrence=3)
1565
1566 def test_matchcase_mapping(self):
1567 snippet = textwrap.dedent("""\
1568 match x:
1569 case {"a" : a, "b": b}:
1570 pass
1571 """)
1572 compiled_code, _ = self.check_positions_against_ast(snippet)
1573 self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_MAPPING',
1574 line=2, end_line=2, column=9, end_column=26, occurrence=1)
1575 self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_KEYS',
1576 line=2, end_line=2, column=9, end_column=26, occurrence=1)
1577 self.assertOpcodeSourcePositionIs(compiled_code, 'STORE_NAME',
1578 line=2, end_line=2, column=9, end_column=26, occurrence=1)
1579 self.assertOpcodeSourcePositionIs(compiled_code, 'STORE_NAME',
1580 line=2, end_line=2, column=9, end_column=26, occurrence=2)
1581
1582 def test_matchcase_mapping_wildcard(self):
1583 snippet = textwrap.dedent("""\
1584 match x:
1585 case {"a" : a, "b": b, **c}:
1586 pass
1587 """)
1588 compiled_code, _ = self.check_positions_against_ast(snippet)
1589 self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_MAPPING',
1590 line=2, end_line=2, column=9, end_column=31, occurrence=1)
1591 self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_KEYS',
1592 line=2, end_line=2, column=9, end_column=31, occurrence=1)
1593 self.assertOpcodeSourcePositionIs(compiled_code, 'STORE_NAME',
1594 line=2, end_line=2, column=9, end_column=31, occurrence=1)
1595 self.assertOpcodeSourcePositionIs(compiled_code, 'STORE_NAME',
1596 line=2, end_line=2, column=9, end_column=31, occurrence=2)
1597
1598 def test_matchcase_class(self):
1599 snippet = textwrap.dedent("""\
1600 match x:
1601 case C(a, b):
1602 pass
1603 """)
1604 compiled_code, _ = self.check_positions_against_ast(snippet)
1605 self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_CLASS',
1606 line=2, end_line=2, column=9, end_column=16, occurrence=1)
1607 self.assertOpcodeSourcePositionIs(compiled_code, 'UNPACK_SEQUENCE',
1608 line=2, end_line=2, column=9, end_column=16, occurrence=1)
1609 self.assertOpcodeSourcePositionIs(compiled_code, 'STORE_NAME',
1610 line=2, end_line=2, column=9, end_column=16, occurrence=1)
1611 self.assertOpcodeSourcePositionIs(compiled_code, 'STORE_NAME',
1612 line=2, end_line=2, column=9, end_column=16, occurrence=2)
1613
1614 def test_matchcase_or(self):
1615 snippet = textwrap.dedent("""\
1616 match x:
1617 case C(1) | C(2):
1618 pass
1619 """)
1620 compiled_code, _ = self.check_positions_against_ast(snippet)
1621 self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_CLASS',
1622 line=2, end_line=2, column=9, end_column=13, occurrence=1)
1623 self.assertOpcodeSourcePositionIs(compiled_code, 'MATCH_CLASS',
1624 line=2, end_line=2, column=16, end_column=20, occurrence=2)
1625
1626 def test_very_long_line_end_offset(self):
1627 # Make sure we get the correct column offset for offsets
1628 # too large to store in a byte.
1629 long_string = "a" * 1000
1630 snippet = f"g('{long_string}')"
1631
1632 compiled_code, _ = self.check_positions_against_ast(snippet)
1633 self.assertOpcodeSourcePositionIs(compiled_code, 'CALL',
1634 line=1, end_line=1, column=0, end_column=1005)
1635
1636 def test_complex_single_line_expression(self):
1637 snippet = "a - b @ (c * x['key'] + 23)"
1638
1639 compiled_code, _ = self.check_positions_against_ast(snippet)
1640 self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_SUBSCR',
1641 line=1, end_line=1, column=13, end_column=21)
1642 self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP',
1643 line=1, end_line=1, column=9, end_column=21, occurrence=1)
1644 self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP',
1645 line=1, end_line=1, column=9, end_column=26, occurrence=2)
1646 self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP',
1647 line=1, end_line=1, column=4, end_column=27, occurrence=3)
1648 self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP',
1649 line=1, end_line=1, column=0, end_column=27, occurrence=4)
1650
1651 def test_multiline_assert_rewritten_as_method_call(self):
1652 # GH-94694: Don't crash if pytest rewrites a multiline assert as a
1653 # method call with the same location information:
1654 tree = ast.parse("assert (\n42\n)")
1655 old_node = tree.body[0]
1656 new_node = ast.Expr(
1657 ast.Call(
1658 ast.Attribute(
1659 ast.Name("spam", ast.Load()),
1660 "eggs",
1661 ast.Load(),
1662 ),
1663 [],
1664 [],
1665 )
1666 )
1667 ast.copy_location(new_node, old_node)
1668 ast.fix_missing_locations(new_node)
1669 tree.body[0] = new_node
1670 compile(tree, "<test>", "exec")
1671
1672 def test_push_null_load_global_positions(self):
1673 source_template = """
1674 import abc, dis
1675 import ast as art
1676
1677 abc = None
1678 dix = dis
1679 ast = art
1680
1681 def f():
1682 {}
1683 """
1684 for body in [
1685 " abc.a()",
1686 " art.a()",
1687 " ast.a()",
1688 " dis.a()",
1689 " dix.a()",
1690 " abc[...]()",
1691 " art()()",
1692 " (ast or ...)()",
1693 " [dis]()",
1694 " (dix + ...)()",
1695 ]:
1696 with self.subTest(body):
1697 namespace = {}
1698 source = textwrap.dedent(source_template.format(body))
1699 with warnings.catch_warnings():
1700 warnings.simplefilter('ignore', SyntaxWarning)
1701 exec(source, namespace)
1702 code = namespace["f"].__code__
1703 self.assertOpcodeSourcePositionIs(
1704 code,
1705 "LOAD_GLOBAL",
1706 line=10,
1707 end_line=10,
1708 column=4,
1709 end_column=7,
1710 )
1711
1712 def test_attribute_augassign(self):
1713 source = "(\n lhs \n . \n rhs \n ) += 42"
1714 code = compile(source, "<test>", "exec")
1715 self.assertOpcodeSourcePositionIs(
1716 code, "LOAD_ATTR", line=4, end_line=4, column=5, end_column=8
1717 )
1718 self.assertOpcodeSourcePositionIs(
1719 code, "STORE_ATTR", line=4, end_line=4, column=5, end_column=8
1720 )
1721
1722 def test_attribute_del(self):
1723 source = "del (\n lhs \n . \n rhs \n )"
1724 code = compile(source, "<test>", "exec")
1725 self.assertOpcodeSourcePositionIs(
1726 code, "DELETE_ATTR", line=4, end_line=4, column=5, end_column=8
1727 )
1728
1729 def test_attribute_load(self):
1730 source = "(\n lhs \n . \n rhs \n )"
1731 code = compile(source, "<test>", "exec")
1732 self.assertOpcodeSourcePositionIs(
1733 code, "LOAD_ATTR", line=4, end_line=4, column=5, end_column=8
1734 )
1735
1736 def test_attribute_store(self):
1737 source = "(\n lhs \n . \n rhs \n ) = 42"
1738 code = compile(source, "<test>", "exec")
1739 self.assertOpcodeSourcePositionIs(
1740 code, "STORE_ATTR", line=4, end_line=4, column=5, end_column=8
1741 )
1742
1743 def test_method_call(self):
1744 source = "(\n lhs \n . \n rhs \n )()"
1745 code = compile(source, "<test>", "exec")
1746 self.assertOpcodeSourcePositionIs(
1747 code, "LOAD_ATTR", line=4, end_line=4, column=5, end_column=8
1748 )
1749 self.assertOpcodeSourcePositionIs(
1750 code, "CALL", line=4, end_line=5, column=5, end_column=10
1751 )
1752
1753 def test_weird_attribute_position_regressions(self):
1754 def f():
1755 (bar.
1756 baz)
1757 (bar.
1758 baz(
1759 ))
1760 files().setdefault(
1761 0
1762 ).setdefault(
1763 0
1764 )
1765 for line, end_line, column, end_column in f.__code__.co_positions():
1766 self.assertIsNotNone(line)
1767 self.assertIsNotNone(end_line)
1768 self.assertIsNotNone(column)
1769 self.assertIsNotNone(end_column)
1770 self.assertLessEqual((line, column), (end_line, end_column))
1771
1772 @support.cpython_only
1773 def test_column_offset_deduplication(self):
1774 # GH-95150: Code with different column offsets shouldn't be merged!
1775 for source in [
1776 "lambda: a",
1777 "(a for b in c)",
1778 ]:
1779 with self.subTest(source):
1780 code = compile(f"{source}, {source}", "<test>", "eval")
1781 self.assertEqual(len(code.co_consts), 2)
1782 self.assertIsInstance(code.co_consts[0], types.CodeType)
1783 self.assertIsInstance(code.co_consts[1], types.CodeType)
1784 self.assertNotEqual(code.co_consts[0], code.co_consts[1])
1785 self.assertNotEqual(
1786 list(code.co_consts[0].co_positions()),
1787 list(code.co_consts[1].co_positions()),
1788 )
1789
1790 def test_load_super_attr(self):
1791 source = "class C:\n def __init__(self):\n super().__init__()"
1792 code = compile(source, "<test>", "exec").co_consts[0].co_consts[1]
1793 self.assertOpcodeSourcePositionIs(
1794 code, "LOAD_GLOBAL", line=3, end_line=3, column=4, end_column=9
1795 )
1796
1797
1798 class ESC[4;38;5;81mTestExpressionStackSize(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1799 # These tests check that the computed stack size for a code object
1800 # stays within reasonable bounds (see issue #21523 for an example
1801 # dysfunction).
1802 N = 100
1803
1804 def check_stack_size(self, code):
1805 # To assert that the alleged stack size is not O(N), we
1806 # check that it is smaller than log(N).
1807 if isinstance(code, str):
1808 code = compile(code, "<foo>", "single")
1809 max_size = math.ceil(math.log(len(code.co_code)))
1810 self.assertLessEqual(code.co_stacksize, max_size)
1811
1812 def test_and(self):
1813 self.check_stack_size("x and " * self.N + "x")
1814
1815 def test_or(self):
1816 self.check_stack_size("x or " * self.N + "x")
1817
1818 def test_and_or(self):
1819 self.check_stack_size("x and x or " * self.N + "x")
1820
1821 def test_chained_comparison(self):
1822 self.check_stack_size("x < " * self.N + "x")
1823
1824 def test_if_else(self):
1825 self.check_stack_size("x if x else " * self.N + "x")
1826
1827 def test_binop(self):
1828 self.check_stack_size("x + " * self.N + "x")
1829
1830 def test_list(self):
1831 self.check_stack_size("[" + "x, " * self.N + "x]")
1832
1833 def test_tuple(self):
1834 self.check_stack_size("(" + "x, " * self.N + "x)")
1835
1836 def test_set(self):
1837 self.check_stack_size("{" + "x, " * self.N + "x}")
1838
1839 def test_dict(self):
1840 self.check_stack_size("{" + "x:x, " * self.N + "x:x}")
1841
1842 def test_func_args(self):
1843 self.check_stack_size("f(" + "x, " * self.N + ")")
1844
1845 def test_func_kwargs(self):
1846 kwargs = (f'a{i}=x' for i in range(self.N))
1847 self.check_stack_size("f(" + ", ".join(kwargs) + ")")
1848
1849 def test_meth_args(self):
1850 self.check_stack_size("o.m(" + "x, " * self.N + ")")
1851
1852 def test_meth_kwargs(self):
1853 kwargs = (f'a{i}=x' for i in range(self.N))
1854 self.check_stack_size("o.m(" + ", ".join(kwargs) + ")")
1855
1856 def test_func_and(self):
1857 code = "def f(x):\n"
1858 code += " x and x\n" * self.N
1859 self.check_stack_size(code)
1860
1861 def test_stack_3050(self):
1862 M = 3050
1863 code = "x," * M + "=t"
1864 # This raised on 3.10.0 to 3.10.5
1865 compile(code, "<foo>", "single")
1866
1867 def test_stack_3050_2(self):
1868 M = 3050
1869 args = ", ".join(f"arg{i}:type{i}" for i in range(M))
1870 code = f"def f({args}):\n pass"
1871 # This raised on 3.10.0 to 3.10.5
1872 compile(code, "<foo>", "single")
1873
1874
1875 class ESC[4;38;5;81mTestStackSizeStability(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1876 # Check that repeating certain snippets doesn't increase the stack size
1877 # beyond what a single snippet requires.
1878
1879 def check_stack_size(self, snippet, async_=False):
1880 def compile_snippet(i):
1881 ns = {}
1882 script = """def func():\n""" + i * snippet
1883 if async_:
1884 script = "async " + script
1885 code = compile(script, "<script>", "exec")
1886 exec(code, ns, ns)
1887 return ns['func'].__code__
1888
1889 sizes = [compile_snippet(i).co_stacksize for i in range(2, 5)]
1890 if len(set(sizes)) != 1:
1891 import dis, io
1892 out = io.StringIO()
1893 dis.dis(compile_snippet(1), file=out)
1894 self.fail("stack sizes diverge with # of consecutive snippets: "
1895 "%s\n%s\n%s" % (sizes, snippet, out.getvalue()))
1896
1897 def test_if(self):
1898 snippet = """
1899 if x:
1900 a
1901 """
1902 self.check_stack_size(snippet)
1903
1904 def test_if_else(self):
1905 snippet = """
1906 if x:
1907 a
1908 elif y:
1909 b
1910 else:
1911 c
1912 """
1913 self.check_stack_size(snippet)
1914
1915 def test_try_except_bare(self):
1916 snippet = """
1917 try:
1918 a
1919 except:
1920 b
1921 """
1922 self.check_stack_size(snippet)
1923
1924 def test_try_except_qualified(self):
1925 snippet = """
1926 try:
1927 a
1928 except ImportError:
1929 b
1930 except:
1931 c
1932 else:
1933 d
1934 """
1935 self.check_stack_size(snippet)
1936
1937 def test_try_except_as(self):
1938 snippet = """
1939 try:
1940 a
1941 except ImportError as e:
1942 b
1943 except:
1944 c
1945 else:
1946 d
1947 """
1948 self.check_stack_size(snippet)
1949
1950 def test_try_except_star_qualified(self):
1951 snippet = """
1952 try:
1953 a
1954 except* ImportError:
1955 b
1956 else:
1957 c
1958 """
1959 self.check_stack_size(snippet)
1960
1961 def test_try_except_star_as(self):
1962 snippet = """
1963 try:
1964 a
1965 except* ImportError as e:
1966 b
1967 else:
1968 c
1969 """
1970 self.check_stack_size(snippet)
1971
1972 def test_try_except_star_finally(self):
1973 snippet = """
1974 try:
1975 a
1976 except* A:
1977 b
1978 finally:
1979 c
1980 """
1981 self.check_stack_size(snippet)
1982
1983 def test_try_finally(self):
1984 snippet = """
1985 try:
1986 a
1987 finally:
1988 b
1989 """
1990 self.check_stack_size(snippet)
1991
1992 def test_with(self):
1993 snippet = """
1994 with x as y:
1995 a
1996 """
1997 self.check_stack_size(snippet)
1998
1999 def test_while_else(self):
2000 snippet = """
2001 while x:
2002 a
2003 else:
2004 b
2005 """
2006 self.check_stack_size(snippet)
2007
2008 def test_for(self):
2009 snippet = """
2010 for x in y:
2011 a
2012 """
2013 self.check_stack_size(snippet)
2014
2015 def test_for_else(self):
2016 snippet = """
2017 for x in y:
2018 a
2019 else:
2020 b
2021 """
2022 self.check_stack_size(snippet)
2023
2024 def test_for_break_continue(self):
2025 snippet = """
2026 for x in y:
2027 if z:
2028 break
2029 elif u:
2030 continue
2031 else:
2032 a
2033 else:
2034 b
2035 """
2036 self.check_stack_size(snippet)
2037
2038 def test_for_break_continue_inside_try_finally_block(self):
2039 snippet = """
2040 for x in y:
2041 try:
2042 if z:
2043 break
2044 elif u:
2045 continue
2046 else:
2047 a
2048 finally:
2049 f
2050 else:
2051 b
2052 """
2053 self.check_stack_size(snippet)
2054
2055 def test_for_break_continue_inside_finally_block(self):
2056 snippet = """
2057 for x in y:
2058 try:
2059 t
2060 finally:
2061 if z:
2062 break
2063 elif u:
2064 continue
2065 else:
2066 a
2067 else:
2068 b
2069 """
2070 self.check_stack_size(snippet)
2071
2072 def test_for_break_continue_inside_except_block(self):
2073 snippet = """
2074 for x in y:
2075 try:
2076 t
2077 except:
2078 if z:
2079 break
2080 elif u:
2081 continue
2082 else:
2083 a
2084 else:
2085 b
2086 """
2087 self.check_stack_size(snippet)
2088
2089 def test_for_break_continue_inside_with_block(self):
2090 snippet = """
2091 for x in y:
2092 with c:
2093 if z:
2094 break
2095 elif u:
2096 continue
2097 else:
2098 a
2099 else:
2100 b
2101 """
2102 self.check_stack_size(snippet)
2103
2104 def test_return_inside_try_finally_block(self):
2105 snippet = """
2106 try:
2107 if z:
2108 return
2109 else:
2110 a
2111 finally:
2112 f
2113 """
2114 self.check_stack_size(snippet)
2115
2116 def test_return_inside_finally_block(self):
2117 snippet = """
2118 try:
2119 t
2120 finally:
2121 if z:
2122 return
2123 else:
2124 a
2125 """
2126 self.check_stack_size(snippet)
2127
2128 def test_return_inside_except_block(self):
2129 snippet = """
2130 try:
2131 t
2132 except:
2133 if z:
2134 return
2135 else:
2136 a
2137 """
2138 self.check_stack_size(snippet)
2139
2140 def test_return_inside_with_block(self):
2141 snippet = """
2142 with c:
2143 if z:
2144 return
2145 else:
2146 a
2147 """
2148 self.check_stack_size(snippet)
2149
2150 def test_async_with(self):
2151 snippet = """
2152 async with x as y:
2153 a
2154 """
2155 self.check_stack_size(snippet, async_=True)
2156
2157 def test_async_for(self):
2158 snippet = """
2159 async for x in y:
2160 a
2161 """
2162 self.check_stack_size(snippet, async_=True)
2163
2164 def test_async_for_else(self):
2165 snippet = """
2166 async for x in y:
2167 a
2168 else:
2169 b
2170 """
2171 self.check_stack_size(snippet, async_=True)
2172
2173 def test_for_break_continue_inside_async_with_block(self):
2174 snippet = """
2175 for x in y:
2176 async with c:
2177 if z:
2178 break
2179 elif u:
2180 continue
2181 else:
2182 a
2183 else:
2184 b
2185 """
2186 self.check_stack_size(snippet, async_=True)
2187
2188 def test_return_inside_async_with_block(self):
2189 snippet = """
2190 async with c:
2191 if z:
2192 return
2193 else:
2194 a
2195 """
2196 self.check_stack_size(snippet, async_=True)
2197
2198
2199 if __name__ == "__main__":
2200 unittest.main()