mirror of
				https://github.com/python/cpython.git
				synced 2025-10-26 11:14:33 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			784 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			784 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Tests invocation of the interpreter with various command line arguments
 | |
| # Most tests are executed with environment variables ignored
 | |
| # See test_cmd_line_script.py for testing of script execution
 | |
| 
 | |
| import os
 | |
| import subprocess
 | |
| import sys
 | |
| import tempfile
 | |
| import unittest
 | |
| from test import support
 | |
| from test.support.script_helper import (
 | |
|     spawn_python, kill_python, assert_python_ok, assert_python_failure,
 | |
|     interpreter_requires_environment
 | |
| )
 | |
| 
 | |
| 
 | |
| # Debug build?
 | |
| Py_DEBUG = hasattr(sys, "gettotalrefcount")
 | |
| 
 | |
| 
 | |
| # XXX (ncoghlan): Move to script_helper and make consistent with run_python
 | |
| def _kill_python_and_exit_code(p):
 | |
|     data = kill_python(p)
 | |
|     returncode = p.wait()
 | |
|     return data, returncode
 | |
| 
 | |
| class CmdLineTest(unittest.TestCase):
 | |
|     def test_directories(self):
 | |
|         assert_python_failure('.')
 | |
|         assert_python_failure('< .')
 | |
| 
 | |
|     def verify_valid_flag(self, cmd_line):
 | |
|         rc, out, err = assert_python_ok(*cmd_line)
 | |
|         self.assertTrue(out == b'' or out.endswith(b'\n'))
 | |
|         self.assertNotIn(b'Traceback', out)
 | |
|         self.assertNotIn(b'Traceback', err)
 | |
| 
 | |
|     def test_optimize(self):
 | |
|         self.verify_valid_flag('-O')
 | |
|         self.verify_valid_flag('-OO')
 | |
| 
 | |
|     def test_site_flag(self):
 | |
|         self.verify_valid_flag('-S')
 | |
| 
 | |
|     def test_usage(self):
 | |
|         rc, out, err = assert_python_ok('-h')
 | |
|         self.assertIn(b'usage', out)
 | |
| 
 | |
|     def test_version(self):
 | |
|         version = ('Python %d.%d' % sys.version_info[:2]).encode("ascii")
 | |
|         for switch in '-V', '--version', '-VV':
 | |
|             rc, out, err = assert_python_ok(switch)
 | |
|             self.assertFalse(err.startswith(version))
 | |
|             self.assertTrue(out.startswith(version))
 | |
| 
 | |
|     def test_verbose(self):
 | |
|         # -v causes imports to write to stderr.  If the write to
 | |
|         # stderr itself causes an import to happen (for the output
 | |
|         # codec), a recursion loop can occur.
 | |
|         rc, out, err = assert_python_ok('-v')
 | |
|         self.assertNotIn(b'stack overflow', err)
 | |
|         rc, out, err = assert_python_ok('-vv')
 | |
|         self.assertNotIn(b'stack overflow', err)
 | |
| 
 | |
|     @unittest.skipIf(interpreter_requires_environment(),
 | |
|                      'Cannot run -E tests when PYTHON env vars are required.')
 | |
|     def test_xoptions(self):
 | |
|         def get_xoptions(*args):
 | |
|             # use subprocess module directly because test.support.script_helper adds
 | |
|             # "-X faulthandler" to the command line
 | |
|             args = (sys.executable, '-E') + args
 | |
|             args += ('-c', 'import sys; print(sys._xoptions)')
 | |
|             out = subprocess.check_output(args)
 | |
|             opts = eval(out.splitlines()[0])
 | |
|             return opts
 | |
| 
 | |
|         opts = get_xoptions()
 | |
|         self.assertEqual(opts, {})
 | |
| 
 | |
|         opts = get_xoptions('-Xa', '-Xb=c,d=e')
 | |
|         self.assertEqual(opts, {'a': True, 'b': 'c,d=e'})
 | |
| 
 | |
|     def test_showrefcount(self):
 | |
|         def run_python(*args):
 | |
|             # this is similar to assert_python_ok but doesn't strip
 | |
|             # the refcount from stderr.  It can be replaced once
 | |
|             # assert_python_ok stops doing that.
 | |
|             cmd = [sys.executable]
 | |
|             cmd.extend(args)
 | |
|             PIPE = subprocess.PIPE
 | |
|             p = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE)
 | |
|             out, err = p.communicate()
 | |
|             p.stdout.close()
 | |
|             p.stderr.close()
 | |
|             rc = p.returncode
 | |
|             self.assertEqual(rc, 0)
 | |
|             return rc, out, err
 | |
|         code = 'import sys; print(sys._xoptions)'
 | |
|         # normally the refcount is hidden
 | |
|         rc, out, err = run_python('-c', code)
 | |
|         self.assertEqual(out.rstrip(), b'{}')
 | |
|         self.assertEqual(err, b'')
 | |
|         # "-X showrefcount" shows the refcount, but only in debug builds
 | |
|         rc, out, err = run_python('-X', 'showrefcount', '-c', code)
 | |
|         self.assertEqual(out.rstrip(), b"{'showrefcount': True}")
 | |
