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