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