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