|         if Py_DEBUG:
 | |
|             self.assertRegex(err, br'^\[\d+ refs, \d+ blocks\]')
 | |
|         else:
 | |
|             self.assertEqual(err, b'')
 | |
| 
 | |
|     def test_run_module(self):
 | |
|         # Test expected operation of the '-m' switch
 | |
|         # Switch needs an argument
 | |
|         assert_python_failure('-m')
 | |
|         # Check we get an error for a nonexistent module
 | |
|         assert_python_failure('-m', 'fnord43520xyz')
 | |
|         # Check the runpy module also gives an error for
 | |
|         # a nonexistent module
 | |
|         assert_python_failure('-m', 'runpy', 'fnord43520xyz')
 | |
|         # All good if module is located and run successfully
 | |
|         assert_python_ok('-m', 'timeit', '-n', '1')
 | |
| 
 | |
|     def test_run_module_bug1764407(self):
 | |
|         # -m and -i need to play well together
 | |
|         # Runs the timeit module and checks the __main__
 | |
|         # namespace has been populated appropriately
 | |
|         p = spawn_python('-i', '-m', 'timeit', '-n', '1')
 | |
|         p.stdin.write(b'Timer\n')
 | |
|         p.stdin.write(b'exit()\n')
 | |
|         data = kill_python(p)
 | |
|         self.assertTrue(data.find(b'1 loop') != -1)
 | |
|         self.assertTrue(data.find(b'__main__.Timer') != -1)
 | |
| 
 | |
|     def test_run_code(self):
 | |
|         # Test expected operation of the '-c' switch
 | |
|         # Switch needs an argument
 | |
|         assert_python_failure('-c')
 | |
|         # Check we get an error for an uncaught exception
 | |
|         assert_python_failure('-c', 'raise Exception')
 | |
|         # All good if execution is successful
 | |
|         assert_python_ok('-c', 'pass')
 | |
| 
 | |
|     @unittest.skipUnless(support.FS_NONASCII, 'need support.FS_NONASCII')
 | |
|     def test_non_ascii(self):
 | |
|         # Test handling of non-ascii data
 | |
|         command = ("assert(ord(%r) == %s)"
 | |
|                    % (support.FS_NONASCII, ord(support.FS_NONASCII)))
 | |
|         assert_python_ok('-c', command)
 | |
| 
 | |
|     # On Windows, pass bytes to subprocess doesn't test how Python decodes the
 | |
|     # command line, but how subprocess does decode bytes to unicode. Python
 | |
|     # doesn't decode the command line because Windows provides directly the
 | |
|     # arguments as unicode (using wmain() instead of main()).
 | |
|     @unittest.skipIf(sys.platform == 'win32',
 | |
|                      'Windows has a native unicode API')
 | |
|     def test_undecodable_code(self):
 | |
|         undecodable = b"\xff"
 | |
|         env = os.environ.copy()
 | |
|         # Use C locale to get ascii for the locale encoding
 | |
|         env['LC_ALL'] = 'C'
 | |
|         env['PYTHONCOERCECLOCALE'] = '0'
 | |
|         code = (
 | |
|             b'import locale; '
 | |
|             b'print(ascii("' + undecodable + b'"), '
 | |
|                 b'locale.getpreferredencoding())')
 | |
|         p = subprocess.Popen(
 | |
|             [sys.executable, "-c", code],
 | |
|             stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
 | |
|             env=env)
 | |
|         stdout, stderr = p.communicate()
 | |
|         if p.returncode == 1:
 | |
|             # _Py_char2wchar() decoded b'\xff' as '\udcff' (b'\xff' is not
 | |
|             # decodable from ASCII) and run_command() failed on
 | |
|             # PyUnicode_AsUTF8String(). This is the expected behaviour on
 | |
|             # Linux.
 | |
|             pattern = b"Unable to decode the command from the command line:"
 | |
|         elif p.returncode == 0:
 | |
|             # _Py_char2wchar() decoded b'\xff' as '\xff' even if the locale is
 | |
|             # C and the locale encoding is ASCII. It occurs on FreeBSD, Solaris
 | |
|             # and Mac OS X.
 | |
|             pattern = b"'\\xff' "
 | |
|             # The output is followed by the encoding name, an alias to ASCII.
 | |
|             # Examples: "US-ASCII" or "646" (ISO 646, on Solaris).
 | |
|         else:
 | |
|             raise AssertionError("Unknown exit code: %s, output=%a" % (p.returncode, stdout))
 | |
|         if not stdout.startswith(pattern):
 | |
|             raise AssertionError("%a doesn't start with %a" % (stdout, pattern))
 | |
| 
 | |
|     @unittest.skipUnless((sys.platform == 'darwin' or
 | |
|                 support.is_android), 'test specific to Mac OS X and Android')
 | |
|     def test_osx_android_utf8(self):
 | |
|         def check_output(text):
 | |
|             decoded = text.decode('utf-8', 'surrogateescape')
 | |
|             expected = ascii(decoded).encode('ascii') + b'\n'
 | |
| 
 | |
|             env = os.environ.copy()
 | |
|             # C locale gives ASCII locale encoding, but Python uses UTF-8
 | |
