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