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