|             # to parse the command line arguments on Mac OS X and Android.
 | |
|             env['LC_ALL'] = 'C'
 | |
| 
 | |
|             p = subprocess.Popen(
 | |
|                 (sys.executable, "-c", "import sys; print(ascii(sys.argv[1]))", text),
 | |
|                 stdout=subprocess.PIPE,
 | |
|                 env=env)
 | |
|             stdout, stderr = p.communicate()
 | |
|             self.assertEqual(stdout, expected)
 | |
|             self.assertEqual(p.returncode, 0)
 | |
| 
 | |
|         # test valid utf-8
 | |
|         text = 'e:\xe9, euro:\u20ac, non-bmp:\U0010ffff'.encode('utf-8')
 | |
|         check_output(text)
 | |
| 
 | |
|         # test invalid utf-8
 | |
|         text = (
 | |
|             b'\xff'         # invalid byte
 | |
|             b'\xc3\xa9'     # valid utf-8 character
 | |
|             b'\xc3\xff'     # invalid byte sequence
 | |
|             b'\xed\xa0\x80' # lone surrogate character (invalid)
 | |
|         )
 | |
|         check_output(text)
 | |
| 
 | |
|     def test_unbuffered_output(self):
 | |
|         # Test expected operation of the '-u' switch
 | |
|         for stream in ('stdout', 'stderr'):
 | |
|             # Binary is unbuffered
 | |
|             code = ("import os, sys; sys.%s.buffer.write(b'x'); os._exit(0)"
 | |
|                 % stream)
 | |
|             rc, out, err = assert_python_ok('-u', '-c', code)
 | |
|             data = err if stream == 'stderr' else out
 | |
|             self.assertEqual(data, b'x', "binary %s not unbuffered" % stream)
 | |
|             # Text is unbuffered
 | |
|             code = ("import os, sys; sys.%s.write('x'); os._exit(0)"
 | |
|                 % stream)
 | |
|             rc, out, err = assert_python_ok('-u', '-c', code)
 | |
|             data = err if stream == 'stderr' else out
 | |
|             self.assertEqual(data, b'x', "text %s not unbuffered" % stream)
 | |
| 
 | |
|     def test_unbuffered_input(self):
 | |
|         # sys.stdin still works with '-u'
 | |
|         code = ("import sys; sys.stdout.write(sys.stdin.read(1))")
 | |
|         p = spawn_python('-u', '-c', code)
 | |
|         p.stdin.write(b'x')
 | |
|         p.stdin.flush()
 | |
|         data, rc = _kill_python_and_exit_code(p)
 | |
|         self.assertEqual(rc, 0)
 | |
|         self.assertTrue(data.startswith(b'x'), data)
 | |
| 
 | |
|     def test_large_PYTHONPATH(self):
 | |
|         path1 = "ABCDE" * 100
 | |
|         path2 = "FGHIJ" * 100
 | |
|         path = path1 + os.pathsep + path2
 | |
| 
 | |
|         code = """if 1:
 | |
|             import sys
 | |
|             path = ":".join(sys.path)
 | |
|             path = path.encode("ascii", "backslashreplace")
 | |
|             sys.stdout.buffer.write(path)"""
 | |
|         rc, out, err = assert_python_ok('-S', '-c', code,
 | |
|                                         PYTHONPATH=path)
 | |
|         self.assertIn(path1.encode('ascii'), out)
 | |
|         self.assertIn(path2.encode('ascii'), out)
 | |
| 
 | |
|     def test_empty_PYTHONPATH_issue16309(self):
 | |
|         # On Posix, it is documented that setting PATH to the
 | |
|         # empty string is equivalent to not setting PATH at all,
 | |
|         # which is an exception to the rule that in a string like
 | |
|         # "/bin::/usr/bin" the empty string in the middle gets
 | |
|         # interpreted as '.'
 | |
|         code = """if 1:
 | |
|             import sys
 | |
|             path = ":".join(sys.path)
 | |
|             path = path.encode("ascii", "backslashreplace")
 | |
|             sys.stdout.buffer.write(path)"""
 | |
|         rc1, out1, err1 = assert_python_ok('-c', code, PYTHONPATH="")
 | |
|         rc2, out2, err2 = assert_python_ok('-c', code, __isolated=False)
 | |
|         # regarding to Posix specification, outputs should be equal
 | |
|         # for empty and unset PYTHONPATH
 | |
|         self.assertEqual(out1, out2)
 | |
| 
 | |
|     def test_displayhook_unencodable(self):
 | |
|         for encoding in ('ascii', 'latin-1', 'utf-8'):
 | |
|             env = os.environ.copy()
 | |
|             env['PYTHONIOENCODING'] = encoding
 | |
|             p = subprocess.Popen(
 | |
|                 [sys.executable, '-i'],
 | |
|                 stdin=subprocess.PIPE,
 | |
|                 stdout=subprocess.PIPE,
 | |
|                 stderr=subprocess.STDOUT,
 | |
|                 env=env)
 | |
|             # non-ascii, surrogate, non-BMP printable, non-BMP unprintable
 | |
|             text = "a=\xe9 b=\uDC80 c=\U00010000 d=\U0010FFFF"
 | |
