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