this repo has no description
at trunk 641 lines 22 kB view raw
1#!/usr/bin/env python3 2# Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) 3import contextlib 4import sys 5import unittest 6from _io import StringIO, TextIOWrapper 7 8from test_support import pyro_only, cpython_only 9 10 11# Tests for traceback printing in sys.excepthook 12def panic(): 13 raise RuntimeError("PANIC!!!") 14 15 16def call_panic(): 17 # empty line 18 panic() 19 20 21def raise_after_n_frames(n): 22 if not n: 23 raise RuntimeError("PANIC!!!") 24 raise_after_n_frames(n - 1) 25 26 27class DisplayhookTest(unittest.TestCase): 28 def test_displayhook_with_none_does_not_set_underscore(self): 29 import builtins 30 31 if hasattr(builtins, "_"): 32 del builtins._ 33 34 orig_out = sys.stdout 35 out = StringIO() 36 sys.stdout = out 37 sys.__displayhook__(None) 38 self.assertEqual(out.getvalue(), "") 39 self.assertTrue(not hasattr(builtins, "_")) 40 sys.stdout = orig_out 41 42 def test_displayhook_with_int_sets_underscore(self): 43 import builtins 44 45 orig_out = sys.stdout 46 out = StringIO() 47 sys.stdout = out 48 sys.__displayhook__(42) 49 self.assertEqual(out.getvalue(), "42\n") 50 self.assertEqual(builtins._, 42) 51 sys.stdout = orig_out 52 53 def test_has_displayhook(self): 54 self.assertTrue(hasattr(sys, "displayhook")) 55 56 def test_displayhook_initial_value(self): 57 self.assertIs(sys.displayhook, sys.__displayhook__) 58 59 def test_dunder_std_streams_are_text_io_wrappers(self): 60 self.assertIsInstance(sys.__stderr__, TextIOWrapper) 61 self.assertIsInstance(sys.__stdin__, TextIOWrapper) 62 self.assertIsInstance(sys.__stdout__, TextIOWrapper) 63 64 65class ExceptHookTests(unittest.TestCase): 66 def test_traceback_with_sys_tracebacklimit_truncates_stack_trace(self): 67 has_tracebacklimit = hasattr(sys, "tracebacklimit") 68 if has_tracebacklimit: 69 tmp = sys.tracebacklimit 70 71 with StringIO() as stderr, contextlib.redirect_stderr(stderr): 72 try: 73 sys.tracebacklimit = 1 74 call_panic() 75 except Exception: 76 sys.excepthook(*sys.exc_info()) 77 finally: 78 if has_tracebacklimit: 79 sys.tracebacklimit = tmp 80 else: 81 del sys.tracebacklimit 82 self.assertRegex( 83 stderr.getvalue(), 84 r"""Traceback \(most recent call last\): 85 File ".*/sys_test.py", line \d+, in panic 86 raise RuntimeError\("PANIC!!!"\) 87RuntimeError: PANIC!!! 88""", 89 ) 90 91 def test_traceback_without_sys_tracebacklimit_prints_entire_small_traceback(self): 92 has_tracebacklimit = hasattr(sys, "tracebacklimit") 93 if has_tracebacklimit: 94 tmp = sys.tracebacklimit 95 del sys.tracebacklimit 96 97 with StringIO() as stderr, contextlib.redirect_stderr(stderr): 98 try: 99 call_panic() 100 except Exception: 101 sys.excepthook(*sys.exc_info()) 102 finally: 103 if has_tracebacklimit: 104 sys.tracebacklimit = tmp 105 self.assertRegex( 106 stderr.getvalue(), 107 r"""Traceback \(most recent call last\): 108 File ".*/sys_test.py", line \d+, in test_traceback_without_sys_tracebacklimit_prints_entire_small_traceback 109 call_panic\(\) 110 File ".*/sys_test.py", line \d+, in call_panic 111 panic\(\) 112 File ".*/sys_test.py", line \d+, in panic 113 raise RuntimeError\("PANIC!!!"\) 114RuntimeError: PANIC!!! 115""", 116 ) 117 118 def test_traceback_cuts_recursion_at_3_repeated_lines(self): 119 has_tracebacklimit = hasattr(sys, "tracebacklimit") 120 if has_tracebacklimit: 121 tmp = sys.tracebacklimit 122 del sys.tracebacklimit 123 124 with StringIO() as stderr, contextlib.redirect_stderr(stderr): 125 try: 126 raise_after_n_frames(4) 127 except Exception: 128 sys.excepthook(*sys.exc_info()) 129 finally: 130 if has_tracebacklimit: 131 sys.tracebacklimit = tmp 132 self.assertRegex( 133 stderr.getvalue(), 134 r"""Traceback \(most recent call last\): 135 File ".*/sys_test.py", line \d+, in test_traceback_cuts_recursion_at_3_repeated_lines 136 raise_after_n_frames\(4\) 137 File ".*/sys_test.py", line \d+, in raise_after_n_frames 138 raise_after_n_frames\(n - 1\) 139 File ".*/sys_test.py", line \d+, in raise_after_n_frames 140 raise_after_n_frames\(n - 1\) 141 File ".*/sys_test.py", line \d+, in raise_after_n_frames 142 raise_after_n_frames\(n - 1\) 143 \[Previous line repeated 1 more time\] 144 File ".*/sys_test.py", line \d+, in raise_after_n_frames 145 raise RuntimeError\("PANIC!!!"\) 146RuntimeError: PANIC!!! 147""", 148 ) 149 150 def test_traceback_limits_recursion_and_depth(self): 151 has_tracebacklimit = hasattr(sys, "tracebacklimit") 152 if has_tracebacklimit: 153 tmp = sys.tracebacklimit 154 155 with StringIO() as stderr, contextlib.redirect_stderr(stderr): 156 try: 157 sys.tracebacklimit = 10 158 raise_after_n_frames(20) 159 except Exception: 160 sys.excepthook(*sys.exc_info()) 161 finally: 162 if has_tracebacklimit: 163 sys.tracebacklimit = tmp 164 else: 165 del sys.tracebacklimit 166 self.assertRegex( 167 stderr.getvalue(), 168 r"""Traceback \(most recent call last\): 169 File ".*/sys_test.py", line \d+, in raise_after_n_frames 170 raise_after_n_frames\(n - 1\) 171 File ".*/sys_test.py", line \d+, in raise_after_n_frames 172 raise_after_n_frames\(n - 1\) 173 File ".*/sys_test.py", line \d+, in raise_after_n_frames 174 raise_after_n_frames\(n - 1\) 175 \[Previous line repeated 6 more times\] 176 File ".*/sys_test.py", line \d+, in raise_after_n_frames 177 raise RuntimeError\("PANIC!!!"\) 178RuntimeError: PANIC!!! 179""", 180 ) 181 182 183class SysTests(unittest.TestCase): 184 class Mgr: 185 def __enter__(self): 186 pass 187 188 def __exit__(self, type, value, tb): 189 return True 190 191 def test_excepthook_initial_value(self): 192 self.assertIs(sys.excepthook, sys.__excepthook__) 193 194 def test_exit_raises_system_exit(self): 195 with self.assertRaises(SystemExit) as ctx: 196 sys.exit() 197 198 self.assertEqual(ctx.exception.args, ()) 199 200 def test_exit_with_code_raises_system_exit_with_code(self): 201 with self.assertRaises(SystemExit) as ctx: 202 sys.exit("foo") 203 204 self.assertEqual(ctx.exception.args, ("foo",)) 205 206 def test_exc_info_with_context_manager(self): 207 try: 208 raise RuntimeError() 209 except RuntimeError: 210 info1 = sys.exc_info() 211 with self.Mgr(): 212 raise ValueError() 213 info2 = sys.exc_info() 214 self.assertEqual(info1, info2) 215 216 def test_getdefaultencoding_returns_utf8(self): 217 self.assertEqual(sys.getdefaultencoding(), "utf-8") 218 219 def test_getsizeof_without_dunder_sizeof_raises_type_error(self): 220 class M(type): 221 def mro(cls): 222 return (cls,) 223 224 class C(metaclass=M): 225 __new__ = type.__new__ 226 __call__ = type.__call__ 227 228 with self.assertRaises(TypeError): 229 sys.getsizeof(C()) 230 231 def test_getsizeof_with_non_int_without_default_raises_type_error(self): 232 class C: 233 def __sizeof__(self): 234 return "not an integer" 235 236 with self.assertRaises(TypeError): 237 sys.getsizeof(C()) 238 239 def test_getsizeof_with_non_int_returns_default(self): 240 class C: 241 def __sizeof__(self): 242 return "not an integer" 243 244 self.assertEqual(sys.getsizeof(C(), 42), 42) 245 246 def test_getsizeof_with_negative_raises_value_error(self): 247 class C: 248 def __sizeof__(self): 249 return -1 250 251 with self.assertRaises(ValueError): 252 sys.getsizeof(C()) 253 254 @pyro_only 255 def test_getsizeof_without_default_returns_size_int(self): 256 class C: 257 def __sizeof__(self): 258 return 42 259 260 self.assertEqual(sys.getsizeof(C()), 42) 261 262 @pyro_only 263 def test_getsizeof_with_default_returns_size_int(self): 264 class C: 265 def __sizeof__(self): 266 return 42 267 268 self.assertEqual(sys.getsizeof(C(), 3), 42) 269 270 @pyro_only 271 def test_getsizeof_with_int_subclass_returns_int(self): 272 class N(int): 273 pass 274 275 class C: 276 def __sizeof__(self): 277 return N(42) 278 279 result = sys.getsizeof(C()) 280 self.assertIs(type(result), int) 281 self.assertEqual(result, 42) 282 283 def test_getsetrecursionlimit(self): 284 limit = sys.getrecursionlimit() 285 self.assertGreater(limit, 0) 286 sys.setrecursionlimit(limit + 1) 287 self.assertEqual(sys.getrecursionlimit(), limit + 1) 288 sys.setrecursionlimit(limit) 289 self.assertEqual(sys.getrecursionlimit(), limit) 290 291 def test_gettrace_returns_none(self): 292 self.assertIs(sys.gettrace(), None) 293 294 def test_implementation_cache_tag_matches_version_major_minor(self): 295 name = sys.implementation.name 296 major, minor = sys.version_info.major, sys.version_info.minor 297 cache_tag = f"{name}-{major}{minor}" 298 self.assertEqual(sys.implementation.cache_tag, cache_tag) 299 300 def test_implementation_version_matches_module_version_info(self): 301 self.assertEqual(sys.implementation.version, sys.version_info) 302 303 def test_settrace_with_none_does_nothing(self): 304 sys.settrace(None) 305 self.assertIs(sys.gettrace(), None) 306 307 def test_setrecursionlimit_with_large_limit_raises_overflowerror(self): 308 with self.assertRaises(OverflowError) as context: 309 sys.setrecursionlimit(230992039023490234904329023904239023) 310 self.assertEqual( 311 str(context.exception), "Python int too large to convert to C int" 312 ) 313 314 def test_hash_info_is_plausible(self): 315 def is_power_of_two(x): 316 return x & (x - 1) == 0 317 318 hash_info = sys.hash_info 319 max_value = (1 << (hash_info.width - 1)) - 1 320 self.assertTrue(hash_info.modulus <= max_value) 321 self.assertTrue(is_power_of_two(hash_info.modulus + 1)) 322 self.assertTrue(hash_info.inf <= max_value) 323 self.assertTrue(hash_info.nan <= max_value) 324 self.assertTrue(hash_info.imag <= max_value) 325 self.assertIsInstance(hash_info.algorithm, str) 326 self.assertTrue(hash_info.hash_bits >= hash_info.width) 327 self.assertTrue(hash_info.seed_bits >= hash_info.hash_bits) 328 self.assertIs(hash_info.width, hash_info[0]) 329 self.assertIs(hash_info.modulus, hash_info[1]) 330 self.assertIs(hash_info.inf, hash_info[2]) 331 self.assertIs(hash_info.nan, hash_info[3]) 332 self.assertIs(hash_info.imag, hash_info[4]) 333 self.assertIs(hash_info.algorithm, hash_info[5]) 334 self.assertIs(hash_info.hash_bits, hash_info[6]) 335 self.assertIs(hash_info.seed_bits, hash_info[7]) 336 self.assertIs(hash_info.cutoff, hash_info[8]) 337 338 def test_hash_info_matches_cpython(self): 339 # We should not deviate from cpython without a good reason. 340 hash_info = sys.hash_info 341 self.assertEqual(hash_info.modulus, (1 << 61) - 1) 342 self.assertEqual(hash_info.inf, 314159) 343 self.assertEqual(hash_info.nan, 0) 344 self.assertEqual(hash_info.imag, 1000003) 345 self.assertEqual(hash_info.algorithm, "siphash24") 346 self.assertEqual(hash_info.hash_bits, 64) 347 self.assertEqual(hash_info.seed_bits, 128) 348 349 def test_float_info_matches_cpython(self): 350 float_info = sys.float_info 351 self.assertEqual(float_info.max, 1.7976931348623157e308) 352 self.assertEqual(float_info.max_exp, 1024) 353 self.assertEqual(float_info.max_10_exp, 308) 354 self.assertEqual(float_info.min, 2.2250738585072014e-308) 355 self.assertEqual(float_info.min_exp, -1021) 356 self.assertEqual(float_info.min_10_exp, -307) 357 self.assertEqual(float_info.dig, 15) 358 self.assertEqual(float_info.mant_dig, 53) 359 self.assertEqual(float_info.epsilon, 2.220446049250313e-16) 360 self.assertEqual(float_info.radix, 2) 361 self.assertEqual(float_info.rounds, 1) 362 363 def test_intern_returns_str(self): 364 self.assertEqual(sys.intern("id"), "id") 365 self.assertEqual(sys.intern("long identifier"), "long identifier") 366 367 def test_intern_with_nonstr_raises_typeerror(self): 368 with self.assertRaises(TypeError): 369 sys.intern(12345) 370 371 def test_intern_with_str_subclass_raises_typeerror(self): 372 class NewString(str): 373 pass 374 375 with self.assertRaises(TypeError) as context: 376 sys.intern(NewString("identifier")) 377 378 self.assertEqual(str(context.exception), "can't intern NewString") 379 380 def test_is_finalizing_before_shutdown_returns_false(self): 381 self.assertEqual(sys.is_finalizing(), False) 382 383 def test_stdio_initial_values(self): 384 self.assertIs(sys.stderr, sys.__stderr__) 385 self.assertIs(sys.stdin, sys.__stdin__) 386 self.assertIs(sys.stdout, sys.__stdout__) 387 388 def test_std_streams_are_utf_8_encoded(self): 389 self.assertEqual(sys.stderr.encoding, "utf-8") 390 self.assertEqual(sys.stdin.encoding, "utf-8") 391 self.assertEqual(sys.stdout.encoding, "utf-8") 392 393 def test_std_streams_have_correct_modes(self): 394 self.assertEqual(sys.stderr.mode, "w") 395 self.assertEqual(sys.stdin.mode, "r") 396 self.assertEqual(sys.stdout.mode, "w") 397 398 @pyro_only 399 def test_std_streams_point_to_correct_fileno(self): 400 self.assertEqual(sys.stderr.buffer.fileno(), sys._stderr_fd) 401 self.assertEqual(sys.stdin.buffer.fileno(), sys._stdin_fd) 402 self.assertEqual(sys.stdout.buffer.fileno(), sys._stdout_fd) 403 404 # TODO(T89882231) enable test for Pyro 405 @cpython_only 406 def test_under_getframe_returns_frame(self): 407 from types import ModuleType 408 409 frame = sys._getframe(0) 410 self.assertTrue(frame.f_globals is not None) 411 self.assertEqual(frame.f_globals["__name__"], "__main__") 412 self.assertTrue(frame.f_locals is not None) 413 self.assertEqual(frame.f_locals["self"], self) 414 builtins = __builtins__ 415 if isinstance(builtins, ModuleType): 416 builtins = builtins.__dict__ 417 self.assertIs(frame.f_builtins, builtins) 418 self.assertTrue(frame.f_code is not None) 419 420 def test_under_getframe_with_noninteger_raises_typeerror(self): 421 with self.assertRaises(TypeError): 422 sys._getframe(None) 423 424 # TODO(T89882231) enable test for Pyro 425 @cpython_only 426 def test_under_getframe_returns_frame_with_locals(self): 427 def baz(): 428 return sys._getframe(1).f_locals 429 430 def bar(): 431 foo = 1 # noqa: F841 432 return baz() 433 434 bar_locals = bar() 435 self.assertEqual(len(bar_locals), 2) 436 self.assertEqual(bar_locals["foo"], 1) 437 self.assertEqual(bar_locals["baz"], baz) 438 439 def test_under_getframe_from_function_cannot_modify_locals(self): 440 def baz(): 441 sys._getframe(1).f_locals["foo"] = "wrong" 442 443 def bar(): 444 foo = "correct" 445 baz() 446 return foo 447 448 self.assertEqual(bar(), "correct") 449 450 def test_under_getframe_from_global_scope_gets_locals(self): 451 from types import ModuleType 452 453 module = ModuleType("") 454 module.sys = sys 455 module.temp = "wrong" 456 exec("sys._getframe(0).f_locals['temp'] = 'correct'", module.__dict__) 457 self.assertEqual(module.temp, "correct") 458 459 def test_under_getframe_with_high_depth_raises_valueerror(self): 460 with self.assertRaises(ValueError) as context: 461 sys._getframe(1000) 462 self.assertEqual(str(context.exception), "call stack is not deep enough") 463 464 def test_under_getframe_with_negative_integer_returns_top_frame(self): 465 self.assertEqual(sys._getframe(-1).f_code, sys._getframe(0).f_code) 466 467 def test_under_getframe_with_no_argument_returns_top_frame(self): 468 self.assertEqual(sys._getframe().f_code, sys._getframe(0).f_code) 469 470 def test_under_getframe_f_back_points_to_previous_frame(self): 471 def baz(): 472 return sys._getframe(0) 473 474 def bar(): 475 return baz() 476 477 def foo(): 478 return bar() 479 480 frame = foo() 481 self.assertIs(frame.f_code, baz.__code__) 482 self.assertIs(frame.f_back.f_code, bar.__code__) 483 self.assertIs(frame.f_back.f_back.f_code, foo.__code__) 484 485 def test_under_getframe_f_back_leads_to_module_frame(self): 486 frame = sys._getframe() 487 while True: 488 if frame.f_back is None: 489 break 490 frame = frame.f_back 491 self.assertIsNone(frame.f_back) 492 self.assertIs(frame.f_globals, sys.modules[self.__module__].__dict__) 493 494 @pyro_only 495 def test_under_getframe_f_back_excludes_builtins_function(self): 496 recorded_frame = None 497 498 class C: 499 def __hash__(self): 500 nonlocal recorded_frame 501 recorded_frame = sys._getframe() 502 return 1234 503 504 def foo(): 505 c = C() 506 d = {} 507 # Calling C.__hash__ via native code, dict.__delitem__. 508 try: 509 del d[c] 510 except KeyError: 511 pass 512 513 foo() 514 515 self.assertIs(recorded_frame.f_code, C.__hash__.__code__) 516 # The result excludes dict.__delitem__. 517 self.assertIs(recorded_frame.f_back.f_code, foo.__code__) 518 519 def test_version(self): 520 self.assertTrue(sys.version) 521 self.assertEqual(len(sys.version_info), 5) 522 523 def test_hexversion(self): 524 self.assertIsInstance(sys.hexversion, int) 525 self.assertEqual((sys.hexversion >> 24) & 0xFF, sys.version_info.major) 526 self.assertEqual((sys.hexversion >> 16) & 0xFF, sys.version_info.minor) 527 self.assertEqual((sys.hexversion >> 8) & 0xFF, sys.version_info.micro) 528 release_level = (sys.hexversion >> 4) & 0xF 529 release_level_str = {0xA: "alpha", 0xB: "beta", 0xC: "candidate", 0xF: "final"} 530 self.assertEqual( 531 release_level_str[release_level], sys.version_info.releaselevel 532 ) 533 self.assertEqual(sys.hexversion & 0xF, sys.version_info.serial) 534 535 def test_under_getframe_f_lineno(self): 536 d = {} 537 exec("import sys\n\nresult = sys._getframe().f_lineno", d) 538 self.assertIs(d["result"], 3) 539 540 def test_set_asyncgen_hooks_raises_type_error_on_non_none_non_callable_finalizer( 541 self, 542 ): 543 with self.assertRaises(TypeError): 544 sys.set_asyncgen_hooks(finalizer=1) 545 546 def test_set_asyncgen_hooks_raises_type_error_on_non_none_non_callable_firstiter( 547 self, 548 ): 549 with self.assertRaises(TypeError): 550 sys.set_asyncgen_hooks(firstiter=1) 551 552 def test_set_asyncgen_hooks_with_none_values(self): 553 sys.set_asyncgen_hooks(None, None) 554 hooks = sys.get_asyncgen_hooks() 555 self.assertIsNone(hooks[0], None) 556 self.assertIsNone(hooks[1], None) 557 558 def test_set_asyncgen_hooks_with_callables(self): 559 def f1(): 560 pass 561 562 def f2(): 563 pass 564 565 sys.set_asyncgen_hooks(f1, f2) 566 hooks = sys.get_asyncgen_hooks() 567 self.assertEqual(hooks[0], f1) 568 self.assertEqual(hooks[1], f2) 569 570 def test_set_asyncgen_hooks_with_only_named_firstiter(self): 571 def f(): 572 pass 573 574 # Clear any existing values 575 sys.set_asyncgen_hooks(None, None) 576 577 sys.set_asyncgen_hooks(firstiter=f) 578 hooks = sys.get_asyncgen_hooks() 579 self.assertEqual(hooks[0], f) 580 self.assertEqual(hooks[1], None) 581 582 def test_set_asyncgen_hooks_with_only_positional_firstiter(self): 583 def f(): 584 pass 585 586 # Clear any existing values 587 sys.set_asyncgen_hooks(None, None) 588 589 sys.set_asyncgen_hooks(f) 590 hooks = sys.get_asyncgen_hooks() 591 self.assertEqual(hooks[0], f) 592 self.assertEqual(hooks[1], None) 593 594 def test_set_asyncgen_hooks_with_only_named_finalizer(self): 595 def f(): 596 pass 597 598 # Clear any existing values 599 sys.set_asyncgen_hooks(None, None) 600 601 sys.set_asyncgen_hooks(finalizer=f) 602 hooks = sys.get_asyncgen_hooks() 603 self.assertEqual(hooks[0], None) 604 self.assertEqual(hooks[1], f) 605 606 def test_set_asyncgen_hooks_with_no_args(self): 607 def f1(): 608 pass 609 610 def f2(): 611 pass 612 613 # Set initial values which shouldn't be affected 614 sys.set_asyncgen_hooks(f1, f2) 615 616 sys.set_asyncgen_hooks() 617 hooks = sys.get_asyncgen_hooks() 618 self.assertEqual(hooks[0], f1) 619 self.assertEqual(hooks[1], f2) 620 621 def test_asyncgen_hooks_attributes(self): 622 def f1(): 623 pass 624 625 def f2(): 626 pass 627 628 sys.set_asyncgen_hooks(f1, f2) 629 hooks = sys.get_asyncgen_hooks() 630 self.assertEqual(hooks.firstiter, f1) 631 self.assertEqual(hooks.finalizer, f2) 632 633 def test_executable_contains_python3(self): 634 self.assertIn("python", sys.executable) 635 636 def test_under_base_executable_contains_python3(self): 637 self.assertIn("python", sys._base_executable) 638 639 640if __name__ == "__main__": 641 unittest.main()