|             p.stdin.write(ascii(text).encode('ascii') + b"\n")
 | |
|             p.stdin.write(b'exit()\n')
 | |
|             data = kill_python(p)
 | |
|             escaped = repr(text).encode(encoding, 'backslashreplace')
 | |
|             self.assertIn(escaped, data)
 | |
| 
 | |
|     def check_input(self, code, expected):
 | |
|         with tempfile.NamedTemporaryFile("wb+") as stdin:
 | |
|             sep = os.linesep.encode('ASCII')
 | |
|             stdin.write(sep.join((b'abc', b'def')))
 | |
|             stdin.flush()
 | |
|             stdin.seek(0)
 | |
|             with subprocess.Popen(
 | |
|                 (sys.executable, "-c", code),
 | |
|                 stdin=stdin, stdout=subprocess.PIPE) as proc:
 | |
|                 stdout, stderr = proc.communicate()
 | |
|         self.assertEqual(stdout.rstrip(), expected)
 | |
| 
 | |
|     def test_stdin_readline(self):
 | |
|         # Issue #11272: check that sys.stdin.readline() replaces '\r\n' by '\n'
 | |
|         # on Windows (sys.stdin is opened in binary mode)
 | |
|         self.check_input(
 | |
|             "import sys; print(repr(sys.stdin.readline()))",
 | |
|             b"'abc\\n'")
 | |
| 
 | |
|     def test_builtin_input(self):
 | |
|         # Issue #11272: check that input() strips newlines ('\n' or '\r\n')
 | |
|         self.check_input(
 | |
|             "print(repr(input()))",
 | |
|             b"'abc'")
 | |
| 
 | |
|     def test_output_newline(self):
 | |
|         # Issue 13119 Newline for print() should be \r\n on Windows.
 | |
|         code = """if 1:
 | |
|             import sys
 | |
|             print(1)
 | |
|             print(2)
 | |
|             print(3, file=sys.stderr)
 | |
|             print(4, file=sys.stderr)"""
 | |
|         rc, out, err = assert_python_ok('-c', code)
 | |
| 
 | |
|         if sys.platform == 'win32':
 | |
|             self.assertEqual(b'1\r\n2\r\n', out)
 | |
|             self.assertEqual(b'3\r\n4', err)
 | |
|         else:
 | |
|             self.assertEqual(b'1\n2\n', out)
 | |
|             self.assertEqual(b'3\n4', err)
 | |
| 
 | |
|     def test_unmached_quote(self):
 | |
|         # Issue #10206: python program starting with unmatched quote
 | |
|         # spewed spaces to stdout
 | |
|         rc, out, err = assert_python_failure('-c', "'")
 | |
|         self.assertRegex(err.decode('ascii', 'ignore'), 'SyntaxError')
 | |
|         self.assertEqual(b'', out)
 | |
| 
 | |
|     def test_stdout_flush_at_shutdown(self):
 | |
|         # Issue #5319: if stdout.flush() fails at shutdown, an error should
 | |
|         # be printed out.
 | |
|         code = """if 1:
 | |
|             import os, sys, test.support
 | |
|             test.support.SuppressCrashReport().__enter__()
 | |
|             sys.stdout.write('x')
 | |
|             os.close(sys.stdout.fileno())"""
 | |
|         rc, out, err = assert_python_failure('-c', code)
 | |
|         self.assertEqual(b'', out)
 | |
|         self.assertEqual(120, rc)
 | |
|         self.assertRegex(err.decode('ascii', 'ignore'),
 | |
|                          'Exception ignored in.*\nOSError: .*')
 | |
| 
 | |
|     def test_closed_stdout(self):
 | |
|         # Issue #13444: if stdout has been explicitly closed, we should
 | |
|         # not attempt to flush it at shutdown.
 | |
|         code = "import sys; sys.stdout.close()"
 | |
|         rc, out, err = assert_python_ok('-c', code)
 | |
|         self.assertEqual(b'', err)
 | |
| 
 | |
|     # Issue #7111: Python should work without standard streams
 | |
| 
 | |
|     @unittest.skipIf(os.name != 'posix', "test needs POSIX semantics")
 | |
|     @unittest.skipIf(sys.platform == "vxworks",
 | |
|                          "test needs preexec support in subprocess.Popen")
 | |
|     def _test_no_stdio(self, streams):
 | |
|         code = """if 1:
 | |
|             import os, sys
 | |
|             for i, s in enumerate({streams}):
 | |
|                 if getattr(sys, s) is not None:
 | |
|                     os._exit(i + 1)
 | |
|             os._exit(42)""".format(streams=streams)
 | |
|         def preexec():
 | |
|             if 'stdin' in streams:
 | |
|                 os.close(0)
 | |
|             if 'stdout' in streams:
 | |
|                 os.close(1)
 | |
|             if 'stderr' in streams:
 | |
|                 os.close(2)
 | |
|         p = subprocess.Popen(
 | |
|             [sys.executable, "-E", "-c", code],
 | |
|             stdin=subprocess.PIPE,
 | |
|             stdout=subprocess.PIPE,
 | |
|             stderr=subprocess.PIPE,
 | |
|             preexec_fn=preexec)
 | |
