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