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 import os_helper 13from test.support import force_not_colorized 14from test.support.script_helper import ( 15 spawn_python, kill_python, assert_python_ok, assert_python_failure, 16 interpreter_requires_environment 17) 18 19if not support.has_subprocess_support: 20 raise unittest.SkipTest("test module requires subprocess") 21 22 23# XXX (ncoghlan): Move to script_helper and make consistent with run_python 24def _kill_python_and_exit_code(p): 25 data = kill_python(p) 26 returncode = p.wait() 27 return data, returncode 28 29 30class CmdLineTest(unittest.TestCase): 31 def test_directories(self): 32 assert_python_failure('.') 33 assert_python_failure('< .') 34 35 def verify_valid_flag(self, cmd_line): 36 rc, out, err = assert_python_ok(cmd_line) 37 self.assertTrue(out == b'' or out.endswith(b'\n')) 38 self.assertNotIn(b'Traceback', out) 39 self.assertNotIn(b'Traceback', err) 40 return out 41 42 @support.cpython_only 43 def test_help(self): 44 self.verify_valid_flag('-h') 45 self.verify_valid_flag('-?') 46 out = self.verify_valid_flag('--help') 47 lines = out.splitlines() 48 self.assertIn(b'usage', lines[0]) 49 self.assertNotIn(b'PYTHONHOME', out) 50 self.assertNotIn(b'-X dev', out) 51 self.assertLess(len(lines), 50) 52 53 @support.cpython_only 54 def test_help_env(self): 55 out = self.verify_valid_flag('--help-env') 56 self.assertIn(b'PYTHONHOME', out) 57 58 @support.cpython_only 59 def test_help_xoptions(self): 60 out = self.verify_valid_flag('--help-xoptions') 61 self.assertIn(b'-X dev', out) 62 63 @support.cpython_only 64 def test_help_all(self): 65 out = self.verify_valid_flag('--help-all') 66 lines = out.splitlines() 67 self.assertIn(b'usage', lines[0]) 68 self.assertIn(b'PYTHONHOME', out) 69 self.assertIn(b'-X dev', out) 70 71 # The first line contains the program name, 72 # but the rest should be ASCII-only 73 b''.join(lines[1:]).decode('ascii') 74 75 def test_optimize(self): 76 self.verify_valid_flag('-O') 77 self.verify_valid_flag('-OO') 78 79 def test_site_flag(self): 80 self.verify_valid_flag('-S') 81 82 @support.cpython_only 83 def test_version(self): 84 version = ('Python %d.%d' % sys.version_info[:2]).encode("ascii") 85 for switch in '-V', '--version', '-VV': 86 rc, out, err = assert_python_ok(switch) 87 self.assertFalse(err.startswith(version)) 88 self.assertTrue(out.startswith(version)) 89 90 def test_verbose(self): 91 # -v causes imports to write to stderr. If the write to 92 # stderr itself causes an import to happen (for the output 93 # codec), a recursion loop can occur. 94 rc, out, err = assert_python_ok('-v') 95 self.assertNotIn(b'stack overflow', err) 96 rc, out, err = assert_python_ok('-vv') 97 self.assertNotIn(b'stack overflow', err) 98 99 @unittest.skipIf(interpreter_requires_environment(), 100 'Cannot run -E tests when PYTHON env vars are required.') 101 def test_xoptions(self): 102 def get_xoptions(*args): 103 # use subprocess module directly because test.support.script_helper adds 104 # "-X faulthandler" to the command line 105 args = (sys.executable, '-E') + args 106 args += ('-c', 'import sys; print(sys._xoptions)') 107 out = subprocess.check_output(args) 108 opts = eval(out.splitlines()[0]) 109 return opts 110 111 opts = get_xoptions() 112 self.assertEqual(opts, {}) 113 114 opts = get_xoptions('-Xa', '-Xb=c,d=e') 115 self.assertEqual(opts, {'a': True, 'b': 'c,d=e'}) 116 117 def test_showrefcount(self): 118 def run_python(*args): 119 # this is similar to assert_python_ok but doesn't strip 120 # the refcount from stderr. It can be replaced once 121 # assert_python_ok stops doing that. 122 cmd = [sys.executable] 123 cmd.extend(args) 124 PIPE = subprocess.PIPE 125 p = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) 126 out, err = p.communicate() 127 p.stdout.close() 128 p.stderr.close() 129 rc = p.returncode 130 self.assertEqual(rc, 0) 131 return rc, out, err 132 code = 'import sys; print(sys._xoptions)' 133 # normally the refcount is hidden 134 rc, out, err = run_python('-c', code) 135 self.assertEqual(out.rstrip(), b'{}') 136 self.assertEqual(err, b'') 137 # "-X showrefcount" shows the refcount, but only in debug builds 138 rc, out, err = run_python('-I', '-X', 'showrefcount', '-c', code) 139 self.assertEqual(out.rstrip(), b"{'showrefcount': True}") 140 if support.Py_DEBUG: 141 # bpo-46417: Tolerate negative reference count which can occur 142 # because of bugs in C extensions. This test is only about checking 143 # the showrefcount feature. 144 self.assertRegex(err, br'^\[-?\d+ refs, \d+ blocks\]') 145 else: 146 self.assertEqual(err, b'') 147 148 @support.cpython_only 149 def test_xoption_frozen_modules(self): 150 tests = { 151 ('=on', 'FrozenImporter'), 152 ('=off', 'SourceFileLoader'), 153 ('=', 'FrozenImporter'), 154 ('', 'FrozenImporter'), 155 } 156 for raw, expected in tests: 157 cmd = ['-X', f'frozen_modules{raw}', 158 '-c', 'import os; print(os.__spec__.loader, end="")'] 159 with self.subTest(raw): 160 res = assert_python_ok(*cmd) 161 self.assertRegex(res.out.decode('utf-8'), expected) 162 163 @support.cpython_only 164 def test_env_var_frozen_modules(self): 165 tests = { 166 ('on', 'FrozenImporter'), 167 ('off', 'SourceFileLoader'), 168 } 169 for raw, expected in tests: 170 cmd = ['-c', 'import os; print(os.__spec__.loader, end="")'] 171 with self.subTest(raw): 172 res = assert_python_ok(*cmd, PYTHON_FROZEN_MODULES=raw) 173 self.assertRegex(res.out.decode('utf-8'), expected) 174 175 def test_run_module(self): 176 # Test expected operation of the '-m' switch 177 # Switch needs an argument 178 assert_python_failure('-m') 179 # Check we get an error for a nonexistent module 180 assert_python_failure('-m', 'fnord43520xyz') 181 # Check the runpy module also gives an error for 182 # a nonexistent module 183 assert_python_failure('-m', 'runpy', 'fnord43520xyz') 184 # All good if module is located and run successfully 185 assert_python_ok('-m', 'timeit', '-n', '1') 186 187 def test_run_module_bug1764407(self): 188 # -m and -i need to play well together 189 # Runs the timeit module and checks the __main__ 190 # namespace has been populated appropriately 191 p = spawn_python('-i', '-m', 'timeit', '-n', '1') 192 p.stdin.write(b'Timer\n') 193 p.stdin.write(b'exit()\n') 194 data = kill_python(p) 195 self.assertTrue(data.find(b'1 loop') != -1) 196 self.assertTrue(data.find(b'__main__.Timer') != -1) 197 198 def test_relativedir_bug46421(self): 199 # Test `python -m unittest` with a relative directory beginning with ./ 200 # Note: We have to switch to the project's top module's directory, as per 201 # the python unittest wiki. We will switch back when we are done. 202 projectlibpath = os.path.dirname(__file__).removesuffix("test") 203 with os_helper.change_cwd(projectlibpath): 204 # Testing with and without ./ 205 assert_python_ok('-m', 'unittest', "test/test_longexp.py") 206 assert_python_ok('-m', 'unittest', "./test/test_longexp.py") 207 208 def test_run_code(self): 209 # Test expected operation of the '-c' switch 210 # Switch needs an argument 211 assert_python_failure('-c') 212 # Check we get an error for an uncaught exception 213 assert_python_failure('-c', 'raise Exception') 214 # All good if execution is successful 215 assert_python_ok('-c', 'pass') 216 217 @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII') 218 def test_non_ascii(self): 219 # Test handling of non-ascii data 220 command = ("assert(ord(%r) == %s)" 221 % (os_helper.FS_NONASCII, ord(os_helper.FS_NONASCII))) 222 assert_python_ok('-c', command) 223 224 @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII') 225 def test_coding(self): 226 # bpo-32381: the -c command ignores the coding cookie 227 ch = os_helper.FS_NONASCII 228 cmd = f"# coding: latin1\nprint(ascii('{ch}'))" 229 res = assert_python_ok('-c', cmd) 230 self.assertEqual(res.out.rstrip(), ascii(ch).encode('ascii')) 231 232 # On Windows, pass bytes to subprocess doesn't test how Python decodes the 233 # command line, but how subprocess does decode bytes to unicode. Python 234 # doesn't decode the command line because Windows provides directly the 235 # arguments as unicode (using wmain() instead of main()). 236 @unittest.skipIf(sys.platform == 'win32', 237 'Windows has a native unicode API') 238 def test_undecodable_code(self): 239 undecodable = b"\xff" 240 env = os.environ.copy() 241 # Use C locale to get ascii for the locale encoding 242 env['LC_ALL'] = 'C' 243 env['PYTHONCOERCECLOCALE'] = '0' 244 code = ( 245 b'import locale; ' 246 b'print(ascii("' + undecodable + b'"), ' 247 b'locale.getencoding())') 248 p = subprocess.Popen( 249 [sys.executable, "-c", code], 250 stdout=subprocess.PIPE, stderr=subprocess.STDOUT, 251 env=env) 252 stdout, stderr = p.communicate() 253 if p.returncode == 1: 254 # _Py_char2wchar() decoded b'\xff' as '\udcff' (b'\xff' is not 255 # decodable from ASCII) and run_command() failed on 256 # PyUnicode_AsUTF8String(). This is the expected behaviour on 257 # Linux. 258 pattern = b"Unable to decode the command from the command line:" 259 elif p.returncode == 0: 260 # _Py_char2wchar() decoded b'\xff' as '\xff' even if the locale is 261 # C and the locale encoding is ASCII. It occurs on FreeBSD, Solaris 262 # and Mac OS X. 263 pattern = b"'\\xff' " 264 # The output is followed by the encoding name, an alias to ASCII. 265 # Examples: "US-ASCII" or "646" (ISO 646, on Solaris). 266 else: 267 raise AssertionError("Unknown exit code: %s, output=%a" % (p.returncode, stdout)) 268 if not stdout.startswith(pattern): 269 raise AssertionError("%a doesn't start with %a" % (stdout, pattern)) 270 271 @unittest.skipIf(sys.platform == 'win32', 272 'Windows has a native unicode API') 273 def test_invalid_utf8_arg(self): 274 # bpo-35883: Py_DecodeLocale() must escape b'\xfd\xbf\xbf\xbb\xba\xba' 275 # byte sequence with surrogateescape rather than decoding it as the 276 # U+7fffbeba character which is outside the [U+0000; U+10ffff] range of 277 # Python Unicode characters. 278 # 279 # Test with default config, in the C locale, in the Python UTF-8 Mode. 280 code = 'import sys, os; s=os.fsencode(sys.argv[1]); print(ascii(s))' 281 282 def run_default(arg): 283 cmd = [sys.executable, '-c', code, arg] 284 return subprocess.run(cmd, stdout=subprocess.PIPE, text=True) 285 286 def run_c_locale(arg): 287 cmd = [sys.executable, '-c', code, arg] 288 env = dict(os.environ) 289 env['LC_ALL'] = 'C' 290 return subprocess.run(cmd, stdout=subprocess.PIPE, 291 text=True, env=env) 292 293 def run_utf8_mode(arg): 294 cmd = [sys.executable, '-X', 'utf8', '-c', code, arg] 295 return subprocess.run(cmd, stdout=subprocess.PIPE, text=True) 296 297 valid_utf8 = 'e:\xe9, euro:\u20ac, non-bmp:\U0010ffff'.encode('utf-8') 298 # invalid UTF-8 byte sequences with a valid UTF-8 sequence 299 # in the middle. 300 invalid_utf8 = ( 301 b'\xff' # invalid byte 302 b'\xc3\xff' # invalid byte sequence 303 b'\xc3\xa9' # valid utf-8: U+00E9 character 304 b'\xed\xa0\x80' # lone surrogate character (invalid) 305 b'\xfd\xbf\xbf\xbb\xba\xba' # character outside [U+0000; U+10ffff] 306 ) 307 test_args = [valid_utf8, invalid_utf8] 308 309 for run_cmd in (run_default, run_c_locale, run_utf8_mode): 310 with self.subTest(run_cmd=run_cmd): 311 for arg in test_args: 312 proc = run_cmd(arg) 313 self.assertEqual(proc.stdout.rstrip(), ascii(arg)) 314 315 @unittest.skipUnless((sys.platform == 'darwin' or 316 support.is_android), 'test specific to Mac OS X and Android') 317 def test_osx_android_utf8(self): 318 text = 'e:\xe9, euro:\u20ac, non-bmp:\U0010ffff'.encode('utf-8') 319 code = "import sys; print(ascii(sys.argv[1]))" 320 321 decoded = text.decode('utf-8', 'surrogateescape') 322 expected = ascii(decoded).encode('ascii') + b'\n' 323 324 env = os.environ.copy() 325 # C locale gives ASCII locale encoding, but Python uses UTF-8 326 # to parse the command line arguments on Mac OS X and Android. 327 env['LC_ALL'] = 'C' 328 329 p = subprocess.Popen( 330 (sys.executable, "-c", code, text), 331 stdout=subprocess.PIPE, 332 env=env) 333 stdout, stderr = p.communicate() 334 self.assertEqual(stdout, expected) 335 self.assertEqual(p.returncode, 0) 336 337 def test_non_interactive_output_buffering(self): 338 code = textwrap.dedent(""" 339 import sys 340 out = sys.stdout 341 print(out.isatty(), out.write_through, out.line_buffering) 342 err = sys.stderr 343 print(err.isatty(), err.write_through, err.line_buffering) 344 """) 345 args = [sys.executable, '-c', code] 346 proc = subprocess.run(args, stdout=subprocess.PIPE, 347 stderr=subprocess.PIPE, text=True, check=True) 348 self.assertEqual(proc.stdout, 349 'False False False\n' 350 'False False True\n') 351 352 def test_unbuffered_output(self): 353 # Test expected operation of the '-u' switch 354 for stream in ('stdout', 'stderr'): 355 # Binary is unbuffered 356 code = ("import os, sys; sys.%s.buffer.write(b'x'); os._exit(0)" 357 % stream) 358 rc, out, err = assert_python_ok('-u', '-c', code) 359 data = err if stream == 'stderr' else out 360 self.assertEqual(data, b'x', "binary %s not unbuffered" % stream) 361 # Text is unbuffered 362 code = ("import os, sys; sys.%s.write('x'); os._exit(0)" 363 % stream) 364 rc, out, err = assert_python_ok('-u', '-c', code) 365 data = err if stream == 'stderr' else out 366 self.assertEqual(data, b'x', "text %s not unbuffered" % stream) 367 368 def test_unbuffered_input(self): 369 # sys.stdin still works with '-u' 370 code = ("import sys; sys.stdout.write(sys.stdin.read(1))") 371 p = spawn_python('-u', '-c', code) 372 p.stdin.write(b'x') 373 p.stdin.flush() 374 data, rc = _kill_python_and_exit_code(p) 375 self.assertEqual(rc, 0) 376 self.assertTrue(data.startswith(b'x'), data) 377 378 def test_large_PYTHONPATH(self): 379 path1 = "ABCDE" * 100 380 path2 = "FGHIJ" * 100 381 path = path1 + os.pathsep + path2 382 383 code = """if 1: 384 import sys 385 path = ":".join(sys.path) 386 path = path.encode("ascii", "backslashreplace") 387 sys.stdout.buffer.write(path)""" 388 rc, out, err = assert_python_ok('-S', '-c', code, 389 PYTHONPATH=path) 390 self.assertIn(path1.encode('ascii'), out) 391 self.assertIn(path2.encode('ascii'), out) 392 393 @unittest.skipIf(sys.flags.safe_path, 394 'PYTHONSAFEPATH changes default sys.path') 395 def test_empty_PYTHONPATH_issue16309(self): 396 # On Posix, it is documented that setting PATH to the 397 # empty string is equivalent to not setting PATH at all, 398 # which is an exception to the rule that in a string like 399 # "/bin::/usr/bin" the empty string in the middle gets 400 # interpreted as '.' 401 code = """if 1: 402 import sys 403 path = ":".join(sys.path) 404 path = path.encode("ascii", "backslashreplace") 405 sys.stdout.buffer.write(path)""" 406 rc1, out1, err1 = assert_python_ok('-c', code, PYTHONPATH="") 407 rc2, out2, err2 = assert_python_ok('-c', code, __isolated=False) 408 # regarding to Posix specification, outputs should be equal 409 # for empty and unset PYTHONPATH 410 self.assertEqual(out1, out2) 411 412 def test_displayhook_unencodable(self): 413 for encoding in ('ascii', 'latin-1', 'utf-8'): 414 env = os.environ.copy() 415 env['PYTHONIOENCODING'] = encoding 416 p = subprocess.Popen( 417 [sys.executable, '-i'], 418 stdin=subprocess.PIPE, 419 stdout=subprocess.PIPE, 420 stderr=subprocess.STDOUT, 421 env=env) 422 # non-ascii, surrogate, non-BMP printable, non-BMP unprintable 423 text = "a=\xe9 b=\uDC80 c=\U00010000 d=\U0010FFFF" 424 p.stdin.write(ascii(text).encode('ascii') + b"\n") 425 p.stdin.write(b'exit()\n') 426 data = kill_python(p) 427 escaped = repr(text).encode(encoding, 'backslashreplace') 428 self.assertIn(escaped, data) 429 430 def check_input(self, code, expected): 431 with tempfile.NamedTemporaryFile("wb+") as stdin: 432 sep = os.linesep.encode('ASCII') 433 stdin.write(sep.join((b'abc', b'def'))) 434 stdin.flush() 435 stdin.seek(0) 436 with subprocess.Popen( 437 (sys.executable, "-c", code), 438 stdin=stdin, stdout=subprocess.PIPE) as proc: 439 stdout, stderr = proc.communicate() 440 self.assertEqual(stdout.rstrip(), expected) 441 442 def test_stdin_readline(self): 443 # Issue #11272: check that sys.stdin.readline() replaces '\r\n' by '\n' 444 # on Windows (sys.stdin is opened in binary mode) 445 self.check_input( 446 "import sys; print(repr(sys.stdin.readline()))", 447 b"'abc\\n'") 448 449 def test_builtin_input(self): 450 # Issue #11272: check that input() strips newlines ('\n' or '\r\n') 451 self.check_input( 452 "print(repr(input()))", 453 b"'abc'") 454 455 def test_output_newline(self): 456 # Issue 13119 Newline for print() should be \r\n on Windows. 457 code = """if 1: 458 import sys 459 print(1) 460 print(2) 461 print(3, file=sys.stderr) 462 print(4, file=sys.stderr)""" 463 rc, out, err = assert_python_ok('-c', code) 464 465 if sys.platform == 'win32': 466 self.assertEqual(b'1\r\n2\r\n', out) 467 self.assertEqual(b'3\r\n4\r\n', err) 468 else: 469 self.assertEqual(b'1\n2\n', out) 470 self.assertEqual(b'3\n4\n', err) 471 472 def test_unmached_quote(self): 473 # Issue #10206: python program starting with unmatched quote 474 # spewed spaces to stdout 475 rc, out, err = assert_python_failure('-c', "'") 476 self.assertRegex(err.decode('ascii', 'ignore'), 'SyntaxError') 477 self.assertEqual(b'', out) 478 479 def test_stdout_flush_at_shutdown(self): 480 # Issue #5319: if stdout.flush() fails at shutdown, an error should 481 # be printed out. 482 code = """if 1: 483 import os, sys, test.support 484 test.support.SuppressCrashReport().__enter__() 485 sys.stdout.write('x') 486 os.close(sys.stdout.fileno())""" 487 rc, out, err = assert_python_failure('-c', code) 488 self.assertEqual(b'', out) 489 self.assertEqual(120, rc) 490 self.assertIn(b'Exception ignored on flushing sys.stdout:\n' 491 b'OSError: '.replace(b'\n', os.linesep.encode()), 492 err) 493 494 def test_closed_stdout(self): 495 # Issue #13444: if stdout has been explicitly closed, we should 496 # not attempt to flush it at shutdown. 497 code = "import sys; sys.stdout.close()" 498 rc, out, err = assert_python_ok('-c', code) 499 self.assertEqual(b'', err) 500 501 # Issue #7111: Python should work without standard streams 502 503 @unittest.skipIf(os.name != 'posix', "test needs POSIX semantics") 504 @unittest.skipIf(sys.platform == "vxworks", 505 "test needs preexec support in subprocess.Popen") 506 def _test_no_stdio(self, streams): 507 code = """if 1: 508 import os, sys 509 for i, s in enumerate({streams}): 510 if getattr(sys, s) is not None: 511 os._exit(i + 1) 512 os._exit(42)""".format(streams=streams) 513 def preexec(): 514 if 'stdin' in streams: 515 os.close(0) 516 if 'stdout' in streams: 517 os.close(1) 518 if 'stderr' in streams: 519 os.close(2) 520 p = subprocess.Popen( 521 [sys.executable, "-E", "-c", code], 522 stdin=subprocess.PIPE, 523 stdout=subprocess.PIPE, 524 stderr=subprocess.PIPE, 525 preexec_fn=preexec) 526 out, err = p.communicate() 527 self.assertEqual(err, b'') 528 self.assertEqual(p.returncode, 42) 529 530 def test_no_stdin(self): 531 self._test_no_stdio(['stdin']) 532 533 def test_no_stdout(self): 534 self._test_no_stdio(['stdout']) 535 536 def test_no_stderr(self): 537 self._test_no_stdio(['stderr']) 538 539 def test_no_std_streams(self): 540 self._test_no_stdio(['stdin', 'stdout', 'stderr']) 541 542 def test_hash_randomization(self): 543 # Verify that -R enables hash randomization: 544 self.verify_valid_flag('-R') 545 hashes = [] 546 if os.environ.get('PYTHONHASHSEED', 'random') != 'random': 547 env = dict(os.environ) # copy 548 # We need to test that it is enabled by default without 549 # the environment variable enabling it for us. 550 del env['PYTHONHASHSEED'] 551 env['__cleanenv'] = '1' # consumed by assert_python_ok() 552 else: 553 env = {} 554 for i in range(3): 555 code = 'print(hash("spam"))' 556 rc, out, err = assert_python_ok('-c', code, **env) 557 self.assertEqual(rc, 0) 558 hashes.append(out) 559 hashes = sorted(set(hashes)) # uniq 560 # Rare chance of failure due to 3 random seeds honestly being equal. 561 self.assertGreater(len(hashes), 1, 562 msg='3 runs produced an identical random hash ' 563 ' for "spam": {}'.format(hashes)) 564 565 # Verify that sys.flags contains hash_randomization 566 code = 'import sys; print("random is", sys.flags.hash_randomization)' 567 rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='') 568 self.assertIn(b'random is 1', out) 569 570 rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='random') 571 self.assertIn(b'random is 1', out) 572 573 rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='0') 574 self.assertIn(b'random is 0', out) 575 576 rc, out, err = assert_python_ok('-R', '-c', code, PYTHONHASHSEED='0') 577 self.assertIn(b'random is 1', out) 578 579 def test_del___main__(self): 580 # Issue #15001: PyRun_SimpleFileExFlags() did crash because it kept a 581 # borrowed reference to the dict of __main__ module and later modify 582 # the dict whereas the module was destroyed 583 filename = os_helper.TESTFN 584 self.addCleanup(os_helper.unlink, filename) 585 with open(filename, "w", encoding="utf-8") as script: 586 print("import sys", file=script) 587 print("del sys.modules['__main__']", file=script) 588 assert_python_ok(filename) 589 590 @support.cpython_only 591 def test_unknown_options(self): 592 rc, out, err = assert_python_failure('-E', '-z') 593 self.assertIn(b'Unknown option: -z', err) 594 self.assertEqual(err.splitlines().count(b'Unknown option: -z'), 1) 595 self.assertEqual(b'', out) 596 # Add "without='-E'" to prevent _assert_python to append -E 597 # to env_vars and change the output of stderr 598 rc, out, err = assert_python_failure('-z', without='-E') 599 self.assertIn(b'Unknown option: -z', err) 600 self.assertEqual(err.splitlines().count(b'Unknown option: -z'), 1) 601 self.assertEqual(b'', out) 602 rc, out, err = assert_python_failure('-a', '-z', without='-E') 603 self.assertIn(b'Unknown option: -a', err) 604 # only the first unknown option is reported 605 self.assertNotIn(b'Unknown option: -z', err) 606 self.assertEqual(err.splitlines().count(b'Unknown option: -a'), 1) 607 self.assertEqual(b'', out) 608 609 @unittest.skipIf(interpreter_requires_environment(), 610 'Cannot run -I tests when PYTHON env vars are required.') 611 def test_isolatedmode(self): 612 self.verify_valid_flag('-I') 613 self.verify_valid_flag('-IEPs') 614 rc, out, err = assert_python_ok('-I', '-c', 615 'from sys import flags as f; ' 616 'print(f.no_user_site, f.ignore_environment, f.isolated, f.safe_path)', 617 # dummyvar to prevent extraneous -E 618 dummyvar="") 619 self.assertEqual(out.strip(), b'1 1 1 True') 620 with os_helper.temp_cwd() as tmpdir: 621 fake = os.path.join(tmpdir, "uuid.py") 622 main = os.path.join(tmpdir, "main.py") 623 with open(fake, "w", encoding="utf-8") as f: 624 f.write("raise RuntimeError('isolated mode test')\n") 625 with open(main, "w", encoding="utf-8") as f: 626 f.write("import uuid\n") 627 f.write("print('ok')\n") 628 # Use -E to ignore PYTHONSAFEPATH env var 629 self.assertRaises(subprocess.CalledProcessError, 630 subprocess.check_output, 631 [sys.executable, '-E', main], cwd=tmpdir, 632 stderr=subprocess.DEVNULL) 633 out = subprocess.check_output([sys.executable, "-I", main], 634 cwd=tmpdir) 635 self.assertEqual(out.strip(), b"ok") 636 637 def test_sys_flags_set(self): 638 # Issue 31845: a startup refactoring broke reading flags from env vars 639 for value, expected in (("", 0), ("1", 1), ("text", 1), ("2", 2)): 640 env_vars = dict( 641 PYTHONDEBUG=value, 642 PYTHONOPTIMIZE=value, 643 PYTHONDONTWRITEBYTECODE=value, 644 PYTHONVERBOSE=value, 645 ) 646 expected_bool = int(bool(value)) 647 code = ( 648 "import sys; " 649 "sys.stderr.write(str(sys.flags)); " 650 f"""sys.exit(not ( 651 sys.flags.optimize == sys.flags.verbose == {expected} 652 and sys.flags.debug == sys.flags.dont_write_bytecode == {expected_bool} 653 ))""" 654 ) 655 with self.subTest(envar_value=value): 656 assert_python_ok('-c', code, **env_vars) 657 658 def test_set_pycache_prefix(self): 659 # sys.pycache_prefix can be set from either -X pycache_prefix or 660 # PYTHONPYCACHEPREFIX env var, with the former taking precedence. 661 NO_VALUE = object() # `-X pycache_prefix` with no `=PATH` 662 cases = [ 663 # (PYTHONPYCACHEPREFIX, -X pycache_prefix, sys.pycache_prefix) 664 (None, None, None), 665 ('foo', None, 'foo'), 666 (None, 'bar', 'bar'), 667 ('foo', 'bar', 'bar'), 668 ('foo', '', None), 669 ('foo', NO_VALUE, None), 670 ] 671 for envval, opt, expected in cases: 672 exp_clause = "is None" if expected is None else f'== "{expected}"' 673 code = f"import sys; sys.exit(not sys.pycache_prefix {exp_clause})" 674 args = ['-c', code] 675 env = {} if envval is None else {'PYTHONPYCACHEPREFIX': envval} 676 if opt is NO_VALUE: 677 args[:0] = ['-X', 'pycache_prefix'] 678 elif opt is not None: 679 args[:0] = ['-X', f'pycache_prefix={opt}'] 680 with self.subTest(envval=envval, opt=opt): 681 with os_helper.temp_cwd(): 682 assert_python_ok(*args, **env) 683 684 def run_xdev(self, *args, check_exitcode=True, xdev=True): 685 env = dict(os.environ) 686 env.pop('PYTHONWARNINGS', None) 687 env.pop('PYTHONDEVMODE', None) 688 env.pop('PYTHONMALLOC', None) 689 690 if xdev: 691 args = (sys.executable, '-X', 'dev', *args) 692 else: 693 args = (sys.executable, *args) 694 proc = subprocess.run(args, 695 stdout=subprocess.PIPE, 696 stderr=subprocess.STDOUT, 697 universal_newlines=True, 698 env=env) 699 if check_exitcode: 700 self.assertEqual(proc.returncode, 0, proc) 701 return proc.stdout.rstrip() 702 703 @support.cpython_only 704 def test_xdev(self): 705 # sys.flags.dev_mode 706 code = "import sys; print(sys.flags.dev_mode)" 707 out = self.run_xdev("-c", code, xdev=False) 708 self.assertEqual(out, "False") 709 out = self.run_xdev("-c", code) 710 self.assertEqual(out, "True") 711 712 # Warnings 713 code = ("import warnings; " 714 "print(' '.join('%s::%s' % (f[0], f[2].__name__) " 715 "for f in warnings.filters))") 716 if support.Py_DEBUG: 717 expected_filters = "default::Warning" 718 else: 719 expected_filters = ("default::Warning " 720 "default::DeprecationWarning " 721 "ignore::DeprecationWarning " 722 "ignore::PendingDeprecationWarning " 723 "ignore::ImportWarning " 724 "ignore::ResourceWarning") 725 726 out = self.run_xdev("-c", code) 727 self.assertEqual(out, expected_filters) 728 729 out = self.run_xdev("-b", "-c", code) 730 self.assertEqual(out, f"default::BytesWarning {expected_filters}") 731 732 out = self.run_xdev("-bb", "-c", code) 733 self.assertEqual(out, f"error::BytesWarning {expected_filters}") 734 735 out = self.run_xdev("-Werror", "-c", code) 736 self.assertEqual(out, f"error::Warning {expected_filters}") 737 738 # Memory allocator debug hooks 739 try: 740 import _testinternalcapi 741 except ImportError: 742 pass 743 else: 744 code = "import _testinternalcapi; print(_testinternalcapi.pymem_getallocatorsname())" 745 with support.SuppressCrashReport(): 746 out = self.run_xdev("-c", code, check_exitcode=False) 747 if support.with_pymalloc(): 748 alloc_name = "pymalloc_debug" 749 elif support.Py_GIL_DISABLED: 750 alloc_name = "mimalloc_debug" 751 else: 752 alloc_name = "malloc_debug" 753 self.assertEqual(out, alloc_name) 754 755 # Faulthandler 756 try: 757 import faulthandler 758 except ImportError: 759 pass 760 else: 761 code = "import faulthandler; print(faulthandler.is_enabled())" 762 out = self.run_xdev("-c", code) 763 self.assertEqual(out, "True") 764 765 def check_warnings_filters(self, cmdline_option, envvar, use_pywarning=False): 766 if use_pywarning: 767 code = ("import sys; from test.support.import_helper import " 768 "import_fresh_module; " 769 "warnings = import_fresh_module('warnings', blocked=['_warnings']); ") 770 else: 771 code = "import sys, warnings; " 772 code += ("print(' '.join('%s::%s' % (f[0], f[2].__name__) " 773 "for f in warnings.filters))") 774 args = (sys.executable, '-W', cmdline_option, '-bb', '-c', code) 775 env = dict(os.environ) 776 env.pop('PYTHONDEVMODE', None) 777 env["PYTHONWARNINGS"] = envvar 778 proc = subprocess.run(args, 779 stdout=subprocess.PIPE, 780 stderr=subprocess.STDOUT, 781 universal_newlines=True, 782 env=env) 783 self.assertEqual(proc.returncode, 0, proc) 784 return proc.stdout.rstrip() 785 786 def test_warnings_filter_precedence(self): 787 expected_filters = ("error::BytesWarning " 788 "once::UserWarning " 789 "always::UserWarning") 790 if not support.Py_DEBUG: 791 expected_filters += (" " 792 "default::DeprecationWarning " 793 "ignore::DeprecationWarning " 794 "ignore::PendingDeprecationWarning " 795 "ignore::ImportWarning " 796 "ignore::ResourceWarning") 797 798 out = self.check_warnings_filters("once::UserWarning", 799 "always::UserWarning") 800 self.assertEqual(out, expected_filters) 801 802 out = self.check_warnings_filters("once::UserWarning", 803 "always::UserWarning", 804 use_pywarning=True) 805 self.assertEqual(out, expected_filters) 806 807 def check_pythonmalloc(self, env_var, name): 808 code = 'import _testinternalcapi; print(_testinternalcapi.pymem_getallocatorsname())' 809 env = dict(os.environ) 810 env.pop('PYTHONDEVMODE', None) 811 if env_var is not None: 812 env['PYTHONMALLOC'] = env_var 813 else: 814 env.pop('PYTHONMALLOC', None) 815 args = (sys.executable, '-c', code) 816 proc = subprocess.run(args, 817 stdout=subprocess.PIPE, 818 stderr=subprocess.STDOUT, 819 universal_newlines=True, 820 env=env) 821 self.assertEqual(proc.stdout.rstrip(), name) 822 self.assertEqual(proc.returncode, 0) 823 824 @support.cpython_only 825 def test_pythonmalloc(self): 826 # Test the PYTHONMALLOC environment variable 827 malloc = not support.Py_GIL_DISABLED 828 pymalloc = support.with_pymalloc() 829 mimalloc = support.with_mimalloc() 830 if support.Py_GIL_DISABLED: 831 default_name = 'mimalloc_debug' if support.Py_DEBUG else 'mimalloc' 832 default_name_debug = 'mimalloc_debug' 833 elif pymalloc: 834 default_name = 'pymalloc_debug' if support.Py_DEBUG else 'pymalloc' 835 default_name_debug = 'pymalloc_debug' 836 else: 837 default_name = 'malloc_debug' if support.Py_DEBUG else 'malloc' 838 default_name_debug = 'malloc_debug' 839 840 tests = [ 841 (None, default_name), 842 ('debug', default_name_debug), 843 ] 844 if malloc: 845 tests.extend([ 846 ('malloc', 'malloc'), 847 ('malloc_debug', 'malloc_debug'), 848 ]) 849 if pymalloc: 850 tests.extend(( 851 ('pymalloc', 'pymalloc'), 852 ('pymalloc_debug', 'pymalloc_debug'), 853 )) 854 if mimalloc: 855 tests.extend(( 856 ('mimalloc', 'mimalloc'), 857 ('mimalloc_debug', 'mimalloc_debug'), 858 )) 859 860 for env_var, name in tests: 861 with self.subTest(env_var=env_var, name=name): 862 self.check_pythonmalloc(env_var, name) 863 864 def test_pythondevmode_env(self): 865 # Test the PYTHONDEVMODE environment variable 866 code = "import sys; print(sys.flags.dev_mode)" 867 env = dict(os.environ) 868 env.pop('PYTHONDEVMODE', None) 869 args = (sys.executable, '-c', code) 870 871 proc = subprocess.run(args, stdout=subprocess.PIPE, 872 universal_newlines=True, env=env) 873 self.assertEqual(proc.stdout.rstrip(), 'False') 874 self.assertEqual(proc.returncode, 0, proc) 875 876 env['PYTHONDEVMODE'] = '1' 877 proc = subprocess.run(args, stdout=subprocess.PIPE, 878 universal_newlines=True, env=env) 879 self.assertEqual(proc.stdout.rstrip(), 'True') 880 self.assertEqual(proc.returncode, 0, proc) 881 882 def test_python_gil(self): 883 cases = [ 884 # (env, opt, expected, msg) 885 ('1', None, '1', "PYTHON_GIL=1"), 886 (None, '1', '1', "-X gil=1"), 887 ] 888 889 if support.Py_GIL_DISABLED: 890 cases.extend( 891 [ 892 (None, None, 'None', "no options set"), 893 ('0', None, '0', "PYTHON_GIL=0"), 894 ('1', '0', '0', "-X gil=0 overrides PYTHON_GIL=1"), 895 (None, '0', '0', "-X gil=0"), 896 ] 897 ) 898 else: 899 cases.extend( 900 [ 901 (None, None, '1', '-X gil=0 (unsupported by this build)'), 902 ('1', None, '1', 'PYTHON_GIL=0 (unsupported by this build)'), 903 ] 904 ) 905 code = "import sys; print(sys.flags.gil)" 906 environ = dict(os.environ) 907 908 for env, opt, expected, msg in cases: 909 with self.subTest(msg, env=env, opt=opt): 910 environ.pop('PYTHON_GIL', None) 911 if env is not None: 912 environ['PYTHON_GIL'] = env 913 extra_args = [] 914 if opt is not None: 915 extra_args = ['-X', f'gil={opt}'] 916 917 proc = subprocess.run([sys.executable, *extra_args, '-c', code], 918 stdout=subprocess.PIPE, 919 stderr=subprocess.PIPE, 920 text=True, env=environ) 921 self.assertEqual(proc.returncode, 0, proc) 922 self.assertEqual(proc.stdout.rstrip(), expected) 923 self.assertEqual(proc.stderr, '') 924 925 @unittest.skipUnless(sys.platform == 'win32', 926 'bpo-32457 only applies on Windows') 927 def test_argv0_normalization(self): 928 args = sys.executable, '-c', 'print(0)' 929 prefix, exe = os.path.split(sys.executable) 930 executable = prefix + '\\.\\.\\.\\' + exe 931 932 proc = subprocess.run(args, stdout=subprocess.PIPE, 933 executable=executable) 934 self.assertEqual(proc.returncode, 0, proc) 935 self.assertEqual(proc.stdout.strip(), b'0') 936 937 @support.cpython_only 938 def test_parsing_error(self): 939 args = [sys.executable, '-I', '--unknown-option'] 940 proc = subprocess.run(args, 941 stdout=subprocess.PIPE, 942 stderr=subprocess.PIPE, 943 text=True) 944 err_msg = "unknown option --unknown-option\nusage: " 945 self.assertTrue(proc.stderr.startswith(err_msg), proc.stderr) 946 self.assertNotEqual(proc.returncode, 0) 947 948 def test_int_max_str_digits(self): 949 code = "import sys; print(sys.flags.int_max_str_digits, sys.get_int_max_str_digits())" 950 951 assert_python_failure('-X', 'int_max_str_digits', '-c', code) 952 assert_python_failure('-X', 'int_max_str_digits=foo', '-c', code) 953 assert_python_failure('-X', 'int_max_str_digits=100', '-c', code) 954 assert_python_failure('-X', 'int_max_str_digits', '-c', code, 955 PYTHONINTMAXSTRDIGITS='4000') 956 957 assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='foo') 958 assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='100') 959 960 res = assert_python_ok('-c', code) 961 res2int = self.res2int 962 current_max = sys.get_int_max_str_digits() 963 self.assertEqual(res2int(res), (current_max, current_max)) 964 res = assert_python_ok('-X', 'int_max_str_digits=0', '-c', code) 965 self.assertEqual(res2int(res), (0, 0)) 966 res = assert_python_ok('-X', 'int_max_str_digits=4000', '-c', code) 967 self.assertEqual(res2int(res), (4000, 4000)) 968 res = assert_python_ok('-X', 'int_max_str_digits=100000', '-c', code) 969 self.assertEqual(res2int(res), (100000, 100000)) 970 971 res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='0') 972 self.assertEqual(res2int(res), (0, 0)) 973 res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='4000') 974 self.assertEqual(res2int(res), (4000, 4000)) 975 res = assert_python_ok( 976 '-X', 'int_max_str_digits=6000', '-c', code, 977 PYTHONINTMAXSTRDIGITS='4000' 978 ) 979 self.assertEqual(res2int(res), (6000, 6000)) 980 981 def test_cpu_count(self): 982 code = "import os; print(os.cpu_count(), os.process_cpu_count())" 983 res = assert_python_ok('-X', 'cpu_count=4321', '-c', code) 984 self.assertEqual(self.res2int(res), (4321, 4321)) 985 res = assert_python_ok('-c', code, PYTHON_CPU_COUNT='1234') 986 self.assertEqual(self.res2int(res), (1234, 1234)) 987 988 def test_cpu_count_default(self): 989 code = "import os; print(os.cpu_count(), os.process_cpu_count())" 990 res = assert_python_ok('-X', 'cpu_count=default', '-c', code) 991 self.assertEqual(self.res2int(res), (os.cpu_count(), os.process_cpu_count())) 992 res = assert_python_ok('-X', 'cpu_count=default', '-c', code, PYTHON_CPU_COUNT='1234') 993 self.assertEqual(self.res2int(res), (os.cpu_count(), os.process_cpu_count())) 994 es = assert_python_ok('-c', code, PYTHON_CPU_COUNT='default') 995 self.assertEqual(self.res2int(res), (os.cpu_count(), os.process_cpu_count())) 996 997 def res2int(self, res): 998 out = res.out.strip().decode("utf-8") 999 return tuple(int(i) for i in out.split()) 1000 1001 1002@unittest.skipIf(interpreter_requires_environment(), 1003 'Cannot run -I tests when PYTHON env vars are required.') 1004class IgnoreEnvironmentTest(unittest.TestCase): 1005 1006 def run_ignoring_vars(self, predicate, **env_vars): 1007 # Runs a subprocess with -E set, even though we're passing 1008 # specific environment variables 1009 # Logical inversion to match predicate check to a zero return 1010 # code indicating success 1011 code = "import sys; sys.stderr.write(str(sys.flags)); sys.exit(not ({}))".format(predicate) 1012 return assert_python_ok('-E', '-c', code, **env_vars) 1013 1014 def test_ignore_PYTHONPATH(self): 1015 path = "should_be_ignored" 1016 self.run_ignoring_vars("'{}' not in sys.path".format(path), 1017 PYTHONPATH=path) 1018 1019 def test_ignore_PYTHONHASHSEED(self): 1020 self.run_ignoring_vars("sys.flags.hash_randomization == 1", 1021 PYTHONHASHSEED="0") 1022 1023 def test_sys_flags_not_set(self): 1024 # Issue 31845: a startup refactoring broke reading flags from env vars 1025 expected_outcome = """ 1026 (sys.flags.debug == sys.flags.optimize == 1027 sys.flags.dont_write_bytecode == 1028 sys.flags.verbose == sys.flags.safe_path == 0) 1029 """ 1030 self.run_ignoring_vars( 1031 expected_outcome, 1032 PYTHONDEBUG="1", 1033 PYTHONOPTIMIZE="1", 1034 PYTHONDONTWRITEBYTECODE="1", 1035 PYTHONVERBOSE="1", 1036 PYTHONSAFEPATH="1", 1037 ) 1038 1039 1040class SyntaxErrorTests(unittest.TestCase): 1041 @force_not_colorized 1042 def check_string(self, code): 1043 proc = subprocess.run([sys.executable, "-"], input=code, 1044 stdout=subprocess.PIPE, stderr=subprocess.PIPE) 1045 self.assertNotEqual(proc.returncode, 0) 1046 self.assertNotEqual(proc.stderr, None) 1047 self.assertIn(b"\nSyntaxError", proc.stderr) 1048 1049 def test_tokenizer_error_with_stdin(self): 1050 self.check_string(b"(1+2+3") 1051 1052 def test_decoding_error_at_the_end_of_the_line(self): 1053 self.check_string(br"'\u1f'") 1054 1055 1056def tearDownModule(): 1057 support.reap_children() 1058 1059 1060if __name__ == "__main__": 1061 unittest.main() 1062