|         out, err = p.communicate()
 | |
|         self.assertEqual(support.strip_python_stderr(err), b'')
 | |
|         self.assertEqual(p.returncode, 42)
 | |
| 
 | |
|     def test_no_stdin(self):
 | |
|         self._test_no_stdio(['stdin'])
 | |
| 
 | |
|     def test_no_stdout(self):
 | |
|         self._test_no_stdio(['stdout'])
 | |
| 
 | |
|     def test_no_stderr(self):
 | |
|         self._test_no_stdio(['stderr'])
 | |
| 
 | |
|     def test_no_std_streams(self):
 | |
|         self._test_no_stdio(['stdin', 'stdout', 'stderr'])
 | |
| 
 | |
|     def test_hash_randomization(self):
 | |
|         # Verify that -R enables hash randomization:
 | |
|         self.verify_valid_flag('-R')
 | |
|         hashes = []
 | |
|         if os.environ.get('PYTHONHASHSEED', 'random') != 'random':
 | |
|             env = dict(os.environ)  # copy
 | |
|             # We need to test that it is enabled by default without
 | |
|             # the environment variable enabling it for us.
 | |
|             del env['PYTHONHASHSEED']
 | |
|             env['__cleanenv'] = '1'  # consumed by assert_python_ok()
 | |
|         else:
 | |
|             env = {}
 | |
|         for i in range(3):
 | |
|             code = 'print(hash("spam"))'
 | |
|             rc, out, err = assert_python_ok('-c', code, **env)
 | |
|             self.assertEqual(rc, 0)
 | |
|             hashes.append(out)
 | |
|         hashes = sorted(set(hashes))  # uniq
 | |
|         # Rare chance of failure due to 3 random seeds honestly being equal.
 | |
|         self.assertGreater(len(hashes), 1,
 | |
|                            msg='3 runs produced an identical random hash '
 | |
|                                ' for "spam": {}'.format(hashes))
 | |
| 
 | |
|         # Verify that sys.flags contains hash_randomization
 | |
|         code = 'import sys; print("random is", sys.flags.hash_randomization)'
 | |
|         rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='')
 | |
|         self.assertIn(b'random is 1', out)
 | |
| 
 | |
|         rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='random')
 | |
|         self.assertIn(b'random is 1', out)
 | |
| 
 | |
|         rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='0')
 | |
|         self.assertIn(b'random is 0', out)
 | |
| 
 | |
|         rc, out, err = assert_python_ok('-R', '-c', code, PYTHONHASHSEED='0')
 | |
|         self.assertIn(b'random is 1', out)
 | |
| 
 | |
|     def test_del___main__(self):
 | |
|         # Issue #15001: PyRun_SimpleFileExFlags() did crash because it kept a
 | |
|         # borrowed reference to the dict of __main__ module and later modify
 | |
|         # the dict whereas the module was destroyed
 | |
|         filename = support.TESTFN
 | |
|         self.addCleanup(support.unlink, filename)
 | |
|         with open(filename, "w") as script:
 | |
|             print("import sys", file=script)
 | |
|             print("del sys.modules['__main__']", file=script)
 | |
|         assert_python_ok(filename)
 | |
| 
 | |
|     def test_unknown_options(self):
 | |
|         rc, out, err = assert_python_failure('-E', '-z')
 | |
|         self.assertIn(b'Unknown option: -z', err)
 | |
|         self.assertEqual(err.splitlines().count(b'Unknown option: -z'), 1)
 | |
|         self.assertEqual(b'', out)
 | |
|         # Add "without='-E'" to prevent _assert_python to append -E
 | |
|         # to env_vars and change the output of stderr
 | |
|         rc, out, err = assert_python_failure('-z', without='-E')
 | |
|         self.assertIn(b'Unknown option: -z', err)
 | |
|         self.assertEqual(err.splitlines().count(b'Unknown option: -z'), 1)
 | |
|         self.assertEqual(b'', out)
 | |
|         rc, out, err = assert_python_failure('-a', '-z', without='-E')
 | |
|         self.assertIn(b'Unknown option: -a', err)
 | |
|         # only the first unknown option is reported
 | |
|         self.assertNotIn(b'Unknown option: -z', err)
 | |
|         self.assertEqual(err.splitlines().count(b'Unknown option: -a'), 1)
 | |
|         self.assertEqual(b'', out)
 | |
| 
 | |
|     @unittest.skipIf(interpreter_requires_environment(),
 | |
|                      'Cannot run -I tests when PYTHON env vars are required.')
 | |
|     def test_isolatedmode(self):
 | |
|         self.verify_valid_flag('-I')
 | |
|         self.verify_valid_flag('-IEs')
 | |
|         rc, out, err = assert_python_ok('-I', '-c',
 | |
|             'from sys import flags as f; '
 | |
|             'print(f.no_user_site, f.ignore_environment, f.isolated)',
 | |
|             # dummyvar to prevent extraneous -E
 | |
|             dummyvar="")
 | |
|         self.assertEqual(out.strip(), b'1 1 1')
 | |
|         with support.temp_cwd() as tmpdir:
 | |
