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