1# Tests invocation of the interpreter with various command line arguments 2# Most tests are executed with environment variables ignored 3# See test_cmd_line_script.py for testing of script execution 4 5import os 6import subprocess 7import sys 8import tempfile 9import unittest 10from test import support 11from test.support.script_helper import ( 12 spawn_python, kill_python, assert_python_ok, assert_python_failure, 13 interpreter_requires_environment 14) 15 16 17# Debug build? 18Py_DEBUG = hasattr(sys, "gettotalrefcount") 19 20 21# XXX (ncoghlan): Move to script_helper and make consistent with run_python 22def _kill_python_and_exit_code(p): 23 data = kill_python(p) 24 returncode = p.wait() 25 return data, returncode 26 27class CmdLineTest(unittest.TestCase): 28 def test_directories(self): 29 assert_python_failure('.') 30 assert_python_failure('< .') 31 32 def verify_valid_flag(self, cmd_line): 33 rc, out, err = assert_python_ok(*cmd_line) 34 self.assertTrue(out == b'' or out.endswith(b'\n')) 35 self.assertNotIn(b'Traceback', out) 36 self.assertNotIn(b'Traceback', err) 37 38 def test_optimize(self): 39 self.verify_valid_flag('-O') 40 self.verify_valid_flag('-OO') 41 42 def test_site_flag(self): 43 self.verify_valid_flag('-S') 44 45 def test_usage(self): 46 rc, out, err = assert_python_ok('-h') 47 self.assertIn(b'usage', out) 48 49 def test_version(self): 50 version = ('Python %d.%d' % sys.version_info[:2]).encode("ascii") 51 for switch in '-V', '--version', '-VV': 52 rc, out, err = assert_python_ok(switch) 53 self.assertFalse(err.startswith(version)) 54 self.assertTrue(out.startswith(version)) 55 56 def test_verbose(self): 57 # -v causes imports to write to stderr. If the write to 58 # stderr itself causes an import to happen (for the output 59 # codec), a recursion loop can occur. 60 rc, out, err = assert_python_ok('-v') 61 self.assertNotIn(b'stack overflow', err) 62 rc, out, err = assert_python_ok('-vv') 63 self.assertNotIn(b'stack overflow', err) 64 65 @unittest.skipIf(interpreter_requires_environment(), 66 'Cannot run -E tests when PYTHON env vars are required.') 67 def test_xoptions(self): 68 def get_xoptions(*args): 69 # use subprocess module directly because test.support.script_helper adds 70 # "-X faulthandler" to the command line 71 args = (sys.executable, '-E') + args 72 args += ('-c', 'import sys; print(sys._xoptions)') 73 out = subprocess.check_output(args) 74 opts = eval(out.splitlines()[0]) 75 return opts 76 77 opts = get_xoptions() 78 self.assertEqual(opts, {}) 79 80 opts = get_xoptions('-Xa', '-Xb=c,d=e') 81 self.assertEqual(opts, {'a': True, 'b': 'c,d=e'}) 82 83 def test_showrefcount(self): 84 def run_python(*args): 85 # this is similar to assert_python_ok but doesn't strip 86 # the refcount from stderr. It can be replaced once 87 # assert_python_ok stops doing that. 88 cmd = [sys.executable] 89 cmd.extend(args) 90 PIPE = subprocess.PIPE 91 p = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) 92 out, err = p.communicate() 93 p.stdout.close() 94 p.stderr.close() 95 rc = p.returncode 96 self.assertEqual(rc, 0) 97 return rc, out, err 98 code = 'import sys; print(sys._xoptions)' 99 # normally the refcount is hidden 100 rc, out, err = run_python('-c', code) 101 self.assertEqual(out.rstrip(), b'{}') 102 self.assertEqual(err, b'') 103 # "-X showrefcount" shows the refcount, but only in debug builds 104 rc, out, err = run_python('-X', 'showrefcount', '-c', code) 105 self.assertEqual(out.rstrip(), b"{'showrefcount': True}") 106 if Py_DEBUG: 107 self.assertRegex(err, br'^\[\d+ refs, \d+ blocks\]') 108 else: 109 self.assertEqual(err, b'') 110 111 def test_run_module(self): 112 # Test expected operation of the '-m' switch 113 # Switch needs an argument 114 assert_python_failure('-m') 115 # Check we get an error for a nonexistent module 116 assert_python_failure('-m', 'fnord43520xyz') 117 # Check the runpy module also gives an error for 118 # a nonexistent module 119 assert_python_failure('-m', 'runpy', 'fnord43520xyz') 120 # All good if module is located and run successfully 121 assert_python_ok('-m', 'timeit', '-n', '1') 122 123 def test_run_module_bug1764407(self): 124 # -m and -i need to play well together 125 # Runs the timeit module and checks the __main__ 126 # namespace has been populated appropriately 127 p = spawn_python('-i', '-m', 'timeit', '-n', '1') 128 p.stdin.write(b'Timer\n') 129 p.stdin.write(b'exit()\n') 130 data = kill_python(p) 131 self.assertTrue(data.find(b'1 loop') != -1) 132 self.assertTrue(data.find(b'__main__.Timer') != -1) 133 134 def test_run_code(self): 135 # Test expected operation of the '-c' switch 136 # Switch needs an argument 137 assert_python_failure('-c') 138 # Check we get an error for an uncaught exception 139 assert_python_failure('-c', 'raise Exception') 140 # All good if execution is successful 141 assert_python_ok('-c', 'pass') 142 143 @unittest.skipUnless(support.FS_NONASCII, 'need support.FS_NONASCII') 144 def test_non_ascii(self): 145 # Test handling of non-ascii data 146 command = ("assert(ord(%r) == %s)" 147 % (support.FS_NONASCII, ord(support.FS_NONASCII))) 148 assert_python_ok('-c', command) 149 150 # On Windows, pass bytes to subprocess doesn't test how Python decodes the 151 # command line, but how subprocess does decode bytes to unicode. Python 152 # doesn't decode the command line because Windows provides directly the 153 # arguments as unicode (using wmain() instead of main()). 154 @unittest.skipIf(sys.platform == 'win32', 155 'Windows has a native unicode API') 156 def test_undecodable_code(self): 157 undecodable = b"\xff" 158 env = os.environ.copy() 159 # Use C locale to get ascii for the locale encoding 160 env['LC_ALL'] = 'C' 161 env['PYTHONCOERCECLOCALE'] = '0' 162 code = ( 163 b'import locale; ' 164 b'print(ascii("' + undecodable + b'"), ' 165 b'locale.getpreferredencoding())') 166 p = subprocess.Popen( 167 [sys.executable, "-c", code], 168 stdout=subprocess.PIPE, stderr=subprocess.STDOUT, 169 env=env) 170 stdout, stderr = p.communicate() 171 if p.returncode == 1: 172 # _Py_char2wchar() decoded b'\xff' as '\udcff' (b'\xff' is not 173 # decodable from ASCII) and run_command() failed on 174 # PyUnicode_AsUTF8String(). This is the expected behaviour on 175 # Linux. 176 pattern = b"Unable to decode the command from the command line:" 177 elif p.returncode == 0: 178 # _Py_char2wchar() decoded b'\xff' as '\xff' even if the locale is 179 # C and the locale encoding is ASCII. It occurs on FreeBSD, Solaris 180 # and Mac OS X. 181 pattern = b"'\\xff' " 182 # The output is followed by the encoding name, an alias to ASCII. 183 # Examples: "US-ASCII" or "646" (ISO 646, on Solaris). 184 else: 185 raise AssertionError("Unknown exit code: %s, output=%a" % (p.returncode, stdout)) 186 if not stdout.startswith(pattern): 187 raise AssertionError("%a doesn't start with %a" % (stdout, pattern)) 188 189 @unittest.skipUnless((sys.platform == 'darwin' or 190 support.is_android), 'test specific to Mac OS X and Android') 191 def test_osx_android_utf8(self): 192 def check_output(text): 193 decoded = text.decode('utf-8', 'surrogateescape') 194 expected = ascii(decoded).encode('ascii') + b'\n' 195 196 env = os.environ.copy() 197 # C locale gives ASCII locale encoding, but Python uses UTF-8 198 # to parse the command line arguments on Mac OS X and Android. 199 env['LC_ALL'] = 'C' 200 201 p = subprocess.Popen( 202 (sys.executable, "-c", "import sys; print(ascii(sys.argv[1]))", text), 203 stdout=subprocess.PIPE, 204 env=env) 205 stdout, stderr = p.communicate() 206 self.assertEqual(stdout, expected) 207 self.assertEqual(p.returncode, 0) 208 209 # test valid utf-8 210 text = 'e:\xe9, euro:\u20ac, non-bmp:\U0010ffff'.encode('utf-8') 211 check_output(text) 212 213 # test invalid utf-8 214 text = ( 215 b'\xff' # invalid byte 216 b'\xc3\xa9' # valid utf-8 character 217 b'\xc3\xff' # invalid byte sequence 218 b'\xed\xa0\x80' # lone surrogate character (invalid) 219 ) 220 check_output(text) 221 222 def test_unbuffered_output(self): 223 # Test expected operation of the '-u' switch 224 for stream in ('stdout', 'stderr'): 225 # Binary is unbuffered 226 code = ("import os, sys; sys.%s.buffer.write(b'x'); os._exit(0)" 227 % stream) 228 rc, out, err = assert_python_ok('-u', '-c', code) 229 data = err if stream == 'stderr' else out 230 self.assertEqual(data, b'x', "binary %s not unbuffered" % stream) 231 # Text is unbuffered 232 code = ("import os, sys; sys.%s.write('x'); os._exit(0)" 233 % stream) 234 rc, out, err = assert_python_ok('-u', '-c', code) 235 data = err if stream == 'stderr' else out 236 self.assertEqual(data, b'x', "text %s not unbuffered" % stream) 237 238 def test_unbuffered_input(self): 239 # sys.stdin still works with '-u' 240 code = ("import sys; sys.stdout.write(sys.stdin.read(1))") 241 p = spawn_python('-u', '-c', code) 242 p.stdin.write(b'x') 243 p.stdin.flush() 244 data, rc = _kill_python_and_exit_code(p) 245 self.assertEqual(rc, 0) 246 self.assertTrue(data.startswith(b'x'), data) 247 248 def test_large_PYTHONPATH(self): 249 path1 = "ABCDE" * 100 250 path2 = "FGHIJ" * 100 251 path = path1 + os.pathsep + path2 252 253 code = """if 1: 254 import sys 255 path = ":".join(sys.path) 256 path = path.encode("ascii", "backslashreplace") 257 sys.stdout.buffer.write(path)""" 258 rc, out, err = assert_python_ok('-S', '-c', code, 259 PYTHONPATH=path) 260 self.assertIn(path1.encode('ascii'), out) 261 self.assertIn(path2.encode('ascii'), out) 262 263 def test_empty_PYTHONPATH_issue16309(self): 264 # On Posix, it is documented that setting PATH to the 265 # empty string is equivalent to not setting PATH at all, 266 # which is an exception to the rule that in a string like 267 # "/bin::/usr/bin" the empty string in the middle gets 268 # interpreted as '.' 269 code = """if 1: 270 import sys 271 path = ":".join(sys.path) 272 path = path.encode("ascii", "backslashreplace") 273 sys.stdout.buffer.write(path)""" 274 rc1, out1, err1 = assert_python_ok('-c', code, PYTHONPATH="") 275 rc2, out2, err2 = assert_python_ok('-c', code, __isolated=False) 276 # regarding to Posix specification, outputs should be equal 277 # for empty and unset PYTHONPATH 278 self.assertEqual(out1, out2) 279 280 def test_displayhook_unencodable(self): 281 for encoding in ('ascii', 'latin-1', 'utf-8'): 282 env = os.environ.copy() 283 env['PYTHONIOENCODING'] = encoding 284 p = subprocess.Popen( 285 [sys.executable, '-i'], 286 stdin=subprocess.PIPE, 287 stdout=subprocess.PIPE, 288 stderr=subprocess.STDOUT, 289 env=env) 290 # non-ascii, surrogate, non-BMP printable, non-BMP unprintable 291 text = "a=\xe9 b=\uDC80 c=\U00010000 d=\U0010FFFF" 292 p.stdin.write(ascii(text).encode('ascii') + b"\n") 293 p.stdin.write(b'exit()\n') 294 data = kill_python(p) 295 escaped = repr(text).encode(encoding, 'backslashreplace') 296 self.assertIn(escaped, data) 297 298 def check_input(self, code, expected): 299 with tempfile.NamedTemporaryFile("wb+") as stdin: 300 sep = os.linesep.encode('ASCII') 301 stdin.write(sep.join((b'abc', b'def'))) 302 stdin.flush() 303 stdin.seek(0) 304 with subprocess.Popen( 305 (sys.executable, "-c", code), 306 stdin=stdin, stdout=subprocess.PIPE) as proc: 307 stdout, stderr = proc.communicate() 308 self.assertEqual(stdout.rstrip(), expected) 309 310 def test_stdin_readline(self): 311 # Issue #11272: check that sys.stdin.readline() replaces '\r\n' by '\n' 312 # on Windows (sys.stdin is opened in binary mode) 313 self.check_input( 314 "import sys; print(repr(sys.stdin.readline()))", 315 b"'abc\\n'") 316 317 def test_builtin_input(self): 318 # Issue #11272: check that input() strips newlines ('\n' or '\r\n') 319 self.check_input( 320 "print(repr(input()))", 321 b"'abc'") 322 323 def test_output_newline(self): 324 # Issue 13119 Newline for print() should be \r\n on Windows. 325 code = """if 1: 326 import sys 327 print(1) 328 print(2) 329 print(3, file=sys.stderr) 330 print(4, file=sys.stderr)""" 331 rc, out, err = assert_python_ok('-c', code) 332 333 if sys.platform == 'win32': 334 self.assertEqual(b'1\r\n2\r\n', out) 335 self.assertEqual(b'3\r\n4', err) 336 else: 337 self.assertEqual(b'1\n2\n', out) 338 self.assertEqual(b'3\n4', err) 339 340 def test_unmached_quote(self): 341 # Issue #10206: python program starting with unmatched quote 342 # spewed spaces to stdout 343 rc, out, err = assert_python_failure('-c', "'") 344 self.assertRegex(err.decode('ascii', 'ignore'), 'SyntaxError') 345 self.assertEqual(b'', out) 346 347 def test_stdout_flush_at_shutdown(self): 348 # Issue #5319: if stdout.flush() fails at shutdown, an error should 349 # be printed out. 350 code = """if 1: 351 import os, sys, test.support 352 test.support.SuppressCrashReport().__enter__() 353 sys.stdout.write('x') 354 os.close(sys.stdout.fileno())""" 355 rc, out, err = assert_python_failure('-c', code) 356 self.assertEqual(b'', out) 357 self.assertEqual(120, rc) 358 self.assertRegex(err.decode('ascii', 'ignore'), 359 'Exception ignored in.*\nOSError: .*') 360 361 def test_closed_stdout(self): 362 # Issue #13444: if stdout has been explicitly closed, we should 363 # not attempt to flush it at shutdown. 364 code = "import sys; sys.stdout.close()" 365 rc, out, err = assert_python_ok('-c', code) 366 self.assertEqual(b'', err) 367 368 # Issue #7111: Python should work without standard streams 369 370 @unittest.skipIf(os.name != 'posix', "test needs POSIX semantics") 371 @unittest.skipIf(sys.platform == "vxworks", 372 "test needs preexec support in subprocess.Popen") 373 def _test_no_stdio(self, streams): 374 code = """if 1: 375 import os, sys 376 for i, s in enumerate({streams}): 377 if getattr(sys, s) is not None: 378 os._exit(i + 1) 379 os._exit(42)""".format(streams=streams) 380 def preexec(): 381 if 'stdin' in streams: 382 os.close(0) 383 if 'stdout' in streams: 384 os.close(1) 385 if 'stderr' in streams: 386 os.close(2) 387 p = subprocess.Popen( 388 [sys.executable, "-E", "-c", code], 389 stdin=subprocess.PIPE, 390 stdout=subprocess.PIPE, 391 stderr=subprocess.PIPE, 392 preexec_fn=preexec) 393 out, err = p.communicate() 394 self.assertEqual(support.strip_python_stderr(err), b'') 395 self.assertEqual(p.returncode, 42) 396 397 def test_no_stdin(self): 398 self._test_no_stdio(['stdin']) 399 400 def test_no_stdout(self): 401 self._test_no_stdio(['stdout']) 402 403 def test_no_stderr(self): 404 self._test_no_stdio(['stderr']) 405 406 def test_no_std_streams(self): 407 self._test_no_stdio(['stdin', 'stdout', 'stderr']) 408 409 def test_hash_randomization(self): 410 # Verify that -R enables hash randomization: 411 self.verify_valid_flag('-R') 412 hashes = [] 413 if os.environ.get('PYTHONHASHSEED', 'random') != 'random': 414 env = dict(os.environ) # copy 415 # We need to test that it is enabled by default without 416 # the environment variable enabling it for us. 417 del env['PYTHONHASHSEED'] 418 env['__cleanenv'] = '1' # consumed by assert_python_ok() 419 else: 420 env = {} 421 for i in range(3): 422 code = 'print(hash("spam"))' 423 rc, out, err = assert_python_ok('-c', code, **env) 424 self.assertEqual(rc, 0) 425 hashes.append(out) 426 hashes = sorted(set(hashes)) # uniq 427 # Rare chance of failure due to 3 random seeds honestly being equal. 428 self.assertGreater(len(hashes), 1, 429 msg='3 runs produced an identical random hash ' 430 ' for "spam": {}'.format(hashes)) 431 432 # Verify that sys.flags contains hash_randomization 433 code = 'import sys; print("random is", sys.flags.hash_randomization)' 434 rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='') 435 self.assertIn(b'random is 1', out) 436 437 rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='random') 438 self.assertIn(b'random is 1', out) 439 440 rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='0') 441 self.assertIn(b'random is 0', out) 442 443 rc, out, err = assert_python_ok('-R', '-c', code, PYTHONHASHSEED='0') 444 self.assertIn(b'random is 1', out) 445 446 def test_del___main__(self): 447 # Issue #15001: PyRun_SimpleFileExFlags() did crash because it kept a 448 # borrowed reference to the dict of __main__ module and later modify 449 # the dict whereas the module was destroyed 450 filename = support.TESTFN 451 self.addCleanup(support.unlink, filename) 452 with open(filename, "w") as script: 453 print("import sys", file=script) 454 print("del sys.modules['__main__']", file=script) 455 assert_python_ok(filename) 456 457 def test_unknown_options(self): 458 rc, out, err = assert_python_failure('-E', '-z') 459 self.assertIn(b'Unknown option: -z', err) 460 self.assertEqual(err.splitlines().count(b'Unknown option: -z'), 1) 461 self.assertEqual(b'', out) 462 # Add "without='-E'" to prevent _assert_python to append -E 463 # to env_vars and change the output of stderr 464 rc, out, err = assert_python_failure('-z', without='-E') 465 self.assertIn(b'Unknown option: -z', err) 466 self.assertEqual(err.splitlines().count(b'Unknown option: -z'), 1) 467 self.assertEqual(b'', out) 468 rc, out, err = assert_python_failure('-a', '-z', without='-E') 469 self.assertIn(b'Unknown option: -a', err) 470 # only the first unknown option is reported 471 self.assertNotIn(b'Unknown option: -z', err) 472 self.assertEqual(err.splitlines().count(b'Unknown option: -a'), 1) 473 self.assertEqual(b'', out) 474 475 @unittest.skipIf(interpreter_requires_environment(), 476 'Cannot run -I tests when PYTHON env vars are required.') 477 def test_isolatedmode(self): 478 self.verify_valid_flag('-I') 479 self.verify_valid_flag('-IEs') 480 rc, out, err = assert_python_ok('-I', '-c', 481 'from sys import flags as f; ' 482 'print(f.no_user_site, f.ignore_environment, f.isolated)', 483 # dummyvar to prevent extraneous -E 484 dummyvar="") 485 self.assertEqual(out.strip(), b'1 1 1') 486 with support.temp_cwd() as tmpdir: 487 fake = os.path.join(tmpdir, "uuid.py") 488 main = os.path.join(tmpdir, "main.py") 489 with open(fake, "w") as f: 490 f.write("raise RuntimeError('isolated mode test')\n") 491 with open(main, "w") as f: 492 f.write("import uuid\n") 493 f.write("print('ok')\n") 494 self.assertRaises(subprocess.CalledProcessError, 495 subprocess.check_output, 496 [sys.executable, main], cwd=tmpdir, 497 stderr=subprocess.DEVNULL) 498 out = subprocess.check_output([sys.executable, "-I", main], 499 cwd=tmpdir) 500 self.assertEqual(out.strip(), b"ok") 501 502 def test_sys_flags_set(self): 503 # Issue 31845: a startup refactoring broke reading flags from env vars 504 for value, expected in (("", 0), ("1", 1), ("text", 1), ("2", 2)): 505 env_vars = dict( 506 PYTHONDEBUG=value, 507 PYTHONOPTIMIZE=value, 508 PYTHONDONTWRITEBYTECODE=value, 509 PYTHONVERBOSE=value, 510 ) 511 dont_write_bytecode = int(bool(value)) 512 code = ( 513 "import sys; " 514 "sys.stderr.write(str(sys.flags)); " 515 f"""sys.exit(not ( 516 sys.flags.debug == sys.flags.optimize == 517 sys.flags.verbose == 518 {expected} 519 and sys.flags.dont_write_bytecode == {dont_write_bytecode} 520 ))""" 521 ) 522 with self.subTest(envar_value=value): 523 assert_python_ok('-c', code, **env_vars) 524 525 def test_set_pycache_prefix(self): 526 # sys.pycache_prefix can be set from either -X pycache_prefix or 527 # PYTHONPYCACHEPREFIX env var, with the former taking precedence. 528 NO_VALUE = object() # `-X pycache_prefix` with no `=PATH` 529 cases = [ 530 # (PYTHONPYCACHEPREFIX, -X pycache_prefix, sys.pycache_prefix) 531 (None, None, None), 532 ('foo', None, 'foo'), 533 (None, 'bar', 'bar'), 534 ('foo', 'bar', 'bar'), 535 ('foo', '', None), 536 ('foo', NO_VALUE, None), 537 ] 538 for envval, opt, expected in cases: 539 exp_clause = "is None" if expected is None else f'== "{expected}"' 540 code = f"import sys; sys.exit(not sys.pycache_prefix {exp_clause})" 541 args = ['-c', code] 542 env = {} if envval is None else {'PYTHONPYCACHEPREFIX': envval} 543 if opt is NO_VALUE: 544 args[:0] = ['-X', 'pycache_prefix'] 545 elif opt is not None: 546 args[:0] = ['-X', f'pycache_prefix={opt}'] 547 with self.subTest(envval=envval, opt=opt): 548 with support.temp_cwd(): 549 assert_python_ok(*args, **env) 550 551 def run_xdev(self, *args, check_exitcode=True, xdev=True): 552 env = dict(os.environ) 553 env.pop('PYTHONWARNINGS', None) 554 env.pop('PYTHONDEVMODE', None) 555 env.pop('PYTHONMALLOC', None) 556 557 if xdev: 558 args = (sys.executable, '-X', 'dev', *args) 559 else: 560 args = (sys.executable, *args) 561 proc = subprocess.run(args, 562 stdout=subprocess.PIPE, 563 stderr=subprocess.STDOUT, 564 universal_newlines=True, 565 env=env) 566 if check_exitcode: 567 self.assertEqual(proc.returncode, 0, proc) 568 return proc.stdout.rstrip() 569 570 def test_xdev(self): 571 # sys.flags.dev_mode 572 code = "import sys; print(sys.flags.dev_mode)" 573 out = self.run_xdev("-c", code, xdev=False) 574 self.assertEqual(out, "False") 575 out = self.run_xdev("-c", code) 576 self.assertEqual(out, "True") 577 578 # Warnings 579 code = ("import warnings; " 580 "print(' '.join('%s::%s' % (f[0], f[2].__name__) " 581 "for f in warnings.filters))") 582 if Py_DEBUG: 583 expected_filters = "default::Warning" 584 else: 585 expected_filters = ("default::Warning " 586 "default::DeprecationWarning " 587 "ignore::DeprecationWarning " 588 "ignore::PendingDeprecationWarning " 589 "ignore::ImportWarning " 590 "ignore::ResourceWarning") 591 592 out = self.run_xdev("-c", code) 593 self.assertEqual(out, expected_filters) 594 595 out = self.run_xdev("-b", "-c", code) 596 self.assertEqual(out, f"default::BytesWarning {expected_filters}") 597 598 out = self.run_xdev("-bb", "-c", code) 599 self.assertEqual(out, f"error::BytesWarning {expected_filters}") 600 601 out = self.run_xdev("-Werror", "-c", code) 602 self.assertEqual(out, f"error::Warning {expected_filters}") 603 604 # Memory allocator debug hooks 605 try: 606 import _testcapi 607 except ImportError: 608 pass 609 else: 610 code = "import _testcapi; print(_testcapi.pymem_getallocatorsname())" 611 with support.SuppressCrashReport(): 612 out = self.run_xdev("-c", code, check_exitcode=False) 613 if support.with_pymalloc(): 614 alloc_name = "pymalloc_debug" 615 else: 616 alloc_name = "malloc_debug" 617 self.assertEqual(out, alloc_name) 618 619 # Faulthandler 620 try: 621 import faulthandler 622 except ImportError: 623 pass 624 else: 625 code = "import faulthandler; print(faulthandler.is_enabled())" 626 out = self.run_xdev("-c", code) 627 self.assertEqual(out, "True") 628 629 def check_warnings_filters(self, cmdline_option, envvar, use_pywarning=False): 630 if use_pywarning: 631 code = ("import sys; from test.support import import_fresh_module; " 632 "warnings = import_fresh_module('warnings', blocked=['_warnings']); ") 633 else: 634 code = "import sys, warnings; " 635 code += ("print(' '.join('%s::%s' % (f[0], f[2].__name__) " 636 "for f in warnings.filters))") 637 args = (sys.executable, '-W', cmdline_option, '-bb', '-c', code) 638 env = dict(os.environ) 639 env.pop('PYTHONDEVMODE', None) 640 env["PYTHONWARNINGS"] = envvar 641 proc = subprocess.run(args, 642 stdout=subprocess.PIPE, 643 stderr=subprocess.STDOUT, 644 universal_newlines=True, 645 env=env) 646 self.assertEqual(proc.returncode, 0, proc) 647 return proc.stdout.rstrip() 648 649 def test_warnings_filter_precedence(self): 650 expected_filters = ("error::BytesWarning " 651 "once::UserWarning " 652 "always::UserWarning") 653 if not Py_DEBUG: 654 expected_filters += (" " 655 "default::DeprecationWarning " 656 "ignore::DeprecationWarning " 657 "ignore::PendingDeprecationWarning " 658 "ignore::ImportWarning " 659 "ignore::ResourceWarning") 660 661 out = self.check_warnings_filters("once::UserWarning", 662 "always::UserWarning") 663 self.assertEqual(out, expected_filters) 664 665 out = self.check_warnings_filters("once::UserWarning", 666 "always::UserWarning", 667 use_pywarning=True) 668 self.assertEqual(out, expected_filters) 669 670 def check_pythonmalloc(self, env_var, name): 671 code = 'import _testcapi; print(_testcapi.pymem_getallocatorsname())' 672 env = dict(os.environ) 673 env.pop('PYTHONDEVMODE', None) 674 if env_var is not None: 675 env['PYTHONMALLOC'] = env_var 676 else: 677 env.pop('PYTHONMALLOC', None) 678 args = (sys.executable, '-c', code) 679 proc = subprocess.run(args, 680 stdout=subprocess.PIPE, 681 stderr=subprocess.STDOUT, 682 universal_newlines=True, 683 env=env) 684 self.assertEqual(proc.stdout.rstrip(), name) 685 self.assertEqual(proc.returncode, 0) 686 687 def test_pythonmalloc(self): 688 # Test the PYTHONMALLOC environment variable 689 pymalloc = support.with_pymalloc() 690 if pymalloc: 691 default_name = 'pymalloc_debug' if Py_DEBUG else 'pymalloc' 692 default_name_debug = 'pymalloc_debug' 693 else: 694 default_name = 'malloc_debug' if Py_DEBUG else 'malloc' 695 default_name_debug = 'malloc_debug' 696 697 tests = [ 698 (None, default_name), 699 ('debug', default_name_debug), 700 ('malloc', 'malloc'), 701 ('malloc_debug', 'malloc_debug'), 702 ] 703 if pymalloc: 704 tests.extend(( 705 ('pymalloc', 'pymalloc'), 706 ('pymalloc_debug', 'pymalloc_debug'), 707 )) 708 709 for env_var, name in tests: 710 with self.subTest(env_var=env_var, name=name): 711 self.check_pythonmalloc(env_var, name) 712 713 def test_pythondevmode_env(self): 714 # Test the PYTHONDEVMODE environment variable 715 code = "import sys; print(sys.flags.dev_mode)" 716 env = dict(os.environ) 717 env.pop('PYTHONDEVMODE', None) 718 args = (sys.executable, '-c', code) 719 720 proc = subprocess.run(args, stdout=subprocess.PIPE, 721 universal_newlines=True, env=env) 722 self.assertEqual(proc.stdout.rstrip(), 'False') 723 self.assertEqual(proc.returncode, 0, proc) 724 725 env['PYTHONDEVMODE'] = '1' 726 proc = subprocess.run(args, stdout=subprocess.PIPE, 727 universal_newlines=True, env=env) 728 self.assertEqual(proc.stdout.rstrip(), 'True') 729 self.assertEqual(proc.returncode, 0, proc) 730 731 @unittest.skipUnless(sys.platform == 'win32', 732 'bpo-32457 only applies on Windows') 733 def test_argv0_normalization(self): 734 args = sys.executable, '-c', 'print(0)' 735 prefix, exe = os.path.split(sys.executable) 736 executable = prefix + '\\.\\.\\.\\' + exe 737 738 proc = subprocess.run(args, stdout=subprocess.PIPE, 739 executable=executable) 740 self.assertEqual(proc.returncode, 0, proc) 741 self.assertEqual(proc.stdout.strip(), b'0') 742 743 def test_parsing_error(self): 744 args = [sys.executable, '-I', '--unknown-option'] 745 proc = subprocess.run(args, 746 stdout=subprocess.PIPE, 747 stderr=subprocess.PIPE, 748 text=True) 749 err_msg = "unknown option --unknown-option\nusage: " 750 self.assertTrue(proc.stderr.startswith(err_msg), proc.stderr) 751 self.assertNotEqual(proc.returncode, 0) 752 753 754 def test_int_max_str_digits(self): 755 code = "import sys; print(sys.flags.int_max_str_digits, sys.get_int_max_str_digits())" 756 757 assert_python_failure('-X', 'int_max_str_digits', '-c', code) 758 assert_python_failure('-X', 'int_max_str_digits=foo', '-c', code) 759 assert_python_failure('-X', 'int_max_str_digits=100', '-c', code) 760 761 assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='foo') 762 assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='100') 763 764 def res2int(res): 765 out = res.out.strip().decode("utf-8") 766 return tuple(int(i) for i in out.split()) 767 768 res = assert_python_ok('-c', code) 769 self.assertEqual(res2int(res), (-1, sys.get_int_max_str_digits())) 770 res = assert_python_ok('-X', 'int_max_str_digits=0', '-c', code) 771 self.assertEqual(res2int(res), (0, 0)) 772 res = assert_python_ok('-X', 'int_max_str_digits=4000', '-c', code) 773 self.assertEqual(res2int(res), (4000, 4000)) 774 res = assert_python_ok('-X', 'int_max_str_digits=100000', '-c', code) 775 self.assertEqual(res2int(res), (100000, 100000)) 776 777 res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='0') 778 self.assertEqual(res2int(res), (0, 0)) 779 res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='4000') 780 self.assertEqual(res2int(res), (4000, 4000)) 781 res = assert_python_ok( 782 '-X', 'int_max_str_digits=6000', '-c', code, 783 PYTHONINTMAXSTRDIGITS='4000' 784 ) 785 self.assertEqual(res2int(res), (6000, 6000)) 786 787 788@unittest.skipIf(interpreter_requires_environment(), 789 'Cannot run -I tests when PYTHON env vars are required.') 790class IgnoreEnvironmentTest(unittest.TestCase): 791 792 def run_ignoring_vars(self, predicate, **env_vars): 793 # Runs a subprocess with -E set, even though we're passing 794 # specific environment variables 795 # Logical inversion to match predicate check to a zero return 796 # code indicating success 797 code = "import sys; sys.stderr.write(str(sys.flags)); sys.exit(not ({}))".format(predicate) 798 return assert_python_ok('-E', '-c', code, **env_vars) 799 800 def test_ignore_PYTHONPATH(self): 801 path = "should_be_ignored" 802 self.run_ignoring_vars("'{}' not in sys.path".format(path), 803 PYTHONPATH=path) 804 805 def test_ignore_PYTHONHASHSEED(self): 806 self.run_ignoring_vars("sys.flags.hash_randomization == 1", 807 PYTHONHASHSEED="0") 808 809 def test_sys_flags_not_set(self): 810 # Issue 31845: a startup refactoring broke reading flags from env vars 811 expected_outcome = """ 812 (sys.flags.debug == sys.flags.optimize == 813 sys.flags.dont_write_bytecode == sys.flags.verbose == 0) 814 """ 815 self.run_ignoring_vars( 816 expected_outcome, 817 PYTHONDEBUG="1", 818 PYTHONOPTIMIZE="1", 819 PYTHONDONTWRITEBYTECODE="1", 820 PYTHONVERBOSE="1", 821 ) 822 823 824def test_main(): 825 support.run_unittest(CmdLineTest, IgnoreEnvironmentTest) 826 support.reap_children() 827 828if __name__ == "__main__": 829 test_main() 830