1import dis 2import math 3import os 4import unittest 5import sys 6import _ast 7import tempfile 8import types 9from test import support 10from test.support import script_helper, FakePath 11 12class TestSpecifics(unittest.TestCase): 13 14 def compile_single(self, source): 15 compile(source, "<single>", "single") 16 17 def assertInvalidSingle(self, source): 18 self.assertRaises(SyntaxError, self.compile_single, source) 19 20 def test_no_ending_newline(self): 21 compile("hi", "<test>", "exec") 22 compile("hi\r", "<test>", "exec") 23 24 def test_empty(self): 25 compile("", "<test>", "exec") 26 27 def test_other_newlines(self): 28 compile("\r\n", "<test>", "exec") 29 compile("\r", "<test>", "exec") 30 compile("hi\r\nstuff\r\ndef f():\n pass\r", "<test>", "exec") 31 compile("this_is\rreally_old_mac\rdef f():\n pass", "<test>", "exec") 32 33 def test_debug_assignment(self): 34 # catch assignments to __debug__ 35 self.assertRaises(SyntaxError, compile, '__debug__ = 1', '?', 'single') 36 import builtins 37 prev = builtins.__debug__ 38 setattr(builtins, '__debug__', 'sure') 39 self.assertEqual(__debug__, prev) 40 setattr(builtins, '__debug__', prev) 41 42 def test_argument_handling(self): 43 # detect duplicate positional and keyword arguments 44 self.assertRaises(SyntaxError, eval, 'lambda a,a:0') 45 self.assertRaises(SyntaxError, eval, 'lambda a,a=1:0') 46 self.assertRaises(SyntaxError, eval, 'lambda a=1,a=1:0') 47 self.assertRaises(SyntaxError, exec, 'def f(a, a): pass') 48 self.assertRaises(SyntaxError, exec, 'def f(a = 0, a = 1): pass') 49 self.assertRaises(SyntaxError, exec, 'def f(a): global a; a = 1') 50 51 def test_syntax_error(self): 52 self.assertRaises(SyntaxError, compile, "1+*3", "filename", "exec") 53 54 def test_none_keyword_arg(self): 55 self.assertRaises(SyntaxError, compile, "f(None=1)", "<string>", "exec") 56 57 def test_duplicate_global_local(self): 58 self.assertRaises(SyntaxError, exec, 'def f(a): global a; a = 1') 59 60 def test_exec_with_general_mapping_for_locals(self): 61 62 class M: 63 "Test mapping interface versus possible calls from eval()." 64 def __getitem__(self, key): 65 if key == 'a': 66 return 12 67 raise KeyError 68 def __setitem__(self, key, value): 69 self.results = (key, value) 70 def keys(self): 71 return list('xyz') 72 73 m = M() 74 g = globals() 75 exec('z = a', g, m) 76 self.assertEqual(m.results, ('z', 12)) 77 try: 78 exec('z = b', g, m) 79 except NameError: 80 pass 81 else: 82 self.fail('Did not detect a KeyError') 83 exec('z = dir()', g, m) 84 self.assertEqual(m.results, ('z', list('xyz'))) 85 exec('z = globals()', g, m) 86 self.assertEqual(m.results, ('z', g)) 87 exec('z = locals()', g, m) 88 self.assertEqual(m.results, ('z', m)) 89 self.assertRaises(TypeError, exec, 'z = b', m) 90 91 class A: 92 "Non-mapping" 93 pass 94 m = A() 95 self.assertRaises(TypeError, exec, 'z = a', g, m) 96 97 # Verify that dict subclasses work as well 98 class D(dict): 99 def __getitem__(self, key): 100 if key == 'a': 101 return 12 102 return dict.__getitem__(self, key) 103 d = D() 104 exec('z = a', g, d) 105 self.assertEqual(d['z'], 12) 106 107 def test_extended_arg(self): 108 longexpr = 'x = x or ' + '-x' * 2500 109 g = {} 110 code = ''' 111def f(x): 112 %s 113 %s 114 %s 115 %s 116 %s 117 %s 118 %s 119 %s 120 %s 121 %s 122 # the expressions above have no effect, x == argument 123 while x: 124 x -= 1 125 # EXTENDED_ARG/JUMP_ABSOLUTE here 126 return x 127''' % ((longexpr,)*10) 128 exec(code, g) 129 self.assertEqual(g['f'](5), 0) 130 131 def test_argument_order(self): 132 self.assertRaises(SyntaxError, exec, 'def f(a=1, b): pass') 133 134 def test_float_literals(self): 135 # testing bad float literals 136 self.assertRaises(SyntaxError, eval, "2e") 137 self.assertRaises(SyntaxError, eval, "2.0e+") 138 self.assertRaises(SyntaxError, eval, "1e-") 139 self.assertRaises(SyntaxError, eval, "3-4e/21") 140 141 def test_indentation(self): 142 # testing compile() of indented block w/o trailing newline" 143 s = """ 144if 1: 145 if 2: 146 pass""" 147 compile(s, "<string>", "exec") 148 149 # This test is probably specific to CPython and may not generalize 150 # to other implementations. We are trying to ensure that when 151 # the first line of code starts after 256, correct line numbers 152 # in tracebacks are still produced. 153 def test_leading_newlines(self): 154 s256 = "".join(["\n"] * 256 + ["spam"]) 155 co = compile(s256, 'fn', 'exec') 156 self.assertEqual(co.co_firstlineno, 257) 157 self.assertEqual(co.co_lnotab, bytes()) 158 159 def test_literals_with_leading_zeroes(self): 160 for arg in ["077787", "0xj", "0x.", "0e", "090000000000000", 161 "080000000000000", "000000000000009", "000000000000008", 162 "0b42", "0BADCAFE", "0o123456789", "0b1.1", "0o4.2", 163 "0b101j2", "0o153j2", "0b100e1", "0o777e1", "0777", 164 "000777", "000000000000007"]: 165 self.assertRaises(SyntaxError, eval, arg) 166 167 self.assertEqual(eval("0xff"), 255) 168 self.assertEqual(eval("0777."), 777) 169 self.assertEqual(eval("0777.0"), 777) 170 self.assertEqual(eval("000000000000000000000000000000000000000000000000000777e0"), 777) 171 self.assertEqual(eval("0777e1"), 7770) 172 self.assertEqual(eval("0e0"), 0) 173 self.assertEqual(eval("0000e-012"), 0) 174 self.assertEqual(eval("09.5"), 9.5) 175 self.assertEqual(eval("0777j"), 777j) 176 self.assertEqual(eval("000"), 0) 177 self.assertEqual(eval("00j"), 0j) 178 self.assertEqual(eval("00.0"), 0) 179 self.assertEqual(eval("0e3"), 0) 180 self.assertEqual(eval("090000000000000."), 90000000000000.) 181 self.assertEqual(eval("090000000000000.0000000000000000000000"), 90000000000000.) 182 self.assertEqual(eval("090000000000000e0"), 90000000000000.) 183 self.assertEqual(eval("090000000000000e-0"), 90000000000000.) 184 self.assertEqual(eval("090000000000000j"), 90000000000000j) 185 self.assertEqual(eval("000000000000008."), 8.) 186 self.assertEqual(eval("000000000000009."), 9.) 187 self.assertEqual(eval("0b101010"), 42) 188 self.assertEqual(eval("-0b000000000010"), -2) 189 self.assertEqual(eval("0o777"), 511) 190 self.assertEqual(eval("-0o0000010"), -8) 191 192 def test_int_literals_too_long(self): 193 n = 3000 194 source = f"a = 1\nb = 2\nc = {'3'*n}\nd = 4" 195 with support.adjust_int_max_str_digits(n): 196 compile(source, "<long_int_pass>", "exec") # no errors. 197 with support.adjust_int_max_str_digits(n-1): 198 with self.assertRaises(SyntaxError) as err_ctx: 199 compile(source, "<long_int_fail>", "exec") 200 exc = err_ctx.exception 201 self.assertEqual(exc.lineno, 3) 202 self.assertIn('Exceeds the limit ', str(exc)) 203 self.assertIn(' Consider hexadecimal ', str(exc)) 204 205 def test_unary_minus(self): 206 # Verify treatment of unary minus on negative numbers SF bug #660455 207 if sys.maxsize == 2147483647: 208 # 32-bit machine 209 all_one_bits = '0xffffffff' 210 self.assertEqual(eval(all_one_bits), 4294967295) 211 self.assertEqual(eval("-" + all_one_bits), -4294967295) 212 elif sys.maxsize == 9223372036854775807: 213 # 64-bit machine 214 all_one_bits = '0xffffffffffffffff' 215 self.assertEqual(eval(all_one_bits), 18446744073709551615) 216 self.assertEqual(eval("-" + all_one_bits), -18446744073709551615) 217 else: 218 self.fail("How many bits *does* this machine have???") 219 # Verify treatment of constant folding on -(sys.maxsize+1) 220 # i.e. -2147483648 on 32 bit platforms. Should return int. 221 self.assertIsInstance(eval("%s" % (-sys.maxsize - 1)), int) 222 self.assertIsInstance(eval("%s" % (-sys.maxsize - 2)), int) 223 224 if sys.maxsize == 9223372036854775807: 225 def test_32_63_bit_values(self): 226 a = +4294967296 # 1 << 32 227 b = -4294967296 # 1 << 32 228 c = +281474976710656 # 1 << 48 229 d = -281474976710656 # 1 << 48 230 e = +4611686018427387904 # 1 << 62 231 f = -4611686018427387904 # 1 << 62 232 g = +9223372036854775807 # 1 << 63 - 1 233 h = -9223372036854775807 # 1 << 63 - 1 234 235 for variable in self.test_32_63_bit_values.__code__.co_consts: 236 if variable is not None: 237 self.assertIsInstance(variable, int) 238 239 def test_sequence_unpacking_error(self): 240 # Verify sequence packing/unpacking with "or". SF bug #757818 241 i,j = (1, -1) or (-1, 1) 242 self.assertEqual(i, 1) 243 self.assertEqual(j, -1) 244 245 def test_none_assignment(self): 246 stmts = [ 247 'None = 0', 248 'None += 0', 249 '__builtins__.None = 0', 250 'def None(): pass', 251 'class None: pass', 252 '(a, None) = 0, 0', 253 'for None in range(10): pass', 254 'def f(None): pass', 255 'import None', 256 'import x as None', 257 'from x import None', 258 'from x import y as None' 259 ] 260 for stmt in stmts: 261 stmt += "\n" 262 self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'single') 263 self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'exec') 264 265 def test_import(self): 266 succeed = [ 267 'import sys', 268 'import os, sys', 269 'import os as bar', 270 'import os.path as bar', 271 'from __future__ import nested_scopes, generators', 272 'from __future__ import (nested_scopes,\ngenerators)', 273 'from __future__ import (nested_scopes,\ngenerators,)', 274 'from sys import stdin, stderr, stdout', 275 'from sys import (stdin, stderr,\nstdout)', 276 'from sys import (stdin, stderr,\nstdout,)', 277 'from sys import (stdin\n, stderr, stdout)', 278 'from sys import (stdin\n, stderr, stdout,)', 279 'from sys import stdin as si, stdout as so, stderr as se', 280 'from sys import (stdin as si, stdout as so, stderr as se)', 281 'from sys import (stdin as si, stdout as so, stderr as se,)', 282 ] 283 fail = [ 284 'import (os, sys)', 285 'import (os), (sys)', 286 'import ((os), (sys))', 287 'import (sys', 288 'import sys)', 289 'import (os,)', 290 'import os As bar', 291 'import os.path a bar', 292 'from sys import stdin As stdout', 293 'from sys import stdin a stdout', 294 'from (sys) import stdin', 295 'from __future__ import (nested_scopes', 296 'from __future__ import nested_scopes)', 297 'from __future__ import nested_scopes,\ngenerators', 298 'from sys import (stdin', 299 'from sys import stdin)', 300 'from sys import stdin, stdout,\nstderr', 301 'from sys import stdin si', 302 'from sys import stdin,', 303 'from sys import (*)', 304 'from sys import (stdin,, stdout, stderr)', 305 'from sys import (stdin, stdout),', 306 ] 307 for stmt in succeed: 308 compile(stmt, 'tmp', 'exec') 309 for stmt in fail: 310 self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'exec') 311 312 def test_for_distinct_code_objects(self): 313 # SF bug 1048870 314 def f(): 315 f1 = lambda x=1: x 316 f2 = lambda x=2: x 317 return f1, f2 318 f1, f2 = f() 319 self.assertNotEqual(id(f1.__code__), id(f2.__code__)) 320 321 def test_lambda_doc(self): 322 l = lambda: "foo" 323 self.assertIsNone(l.__doc__) 324 325 def test_encoding(self): 326 code = b'# -*- coding: badencoding -*-\npass\n' 327 self.assertRaises(SyntaxError, compile, code, 'tmp', 'exec') 328 code = '# -*- coding: badencoding -*-\n"\xc2\xa4"\n' 329 compile(code, 'tmp', 'exec') 330 self.assertEqual(eval(code), '\xc2\xa4') 331 code = '"\xc2\xa4"\n' 332 self.assertEqual(eval(code), '\xc2\xa4') 333 code = b'"\xc2\xa4"\n' 334 self.assertEqual(eval(code), '\xa4') 335 code = b'# -*- coding: latin1 -*-\n"\xc2\xa4"\n' 336 self.assertEqual(eval(code), '\xc2\xa4') 337 code = b'# -*- coding: utf-8 -*-\n"\xc2\xa4"\n' 338 self.assertEqual(eval(code), '\xa4') 339 code = b'# -*- coding: iso8859-15 -*-\n"\xc2\xa4"\n' 340 self.assertEqual(eval(code), '\xc2\u20ac') 341 code = '"""\\\n# -*- coding: iso8859-15 -*-\n\xc2\xa4"""\n' 342 self.assertEqual(eval(code), '# -*- coding: iso8859-15 -*-\n\xc2\xa4') 343 code = b'"""\\\n# -*- coding: iso8859-15 -*-\n\xc2\xa4"""\n' 344 self.assertEqual(eval(code), '# -*- coding: iso8859-15 -*-\n\xa4') 345 346 def test_subscripts(self): 347 # SF bug 1448804 348 # Class to make testing subscript results easy 349 class str_map(object): 350 def __init__(self): 351 self.data = {} 352 def __getitem__(self, key): 353 return self.data[str(key)] 354 def __setitem__(self, key, value): 355 self.data[str(key)] = value 356 def __delitem__(self, key): 357 del self.data[str(key)] 358 def __contains__(self, key): 359 return str(key) in self.data 360 d = str_map() 361 # Index 362 d[1] = 1 363 self.assertEqual(d[1], 1) 364 d[1] += 1 365 self.assertEqual(d[1], 2) 366 del d[1] 367 self.assertNotIn(1, d) 368 # Tuple of indices 369 d[1, 1] = 1 370 self.assertEqual(d[1, 1], 1) 371 d[1, 1] += 1 372 self.assertEqual(d[1, 1], 2) 373 del d[1, 1] 374 self.assertNotIn((1, 1), d) 375 # Simple slice 376 d[1:2] = 1 377 self.assertEqual(d[1:2], 1) 378 d[1:2] += 1 379 self.assertEqual(d[1:2], 2) 380 del d[1:2] 381 self.assertNotIn(slice(1, 2), d) 382 # Tuple of simple slices 383 d[1:2, 1:2] = 1 384 self.assertEqual(d[1:2, 1:2], 1) 385 d[1:2, 1:2] += 1 386 self.assertEqual(d[1:2, 1:2], 2) 387 del d[1:2, 1:2] 388 self.assertNotIn((slice(1, 2), slice(1, 2)), d) 389 # Extended slice 390 d[1:2:3] = 1 391 self.assertEqual(d[1:2:3], 1) 392 d[1:2:3] += 1 393 self.assertEqual(d[1:2:3], 2) 394 del d[1:2:3] 395 self.assertNotIn(slice(1, 2, 3), d) 396 # Tuple of extended slices 397 d[1:2:3, 1:2:3] = 1 398 self.assertEqual(d[1:2:3, 1:2:3], 1) 399 d[1:2:3, 1:2:3] += 1 400 self.assertEqual(d[1:2:3, 1:2:3], 2) 401 del d[1:2:3, 1:2:3] 402 self.assertNotIn((slice(1, 2, 3), slice(1, 2, 3)), d) 403 # Ellipsis 404 d[...] = 1 405 self.assertEqual(d[...], 1) 406 d[...] += 1 407 self.assertEqual(d[...], 2) 408 del d[...] 409 self.assertNotIn(Ellipsis, d) 410 # Tuple of Ellipses 411 d[..., ...] = 1 412 self.assertEqual(d[..., ...], 1) 413 d[..., ...] += 1 414 self.assertEqual(d[..., ...], 2) 415 del d[..., ...] 416 self.assertNotIn((Ellipsis, Ellipsis), d) 417 418 def test_annotation_limit(self): 419 # more than 255 annotations, should compile ok 420 s = "def f(%s): pass" 421 s %= ', '.join('a%d:%d' % (i,i) for i in range(300)) 422 compile(s, '?', 'exec') 423 424 def test_mangling(self): 425 class A: 426 def f(): 427 __mangled = 1 428 __not_mangled__ = 2 429 import __mangled_mod 430 import __package__.module 431 432 self.assertIn("_A__mangled", A.f.__code__.co_varnames) 433 self.assertIn("__not_mangled__", A.f.__code__.co_varnames) 434 self.assertIn("_A__mangled_mod", A.f.__code__.co_varnames) 435 self.assertIn("__package__", A.f.__code__.co_varnames) 436 437 def test_compile_ast(self): 438 fname = __file__ 439 if fname.lower().endswith('pyc'): 440 fname = fname[:-1] 441 with open(fname, 'r') as f: 442 fcontents = f.read() 443 sample_code = [ 444 ['<assign>', 'x = 5'], 445 ['<ifblock>', """if True:\n pass\n"""], 446 ['<forblock>', """for n in [1, 2, 3]:\n print(n)\n"""], 447 ['<deffunc>', """def foo():\n pass\nfoo()\n"""], 448 [fname, fcontents], 449 ] 450 451 for fname, code in sample_code: 452 co1 = compile(code, '%s1' % fname, 'exec') 453 ast = compile(code, '%s2' % fname, 'exec', _ast.PyCF_ONLY_AST) 454 self.assertTrue(type(ast) == _ast.Module) 455 co2 = compile(ast, '%s3' % fname, 'exec') 456 self.assertEqual(co1, co2) 457 # the code object's filename comes from the second compilation step 458 self.assertEqual(co2.co_filename, '%s3' % fname) 459 460 # raise exception when node type doesn't match with compile mode 461 co1 = compile('print(1)', '<string>', 'exec', _ast.PyCF_ONLY_AST) 462 self.assertRaises(TypeError, compile, co1, '<ast>', 'eval') 463 464 # raise exception when node type is no start node 465 self.assertRaises(TypeError, compile, _ast.If(), '<ast>', 'exec') 466 467 # raise exception when node has invalid children 468 ast = _ast.Module() 469 ast.body = [_ast.BoolOp()] 470 self.assertRaises(TypeError, compile, ast, '<ast>', 'exec') 471 472 def test_dict_evaluation_order(self): 473 i = 0 474 475 def f(): 476 nonlocal i 477 i += 1 478 return i 479 480 d = {f(): f(), f(): f()} 481 self.assertEqual(d, {1: 2, 3: 4}) 482 483 def test_compile_filename(self): 484 for filename in 'file.py', b'file.py': 485 code = compile('pass', filename, 'exec') 486 self.assertEqual(code.co_filename, 'file.py') 487 for filename in bytearray(b'file.py'), memoryview(b'file.py'): 488 with self.assertWarns(DeprecationWarning): 489 code = compile('pass', filename, 'exec') 490 self.assertEqual(code.co_filename, 'file.py') 491 self.assertRaises(TypeError, compile, 'pass', list(b'file.py'), 'exec') 492 493 @support.cpython_only 494 def test_same_filename_used(self): 495 s = """def f(): pass\ndef g(): pass""" 496 c = compile(s, "myfile", "exec") 497 for obj in c.co_consts: 498 if isinstance(obj, types.CodeType): 499 self.assertIs(obj.co_filename, c.co_filename) 500 501 def test_single_statement(self): 502 self.compile_single("1 + 2") 503 self.compile_single("\n1 + 2") 504 self.compile_single("1 + 2\n") 505 self.compile_single("1 + 2\n\n") 506 self.compile_single("1 + 2\t\t\n") 507 self.compile_single("1 + 2\t\t\n ") 508 self.compile_single("1 + 2 # one plus two") 509 self.compile_single("1; 2") 510 self.compile_single("import sys; sys") 511 self.compile_single("def f():\n pass") 512 self.compile_single("while False:\n pass") 513 self.compile_single("if x:\n f(x)") 514 self.compile_single("if x:\n f(x)\nelse:\n g(x)") 515 self.compile_single("class T:\n pass") 516 517 def test_bad_single_statement(self): 518 self.assertInvalidSingle('1\n2') 519 self.assertInvalidSingle('def f(): pass') 520 self.assertInvalidSingle('a = 13\nb = 187') 521 self.assertInvalidSingle('del x\ndel y') 522 self.assertInvalidSingle('f()\ng()') 523 self.assertInvalidSingle('f()\n# blah\nblah()') 524 self.assertInvalidSingle('f()\nxy # blah\nblah()') 525 self.assertInvalidSingle('x = 5 # comment\nx = 6\n') 526 527 def test_particularly_evil_undecodable(self): 528 # Issue 24022 529 src = b'0000\x00\n00000000000\n\x00\n\x9e\n' 530 with tempfile.TemporaryDirectory() as tmpd: 531 fn = os.path.join(tmpd, "bad.py") 532 with open(fn, "wb") as fp: 533 fp.write(src) 534 res = script_helper.run_python_until_end(fn)[0] 535 self.assertIn(b"Non-UTF-8", res.err) 536 537 def test_yet_more_evil_still_undecodable(self): 538 # Issue #25388 539 src = b"#\x00\n#\xfd\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"Non-UTF-8", res.err) 546 547 @support.cpython_only 548 def test_compiler_recursion_limit(self): 549 # Expected limit is sys.getrecursionlimit() * the scaling factor 550 # in symtable.c (currently 3) 551 # We expect to fail *at* that limit, because we use up some of 552 # the stack depth limit in the test suite code 553 # So we check the expected limit and 75% of that 554 # XXX (ncoghlan): duplicating the scaling factor here is a little 555 # ugly. Perhaps it should be exposed somewhere... 556 fail_depth = sys.getrecursionlimit() * 3 557 success_depth = int(fail_depth * 0.75) 558 559 def check_limit(prefix, repeated): 560 expect_ok = prefix + repeated * success_depth 561 self.compile_single(expect_ok) 562 broken = prefix + repeated * fail_depth 563 details = "Compiling ({!r} + {!r} * {})".format( 564 prefix, repeated, fail_depth) 565 with self.assertRaises(RecursionError, msg=details): 566 self.compile_single(broken) 567 568 check_limit("a", "()") 569 check_limit("a", ".b") 570 check_limit("a", "[0]") 571 check_limit("a", "*a") 572 573 def test_null_terminated(self): 574 # The source code is null-terminated internally, but bytes-like 575 # objects are accepted, which could be not terminated. 576 with self.assertRaisesRegex(ValueError, "cannot contain null"): 577 compile("123\x00", "<dummy>", "eval") 578 with self.assertRaisesRegex(ValueError, "cannot contain null"): 579 compile(memoryview(b"123\x00"), "<dummy>", "eval") 580 code = compile(memoryview(b"123\x00")[1:-1], "<dummy>", "eval") 581 self.assertEqual(eval(code), 23) 582 code = compile(memoryview(b"1234")[1:-1], "<dummy>", "eval") 583 self.assertEqual(eval(code), 23) 584 code = compile(memoryview(b"$23$")[1:-1], "<dummy>", "eval") 585 self.assertEqual(eval(code), 23) 586 587 # Also test when eval() and exec() do the compilation step 588 self.assertEqual(eval(memoryview(b"1234")[1:-1]), 23) 589 namespace = dict() 590 exec(memoryview(b"ax = 123")[1:-1], namespace) 591 self.assertEqual(namespace['x'], 12) 592 593 def check_constant(self, func, expected): 594 for const in func.__code__.co_consts: 595 if repr(const) == repr(expected): 596 break 597 else: 598 self.fail("unable to find constant %r in %r" 599 % (expected, func.__code__.co_consts)) 600 601 # Merging equal constants is not a strict requirement for the Python 602 # semantics, it's a more an implementation detail. 603 @support.cpython_only 604 def test_merge_constants(self): 605 # Issue #25843: compile() must merge constants which are equal 606 # and have the same type. 607 608 def check_same_constant(const): 609 ns = {} 610 code = "f1, f2 = lambda: %r, lambda: %r" % (const, const) 611 exec(code, ns) 612 f1 = ns['f1'] 613 f2 = ns['f2'] 614 self.assertIs(f1.__code__, f2.__code__) 615 self.check_constant(f1, const) 616 self.assertEqual(repr(f1()), repr(const)) 617 618 check_same_constant(None) 619 check_same_constant(0) 620 check_same_constant(0.0) 621 check_same_constant(b'abc') 622 check_same_constant('abc') 623 624 # Note: "lambda: ..." emits "LOAD_CONST Ellipsis", 625 # whereas "lambda: Ellipsis" emits "LOAD_GLOBAL Ellipsis" 626 f1, f2 = lambda: ..., lambda: ... 627 self.assertIs(f1.__code__, f2.__code__) 628 self.check_constant(f1, Ellipsis) 629 self.assertEqual(repr(f1()), repr(Ellipsis)) 630 631 # Merge constants in tuple or frozenset 632 f1, f2 = lambda: "not a name", lambda: ("not a name",) 633 f3 = lambda x: x in {("not a name",)} 634 self.assertIs(f1.__code__.co_consts[1], 635 f2.__code__.co_consts[1][0]) 636 self.assertIs(next(iter(f3.__code__.co_consts[1])), 637 f2.__code__.co_consts[1]) 638 639 # {0} is converted to a constant frozenset({0}) by the peephole 640 # optimizer 641 f1, f2 = lambda x: x in {0}, lambda x: x in {0} 642 self.assertIs(f1.__code__, f2.__code__) 643 self.check_constant(f1, frozenset({0})) 644 self.assertTrue(f1(0)) 645 646 # This is a regression test for a CPython specific peephole optimizer 647 # implementation bug present in a few releases. It's assertion verifies 648 # that peephole optimization was actually done though that isn't an 649 # indication of the bugs presence or not (crashing is). 650 @support.cpython_only 651 def test_peephole_opt_unreachable_code_array_access_in_bounds(self): 652 """Regression test for issue35193 when run under clang msan.""" 653 def unused_code_at_end(): 654 return 3 655 raise RuntimeError("unreachable") 656 # The above function definition will trigger the out of bounds 657 # bug in the peephole optimizer as it scans opcodes past the 658 # RETURN_VALUE opcode. This does not always crash an interpreter. 659 # When you build with the clang memory sanitizer it reliably aborts. 660 self.assertEqual( 661 'RETURN_VALUE', 662 list(dis.get_instructions(unused_code_at_end))[-1].opname) 663 664 def test_dont_merge_constants(self): 665 # Issue #25843: compile() must not merge constants which are equal 666 # but have a different type. 667 668 def check_different_constants(const1, const2): 669 ns = {} 670 exec("f1, f2 = lambda: %r, lambda: %r" % (const1, const2), ns) 671 f1 = ns['f1'] 672 f2 = ns['f2'] 673 self.assertIsNot(f1.__code__, f2.__code__) 674 self.assertNotEqual(f1.__code__, f2.__code__) 675 self.check_constant(f1, const1) 676 self.check_constant(f2, const2) 677 self.assertEqual(repr(f1()), repr(const1)) 678 self.assertEqual(repr(f2()), repr(const2)) 679 680 check_different_constants(0, 0.0) 681 check_different_constants(+0.0, -0.0) 682 check_different_constants((0,), (0.0,)) 683 check_different_constants('a', b'a') 684 check_different_constants(('a',), (b'a',)) 685 686 # check_different_constants() cannot be used because repr(-0j) is 687 # '(-0-0j)', but when '(-0-0j)' is evaluated to 0j: we loose the sign. 688 f1, f2 = lambda: +0.0j, lambda: -0.0j 689 self.assertIsNot(f1.__code__, f2.__code__) 690 self.check_constant(f1, +0.0j) 691 self.check_constant(f2, -0.0j) 692 self.assertEqual(repr(f1()), repr(+0.0j)) 693 self.assertEqual(repr(f2()), repr(-0.0j)) 694 695 # {0} is converted to a constant frozenset({0}) by the peephole 696 # optimizer 697 f1, f2 = lambda x: x in {0}, lambda x: x in {0.0} 698 self.assertIsNot(f1.__code__, f2.__code__) 699 self.check_constant(f1, frozenset({0})) 700 self.check_constant(f2, frozenset({0.0})) 701 self.assertTrue(f1(0)) 702 self.assertTrue(f2(0.0)) 703 704 def test_path_like_objects(self): 705 # An implicit test for PyUnicode_FSDecoder(). 706 compile("42", FakePath("test_compile_pathlike"), "single") 707 708 def test_stack_overflow(self): 709 # bpo-31113: Stack overflow when compile a long sequence of 710 # complex statements. 711 compile("if a: b\n" * 200000, "<dummy>", "exec") 712 713 # Multiple users rely on the fact that CPython does not generate 714 # bytecode for dead code blocks. See bpo-37500 for more context. 715 @support.cpython_only 716 def test_dead_blocks_do_not_generate_bytecode(self): 717 def unused_block_if(): 718 if 0: 719 return 42 720 721 def unused_block_while(): 722 while 0: 723 return 42 724 725 def unused_block_if_else(): 726 if 1: 727 return None 728 else: 729 return 42 730 731 def unused_block_while_else(): 732 while 1: 733 return None 734 else: 735 return 42 736 737 funcs = [unused_block_if, unused_block_while, 738 unused_block_if_else, unused_block_while_else] 739 740 for func in funcs: 741 opcodes = list(dis.get_instructions(func)) 742 self.assertEqual(2, len(opcodes)) 743 self.assertEqual('LOAD_CONST', opcodes[0].opname) 744 self.assertEqual(None, opcodes[0].argval) 745 self.assertEqual('RETURN_VALUE', opcodes[1].opname) 746 747 def test_false_while_loop(self): 748 def break_in_while(): 749 while False: 750 break 751 752 def continue_in_while(): 753 while False: 754 continue 755 756 funcs = [break_in_while, continue_in_while] 757 758 # Check that we did not raise but we also don't generate bytecode 759 for func in funcs: 760 opcodes = list(dis.get_instructions(func)) 761 self.assertEqual(2, len(opcodes)) 762 self.assertEqual('LOAD_CONST', opcodes[0].opname) 763 self.assertEqual(None, opcodes[0].argval) 764 self.assertEqual('RETURN_VALUE', opcodes[1].opname) 765 766 def test_big_dict_literal(self): 767 # The compiler has a flushing point in "compiler_dict" that calls compiles 768 # a portion of the dictionary literal when the loop that iterates over the items 769 # reaches 0xFFFF elements but the code was not including the boundary element, 770 # dropping the key at position 0xFFFF. See bpo-41531 for more information 771 772 dict_size = 0xFFFF + 1 773 the_dict = "{" + ",".join(f"{x}:{x}" for x in range(dict_size)) + "}" 774 self.assertEqual(len(eval(the_dict)), dict_size) 775 776class TestExpressionStackSize(unittest.TestCase): 777 # These tests check that the computed stack size for a code object 778 # stays within reasonable bounds (see issue #21523 for an example 779 # dysfunction). 780 N = 100 781 782 def check_stack_size(self, code): 783 # To assert that the alleged stack size is not O(N), we 784 # check that it is smaller than log(N). 785 if isinstance(code, str): 786 code = compile(code, "<foo>", "single") 787 max_size = math.ceil(math.log(len(code.co_code))) 788 self.assertLessEqual(code.co_stacksize, max_size) 789 790 def test_and(self): 791 self.check_stack_size("x and " * self.N + "x") 792 793 def test_or(self): 794 self.check_stack_size("x or " * self.N + "x") 795 796 def test_and_or(self): 797 self.check_stack_size("x and x or " * self.N + "x") 798 799 def test_chained_comparison(self): 800 self.check_stack_size("x < " * self.N + "x") 801 802 def test_if_else(self): 803 self.check_stack_size("x if x else " * self.N + "x") 804 805 def test_binop(self): 806 self.check_stack_size("x + " * self.N + "x") 807 808 def test_func_and(self): 809 code = "def f(x):\n" 810 code += " x and x\n" * self.N 811 self.check_stack_size(code) 812 813 814class TestStackSizeStability(unittest.TestCase): 815 # Check that repeating certain snippets doesn't increase the stack size 816 # beyond what a single snippet requires. 817 818 def check_stack_size(self, snippet, async_=False): 819 def compile_snippet(i): 820 ns = {} 821 script = """def func():\n""" + i * snippet 822 if async_: 823 script = "async " + script 824 code = compile(script, "<script>", "exec") 825 exec(code, ns, ns) 826 return ns['func'].__code__ 827 828 sizes = [compile_snippet(i).co_stacksize for i in range(2, 5)] 829 if len(set(sizes)) != 1: 830 import dis, io 831 out = io.StringIO() 832 dis.dis(compile_snippet(1), file=out) 833 self.fail("stack sizes diverge with # of consecutive snippets: " 834 "%s\n%s\n%s" % (sizes, snippet, out.getvalue())) 835 836 def test_if(self): 837 snippet = """ 838 if x: 839 a 840 """ 841 self.check_stack_size(snippet) 842 843 def test_if_else(self): 844 snippet = """ 845 if x: 846 a 847 elif y: 848 b 849 else: 850 c 851 """ 852 self.check_stack_size(snippet) 853 854 def test_try_except_bare(self): 855 snippet = """ 856 try: 857 a 858 except: 859 b 860 """ 861 self.check_stack_size(snippet) 862 863 def test_try_except_qualified(self): 864 snippet = """ 865 try: 866 a 867 except ImportError: 868 b 869 except: 870 c 871 else: 872 d 873 """ 874 self.check_stack_size(snippet) 875 876 def test_try_except_as(self): 877 snippet = """ 878 try: 879 a 880 except ImportError as e: 881 b 882 except: 883 c 884 else: 885 d 886 """ 887 self.check_stack_size(snippet) 888 889 def test_try_finally(self): 890 snippet = """ 891 try: 892 a 893 finally: 894 b 895 """ 896 self.check_stack_size(snippet) 897 898 def test_with(self): 899 snippet = """ 900 with x as y: 901 a 902 """ 903 self.check_stack_size(snippet) 904 905 def test_while_else(self): 906 snippet = """ 907 while x: 908 a 909 else: 910 b 911 """ 912 self.check_stack_size(snippet) 913 914 def test_for(self): 915 snippet = """ 916 for x in y: 917 a 918 """ 919 self.check_stack_size(snippet) 920 921 def test_for_else(self): 922 snippet = """ 923 for x in y: 924 a 925 else: 926 b 927 """ 928 self.check_stack_size(snippet) 929 930 def test_for_break_continue(self): 931 snippet = """ 932 for x in y: 933 if z: 934 break 935 elif u: 936 continue 937 else: 938 a 939 else: 940 b 941 """ 942 self.check_stack_size(snippet) 943 944 def test_for_break_continue_inside_try_finally_block(self): 945 snippet = """ 946 for x in y: 947 try: 948 if z: 949 break 950 elif u: 951 continue 952 else: 953 a 954 finally: 955 f 956 else: 957 b 958 """ 959 self.check_stack_size(snippet) 960 961 def test_for_break_continue_inside_finally_block(self): 962 snippet = """ 963 for x in y: 964 try: 965 t 966 finally: 967 if z: 968 break 969 elif u: 970 continue 971 else: 972 a 973 else: 974 b 975 """ 976 self.check_stack_size(snippet) 977 978 def test_for_break_continue_inside_except_block(self): 979 snippet = """ 980 for x in y: 981 try: 982 t 983 except: 984 if z: 985 break 986 elif u: 987 continue 988 else: 989 a 990 else: 991 b 992 """ 993 self.check_stack_size(snippet) 994 995 def test_for_break_continue_inside_with_block(self): 996 snippet = """ 997 for x in y: 998 with c: 999 if z: 1000 break 1001 elif u: 1002 continue 1003 else: 1004 a 1005 else: 1006 b 1007 """ 1008 self.check_stack_size(snippet) 1009 1010 def test_return_inside_try_finally_block(self): 1011 snippet = """ 1012 try: 1013 if z: 1014 return 1015 else: 1016 a 1017 finally: 1018 f 1019 """ 1020 self.check_stack_size(snippet) 1021 1022 def test_return_inside_finally_block(self): 1023 snippet = """ 1024 try: 1025 t 1026 finally: 1027 if z: 1028 return 1029 else: 1030 a 1031 """ 1032 self.check_stack_size(snippet) 1033 1034 def test_return_inside_except_block(self): 1035 snippet = """ 1036 try: 1037 t 1038 except: 1039 if z: 1040 return 1041 else: 1042 a 1043 """ 1044 self.check_stack_size(snippet) 1045 1046 def test_return_inside_with_block(self): 1047 snippet = """ 1048 with c: 1049 if z: 1050 return 1051 else: 1052 a 1053 """ 1054 self.check_stack_size(snippet) 1055 1056 def test_async_with(self): 1057 snippet = """ 1058 async with x as y: 1059 a 1060 """ 1061 self.check_stack_size(snippet, async_=True) 1062 1063 def test_async_for(self): 1064 snippet = """ 1065 async for x in y: 1066 a 1067 """ 1068 self.check_stack_size(snippet, async_=True) 1069 1070 def test_async_for_else(self): 1071 snippet = """ 1072 async for x in y: 1073 a 1074 else: 1075 b 1076 """ 1077 self.check_stack_size(snippet, async_=True) 1078 1079 def test_for_break_continue_inside_async_with_block(self): 1080 snippet = """ 1081 for x in y: 1082 async with c: 1083 if z: 1084 break 1085 elif u: 1086 continue 1087 else: 1088 a 1089 else: 1090 b 1091 """ 1092 self.check_stack_size(snippet, async_=True) 1093 1094 def test_return_inside_async_with_block(self): 1095 snippet = """ 1096 async with c: 1097 if z: 1098 return 1099 else: 1100 a 1101 """ 1102 self.check_stack_size(snippet, async_=True) 1103 1104 1105if __name__ == "__main__": 1106 unittest.main() 1107