|             fake = os.path.join(tmpdir, "uuid.py")
 | |
|             main = os.path.join(tmpdir, "main.py")
 | |
|             with open(fake, "w") as f:
 | |
|                 f.write("raise RuntimeError('isolated mode test')\n")
 | |
|             with open(main, "w") as f:
 | |
|                 f.write("import uuid\n")
 | |
|                 f.write("print('ok')\n")
 | |
|             self.assertRaises(subprocess.CalledProcessError,
 | |
|                               subprocess.check_output,
 | |
|                               [sys.executable, main], cwd=tmpdir,
 | |
|                               stderr=subprocess.DEVNULL)
 | |
|             out = subprocess.check_output([sys.executable, "-I", main],
 | |
|                                           cwd=tmpdir)
 | |
|             self.assertEqual(out.strip(), b"ok")
 | |
| 
 | |
|     def test_sys_flags_set(self):
 | |
|         # Issue 31845: a startup refactoring broke reading flags from env vars
 | |
|         for value, expected in (("", 0), ("1", 1), ("text", 1), ("2", 2)):
 | |
|             env_vars = dict(
 | |
|                 PYTHONDEBUG=value,
 | |
|                 PYTHONOPTIMIZE=value,
 | |
|                 PYTHONDONTWRITEBYTECODE=value,
 | |
|                 PYTHONVERBOSE=value,
 | |
|             )
 | |
|             dont_write_bytecode = int(bool(value))
 | |
|             code = (
 | |
|                 "import sys; "
 | |
|                 "sys.stderr.write(str(sys.flags)); "
 | |
|                 f"""sys.exit(not (
 | |
|                     sys.flags.debug == sys.flags.optimize ==
 | |
|                     sys.flags.verbose ==
 | |
|                     {expected}
 | |
|                     and sys.flags.dont_write_bytecode == {dont_write_bytecode}
 | |
|                 ))"""
 | |
|             )
 | |
|             with self.subTest(envar_value=value):
 | |
|                 assert_python_ok('-c', code, **env_vars)
 | |
| 
 | |
|     def test_set_pycache_prefix(self):
 | |
|         # sys.pycache_prefix can be set from either -X pycache_prefix or
 | |
|         # PYTHONPYCACHEPREFIX env var, with the former taking precedence.
 | |
|         NO_VALUE = object()  # `-X pycache_prefix` with no `=PATH`
 | |
|         cases = [
 | |
|             # (PYTHONPYCACHEPREFIX, -X pycache_prefix, sys.pycache_prefix)
 | |
|             (None, None, None),
 | |
|             ('foo', None, 'foo'),
 | |
|             (None, 'bar', 'bar'),
 | |
|             ('foo', 'bar', 'bar'),
 | |
|             ('foo', '', None),
 | |
|             ('foo', NO_VALUE, None),
 | |
|         ]
 | |
|         for envval, opt, expected in cases:
 | |
|             exp_clause = "is None" if expected is None else f'== "{expected}"'
 | |
|             code = f"import sys; sys.exit(not sys.pycache_prefix {exp_clause})"
 | |
|             args = ['-c', code]
 | |
|             env = {} if envval is None else {'PYTHONPYCACHEPREFIX': envval}
 | |
|             if opt is NO_VALUE:
 | |
|                 args[:0] = ['-X', 'pycache_prefix']
 | |
|             elif opt is not None:
 | |
|                 args[:0] = ['-X', f'pycache_prefix={opt}']
 | |
|             with self.subTest(envval=envval, opt=opt):
 | |
|                 with support.temp_cwd():
 | |
|                     assert_python_ok(*args, **env)
 | |
| 
 | |
|     def run_xdev(self, *args, check_exitcode=True, xdev=True):
 | |
|         env = dict(os.environ)
 | |
|         env.pop('PYTHONWARNINGS', None)
 | |
|         env.pop('PYTHONDEVMODE', None)
 | |
|         env.pop('PYTHONMALLOC', None)
 | |
| 
 | |
|         if xdev:
 | |
|             args = (sys.executable, '-X', 'dev', *args)
 | |
|         else:
 | |
|             args = (sys.executable, *args)
 | |
|         proc = subprocess.run(args,
 | |
|                               stdout=subprocess.PIPE,
 | |
|                               stderr=subprocess.STDOUT,
 | |
|                               universal_newlines=True,
 | |
|                               env=env)
 | |
|         if check_exitcode:
 | |
|             self.assertEqual(proc.returncode, 0, proc)
 | |
|         return proc.stdout.rstrip()
 | |
| 
 | |
|     def test_xdev(self):
 | |
|         # sys.flags.dev_mode
 | |
|         code = "import sys; print(sys.flags.dev_mode)"
 | |
|         out = self.run_xdev("-c", code, xdev=False)
 | |
|         self.assertEqual(out, "False")
 | |
|         out = self.run_xdev("-c", code)
 | |
|         self.assertEqual(out, "True")
 | |
| 
 | |
|         # Warnings
 | |
|         code = ("import warnings; "
 | |
|                 "print(' '.join('%s::%s' % (f[0], f[2].__name__) "
 | |
|                                 "for f in warnings.filters))")
 | |
|         if Py_DEBUG:
 | |
|             expected_filters = "default::Warning"
 | |
|         else:
 | |
|             expected_filters = ("default::Warning "
 | |
|                                 "default::DeprecationWarning "
 | |
|                                 "ignore::DeprecationWarning "
 | |
|                                 "ignore::PendingDeprecationWarning "
 | |
|                                 "ignore::ImportWarning "
 | |
|                                 "ignore::ResourceWarning")
 | |
| 
 | |
|         out = self.run_xdev("-c", code)
 | |
|         self.assertEqual(out, expected_filters)
 | |
| 
 | |
|         out = self.run_xdev("-b", "-c", code)
 | |
|         self.assertEqual(out, f"default::BytesWarning {expected_filters}")
 | |
| 
 | |
|         out = self.run_xdev("-bb", "-c", code)
 | |
|         self.assertEqual(out, f"error::BytesWarning {expected_filters}")
 | |
| 
 | |
|         out = self.run_xdev("-Werror", "-c", code)
 | |
|         self.assertEqual(out, f"error::Warning {expected_filters}")
 | |
| 
 | |
|         # Memory allocator debug hooks
 | |
|         try:
 | |
|             import _testcapi
 | |
|         except ImportError:
 | |
|             pass
 | |
|         else:
 | |
|             code = "import _testcapi; print(_testcapi.pymem_getallocatorsname())"
 | |
|             with support.SuppressCrashReport():
 | |
|                 out = self.run_xdev("-c", code, check_exitcode=False)
 | |
|             if support.with_pymalloc():
 | |
|                 alloc_name = "pymalloc_debug"
 | |
|             else:
 | |
|                 alloc_name = "malloc_debug"
 | |
|             self.assertEqual(out, alloc_name)
 | |
| 
 | |
|         # Faulthandler
 | |
|         try:
 | |
|             import faulthandler
 | |
|         except ImportError:
 | |
|             pass
 | |
|         else:
 | |
|             code = "import faulthandler; print(faulthandler.is_enabled())"
 | |
|             out = self.run_xdev("-c", code)
 | |
|             self.assertEqual(out, "True")
 | |
| 
 | |
|     def check_warnings_filters(self, cmdline_option, envvar, use_pywarning=False):
 | |
|         if use_pywarning:
 | |
|             code = ("import sys; from test.support import import_fresh_module; "
 | |
|                     "warnings = import_fresh_module('warnings', blocked=['_warnings']); ")
 | |
|         else:
 | |
|             code = "import sys, warnings; "
 | |
|         code += ("print(' '.join('%s::%s' % (f[0], f[2].__name__) "
 | |
|                                 "for f in warnings.filters))")
 | |
|         args = (sys.executable, '-W', cmdline_option, '-bb', '-c', code)
 | |
|         env = dict(os.environ)
 | |
|         env.pop('PYTHONDEVMODE', None)
 | |
|         env["PYTHONWARNINGS"] = envvar
 | |
|         proc = subprocess.run(args,
 | |
|                               stdout=subprocess.PIPE,
 | |
|                               stderr=subprocess.STDOUT,
 | |
|                               universal_newlines=True,
 | |
|                               env=env)
 | |
|         self.assertEqual(proc.returncode, 0, proc)
 | |
|         return proc.stdout.rstrip()
 | |
| 
 | |
|     def test_warnings_filter_precedence(self):
 | |
|         expected_filters = ("error::BytesWarning "
 | |
|                             "once::UserWarning "
 | |
|                             "always::UserWarning")
 | |
|         if not Py_DEBUG:
 | |
|             expected_filters += (" "
 | |
|                                  "default::DeprecationWarning "
 | |
|                                  "ignore::DeprecationWarning "
 | |
|                                  "ignore::PendingDeprecationWarning "
 | |
|                                  "ignore::ImportWarning "
 | |
|                                  "ignore::ResourceWarning")
 | |
| 
 | |
|         out = self.check_warnings_filters("once::UserWarning",
 | |
|                                           "always::UserWarning")
 | |
|         self.assertEqual(out, expected_filters)
 | |
| 
 | |
|         out = self.check_warnings_filters("once::UserWarning",
 | |
|                                           "always::UserWarning",
 | |
|                                           use_pywarning=True)
 | |
|         self.assertEqual(out, expected_filters)
 | |
| 
 | |
|     def check_pythonmalloc(self, env_var, name):
 | |
|         code = 'import _testcapi; print(_testcapi.pymem_getallocatorsname())'
 | |
|         env = dict(os.environ)
 | |
|         env.pop('PYTHONDEVMODE', None)
 | |
|         if env_var is not None:
 | |
|             env['PYTHONMALLOC'] = env_var
 | |
|         else:
 | |
|             env.pop('PYTHONMALLOC', None)
 | |
|         args = (sys.executable, '-c', code)
 | |
|         proc = subprocess.run(args,
 | |
|                               stdout=subprocess.PIPE,
 | |
|                               stderr=subprocess.STDOUT,
 | |
|                               universal_newlines=True,
 | |
|                               env=env)
 | |
|         self.assertEqual(proc.stdout.rstrip(), name)
 | |
|         self.assertEqual(proc.returncode, 0)
 | |
| 
 | |
|     def test_pythonmalloc(self):
 | |
|         # Test the PYTHONMALLOC environment variable
 | |
|         pymalloc = support.with_pymalloc()
 | |
|         if pymalloc:
 | |
|             default_name = 'pymalloc_debug' if Py_DEBUG else 'pymalloc'
 | |
|             default_name_debug = 'pymalloc_debug'
 | |
|         else:
 | |
|             default_name = 'malloc_debug' if Py_DEBUG else 'malloc'
 | |
|             default_name_debug = 'malloc_debug'
 | |
| 
 | |
|         tests = [
 | |
|             (None, default_name),
 | |
|             ('debug', default_name_debug),
 | |
|             ('malloc', 'malloc'),
 | |
|             ('malloc_debug', 'malloc_debug'),
 | |
|         ]
 | |
|         if pymalloc:
 | |
|             tests.extend((
 | |
|                 ('pymalloc', 'pymalloc'),
 | |
|                 ('pymalloc_debug', 'pymalloc_debug'),
 | |
|             ))
 | |
| 
 | |
|         for env_var, name in tests:
 | |
|             with self.subTest(env_var=env_var, name=name):
 | |
|                 self.check_pythonmalloc(env_var, name)
 | |
| 
 | |
|     def test_pythondevmode_env(self):
 | |
|         # Test the PYTHONDEVMODE environment variable
 | |
|         code = "import sys; print(sys.flags.dev_mode)"
 | |
|         env = dict(os.environ)
 | |
|         env.pop('PYTHONDEVMODE', None)
 | |
|         args = (sys.executable, '-c', code)
 | |
| 
 | |
|         proc = subprocess.run(args, stdout=subprocess.PIPE,
 | |
|                               universal_newlines=True, env=env)
 | |
|         self.assertEqual(proc.stdout.rstrip(), 'False')
 | |
|         self.assertEqual(proc.returncode, 0, proc)
 | |
| 
 | |
|         env['PYTHONDEVMODE'] = '1'
 | |
|         proc = subprocess.run(args, stdout=subprocess.PIPE,
 | |
|                               universal_newlines=True, env=env)
 | |
|         self.assertEqual(proc.stdout.rstrip(), 'True')
 | |
|         self.assertEqual(proc.returncode, 0, proc)
 | |
| 
 | |
|     @unittest.skipUnless(sys.platform == 'win32',
 | |
|                          'bpo-32457 only applies on Windows')
 | |
|     def test_argv0_normalization(self):
 | |
|         args = sys.executable, '-c', 'print(0)'
 | |
|         prefix, exe = os.path.split(sys.executable)
 | |
|         executable = prefix + '\\.\\.\\.\\' + exe
 | |
| 
 | |
|         proc = subprocess.run(args, stdout=subprocess.PIPE,
 | |
|                               executable=executable)
 | |
|         self.assertEqual(proc.returncode, 0, proc)
 | |
|         self.assertEqual(proc.stdout.strip(), b'0')
 | |
| 
 | |
| @unittest.skipIf(interpreter_requires_environment(),
 | |
|                  'Cannot run -I tests when PYTHON env vars are required.')
 | |
| class IgnoreEnvironmentTest(unittest.TestCase):
 | |
| 
 | |
|     def run_ignoring_vars(self, predicate, **env_vars):
 | |
|         # Runs a subprocess with -E set, even though we're passing
 | |
|         # specific environment variables
 | |
|         # Logical inversion to match predicate check to a zero return
 | |
|         # code indicating success
 | |
|         code = "import sys; sys.stderr.write(str(sys.flags)); sys.exit(not ({}))".format(predicate)
 | |
|         return assert_python_ok('-E', '-c', code, **env_vars)
 | |
| 
 | |
|     def test_ignore_PYTHONPATH(self):
 | |
|         path = "should_be_ignored"
 | |
|         self.run_ignoring_vars("'{}' not in sys.path".format(path),
 | |
|                                PYTHONPATH=path)
 | |
| 
 | |
|     def test_ignore_PYTHONHASHSEED(self):
 | |
|         self.run_ignoring_vars("sys.flags.hash_randomization == 1",
 | |
|                                PYTHONHASHSEED="0")
 | |
| 
 | |
|     def test_sys_flags_not_set(self):
 | |
|         # Issue 31845: a startup refactoring broke reading flags from env vars
 | |
|         expected_outcome = """
 | |
|             (sys.flags.debug == sys.flags.optimize ==
 | |
|              sys.flags.dont_write_bytecode == sys.flags.verbose == 0)
 | |
|         """
 | |
|         self.run_ignoring_vars(
 | |
|             expected_outcome,
 | |
|             PYTHONDEBUG="1",
 | |
|             PYTHONOPTIMIZE="1",
 | |
|             PYTHONDONTWRITEBYTECODE="1",
 | |
|             PYTHONVERBOSE="1",
 | |
|         )
 | |
| 
 | |
| 
 | |
| def test_main():
 | |
|     support.run_unittest(CmdLineTest, IgnoreEnvironmentTest)
 | |
|     support.reap_children()
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     test_main()
 | 
