1# tests command line execution of scripts
2
3import contextlib
4import importlib
5import importlib.machinery
6import zipimport
7import unittest
8import sys
9import os
10import os.path
11import py_compile
12import subprocess
13import io
14
15import textwrap
16from test import support
17from test.support.script_helper import (
18    make_pkg, make_script, make_zip_pkg, make_zip_script,
19    assert_python_ok, assert_python_failure, spawn_python, kill_python)
20
21verbose = support.verbose
22
23example_args = ['test1', 'test2', 'test3']
24
25test_source = """\
26# Script may be run with optimisation enabled, so don't rely on assert
27# statements being executed
28def assertEqual(lhs, rhs):
29    if lhs != rhs:
30        raise AssertionError('%r != %r' % (lhs, rhs))
31def assertIdentical(lhs, rhs):
32    if lhs is not rhs:
33        raise AssertionError('%r is not %r' % (lhs, rhs))
34# Check basic code execution
35result = ['Top level assignment']
36def f():
37    result.append('Lower level reference')
38f()
39assertEqual(result, ['Top level assignment', 'Lower level reference'])
40# Check population of magic variables
41assertEqual(__name__, '__main__')
42from importlib.machinery import BuiltinImporter
43_loader = __loader__ if __loader__ is BuiltinImporter else type(__loader__)
44print('__loader__==%a' % _loader)
45print('__file__==%a' % __file__)
46print('__cached__==%a' % __cached__)
47print('__package__==%r' % __package__)
48# Check PEP 451 details
49import os.path
50if __package__ is not None:
51    print('__main__ was located through the import system')
52    assertIdentical(__spec__.loader, __loader__)
53    expected_spec_name = os.path.splitext(os.path.basename(__file__))[0]
54    if __package__:
55        expected_spec_name = __package__ + "." + expected_spec_name
56    assertEqual(__spec__.name, expected_spec_name)
57    assertEqual(__spec__.parent, __package__)
58    assertIdentical(__spec__.submodule_search_locations, None)
59    assertEqual(__spec__.origin, __file__)
60    if __spec__.cached is not None:
61        assertEqual(__spec__.cached, __cached__)
62# Check the sys module
63import sys
64assertIdentical(globals(), sys.modules[__name__].__dict__)
65if __spec__ is not None:
66    # XXX: We're not currently making __main__ available under its real name
67    pass # assertIdentical(globals(), sys.modules[__spec__.name].__dict__)
68from test import test_cmd_line_script
69example_args_list = test_cmd_line_script.example_args
70assertEqual(sys.argv[1:], example_args_list)
71print('sys.argv[0]==%a' % sys.argv[0])
72print('sys.path[0]==%a' % sys.path[0])
73# Check the working directory
74import os
75print('cwd==%a' % os.getcwd())
76"""
77
78def _make_test_script(script_dir, script_basename, source=test_source):
79    to_return = make_script(script_dir, script_basename, source)
80    importlib.invalidate_caches()
81    return to_return
82
83def _make_test_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename,
84                       source=test_source, depth=1):
85    to_return = make_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename,
86                             source, depth)
87    importlib.invalidate_caches()
88    return to_return
89
90class CmdLineTest(unittest.TestCase):
91    def _check_output(self, script_name, exit_code, data,
92                             expected_file, expected_argv0,
93                             expected_path0, expected_package,
94                             expected_loader, expected_cwd=None):
95        if verbose > 1:
96            print("Output from test script %r:" % script_name)
97            print(repr(data))
98        self.assertEqual(exit_code, 0)
99        printed_loader = '__loader__==%a' % expected_loader
100        printed_file = '__file__==%a' % expected_file
101        printed_package = '__package__==%r' % expected_package
102        printed_argv0 = 'sys.argv[0]==%a' % expected_argv0
103        printed_path0 = 'sys.path[0]==%a' % expected_path0
104        if expected_cwd is None:
105            expected_cwd = os.getcwd()
106        printed_cwd = 'cwd==%a' % expected_cwd
107        if verbose > 1:
108            print('Expected output:')
109            print(printed_file)
110            print(printed_package)
111            print(printed_argv0)
112            print(printed_cwd)
113        self.assertIn(printed_loader.encode('utf-8'), data)
114        self.assertIn(printed_file.encode('utf-8'), data)
115        self.assertIn(printed_package.encode('utf-8'), data)
116        self.assertIn(printed_argv0.encode('utf-8'), data)
117        self.assertIn(printed_path0.encode('utf-8'), data)
118        self.assertIn(printed_cwd.encode('utf-8'), data)
119
120    def _check_script(self, script_exec_args, expected_file,
121                            expected_argv0, expected_path0,
122                            expected_package, expected_loader,
123                            *cmd_line_switches, cwd=None, **env_vars):
124        if isinstance(script_exec_args, str):
125            script_exec_args = [script_exec_args]
126        run_args = [*support.optim_args_from_interpreter_flags(),
127                    *cmd_line_switches, *script_exec_args, *example_args]
128        rc, out, err = assert_python_ok(
129            *run_args, __isolated=False, __cwd=cwd, **env_vars
130        )
131        self._check_output(script_exec_args, rc, out + err, expected_file,
132                           expected_argv0, expected_path0,
133                           expected_package, expected_loader, cwd)
134
135    def _check_import_error(self, script_exec_args, expected_msg,
136                            *cmd_line_switches, cwd=None, **env_vars):
137        if isinstance(script_exec_args, str):
138            script_exec_args = (script_exec_args,)
139        else:
140            script_exec_args = tuple(script_exec_args)
141        run_args = cmd_line_switches + script_exec_args
142        rc, out, err = assert_python_failure(
143            *run_args, __isolated=False, __cwd=cwd, **env_vars
144        )
145        if verbose > 1:
146            print(f'Output from test script {script_exec_args!r:}')
147            print(repr(err))
148            print('Expected output: %r' % expected_msg)
149        self.assertIn(expected_msg.encode('utf-8'), err)
150
151    def test_dash_c_loader(self):
152        rc, out, err = assert_python_ok("-c", "print(__loader__)")
153        expected = repr(importlib.machinery.BuiltinImporter).encode("utf-8")
154        self.assertIn(expected, out)
155
156    def test_stdin_loader(self):
157        # Unfortunately, there's no way to automatically test the fully
158        # interactive REPL, since that code path only gets executed when
159        # stdin is an interactive tty.
160        p = spawn_python()
161        try:
162            p.stdin.write(b"print(__loader__)\n")
163            p.stdin.flush()
164        finally:
165            out = kill_python(p)
166        expected = repr(importlib.machinery.BuiltinImporter).encode("utf-8")
167        self.assertIn(expected, out)
168
169    @contextlib.contextmanager
170    def interactive_python(self, separate_stderr=False):
171        if separate_stderr:
172            p = spawn_python('-i', stderr=subprocess.PIPE)
173            stderr = p.stderr
174        else:
175            p = spawn_python('-i', stderr=subprocess.STDOUT)
176            stderr = p.stdout
177        try:
178            # Drain stderr until prompt
179            while True:
180                data = stderr.read(4)
181                if data == b">>> ":
182                    break
183                stderr.readline()
184            yield p
185        finally:
186            kill_python(p)
187            stderr.close()
188
189    def check_repl_stdout_flush(self, separate_stderr=False):
190        with self.interactive_python(separate_stderr) as p:
191            p.stdin.write(b"print('foo')\n")
192            p.stdin.flush()
193            self.assertEqual(b'foo', p.stdout.readline().strip())
194
195    def check_repl_stderr_flush(self, separate_stderr=False):
196        with self.interactive_python(separate_stderr) as p:
197            p.stdin.write(b"1/0\n")
198            p.stdin.flush()
199            stderr = p.stderr if separate_stderr else p.stdout
200            self.assertIn(b'Traceback ', stderr.readline())
201            self.assertIn(b'File "<stdin>"', stderr.readline())
202            self.assertIn(b'ZeroDivisionError', stderr.readline())
203
204    def test_repl_stdout_flush(self):
205        self.check_repl_stdout_flush()
206
207    def test_repl_stdout_flush_separate_stderr(self):
208        self.check_repl_stdout_flush(True)
209
210    def test_repl_stderr_flush(self):
211        self.check_repl_stderr_flush()
212
213    def test_repl_stderr_flush_separate_stderr(self):
214        self.check_repl_stderr_flush(True)
215
216    def test_basic_script(self):
217        with support.temp_dir() as script_dir:
218            script_name = _make_test_script(script_dir, 'script')
219            self._check_script(script_name, script_name, script_name,
220                               script_dir, None,
221                               importlib.machinery.SourceFileLoader)
222
223    def test_script_compiled(self):
224        with support.temp_dir() as script_dir:
225            script_name = _make_test_script(script_dir, 'script')
226            py_compile.compile(script_name, doraise=True)
227            os.remove(script_name)
228            pyc_file = support.make_legacy_pyc(script_name)
229            self._check_script(pyc_file, pyc_file,
230                               pyc_file, script_dir, None,
231                               importlib.machinery.SourcelessFileLoader)
232
233    def test_directory(self):
234        with support.temp_dir() as script_dir:
235            script_name = _make_test_script(script_dir, '__main__')
236            self._check_script(script_dir, script_name, script_dir,
237                               script_dir, '',
238                               importlib.machinery.SourceFileLoader)
239
240    def test_directory_compiled(self):
241        with support.temp_dir() as script_dir:
242            script_name = _make_test_script(script_dir, '__main__')
243            py_compile.compile(script_name, doraise=True)
244            os.remove(script_name)
245            pyc_file = support.make_legacy_pyc(script_name)
246            self._check_script(script_dir, pyc_file, script_dir,
247                               script_dir, '',
248                               importlib.machinery.SourcelessFileLoader)
249
250    def test_directory_error(self):
251        with support.temp_dir() as script_dir:
252            msg = "can't find '__main__' module in %r" % script_dir
253            self._check_import_error(script_dir, msg)
254
255    def test_zipfile(self):
256        with support.temp_dir() as script_dir:
257            script_name = _make_test_script(script_dir, '__main__')
258            zip_name, run_name = make_zip_script(script_dir, 'test_zip', script_name)
259            self._check_script(zip_name, run_name, zip_name, zip_name, '',
260                               zipimport.zipimporter)
261
262    def test_zipfile_compiled_timestamp(self):
263        with support.temp_dir() as script_dir:
264            script_name = _make_test_script(script_dir, '__main__')
265            compiled_name = py_compile.compile(
266                script_name, doraise=True,
267                invalidation_mode=py_compile.PycInvalidationMode.TIMESTAMP)
268            zip_name, run_name = make_zip_script(script_dir, 'test_zip', compiled_name)
269            self._check_script(zip_name, run_name, zip_name, zip_name, '',
270                               zipimport.zipimporter)
271
272    def test_zipfile_compiled_checked_hash(self):
273        with support.temp_dir() as script_dir:
274            script_name = _make_test_script(script_dir, '__main__')
275            compiled_name = py_compile.compile(
276                script_name, doraise=True,
277                invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH)
278            zip_name, run_name = make_zip_script(script_dir, 'test_zip', compiled_name)
279            self._check_script(zip_name, run_name, zip_name, zip_name, '',
280                               zipimport.zipimporter)
281
282    def test_zipfile_compiled_unchecked_hash(self):
283        with support.temp_dir() as script_dir:
284            script_name = _make_test_script(script_dir, '__main__')
285            compiled_name = py_compile.compile(
286                script_name, doraise=True,
287                invalidation_mode=py_compile.PycInvalidationMode.UNCHECKED_HASH)
288            zip_name, run_name = make_zip_script(script_dir, 'test_zip', compiled_name)
289            self._check_script(zip_name, run_name, zip_name, zip_name, '',
290                               zipimport.zipimporter)
291
292    def test_zipfile_error(self):
293        with support.temp_dir() as script_dir:
294            script_name = _make_test_script(script_dir, 'not_main')
295            zip_name, run_name = make_zip_script(script_dir, 'test_zip', script_name)
296            msg = "can't find '__main__' module in %r" % zip_name
297            self._check_import_error(zip_name, msg)
298
299    def test_module_in_package(self):
300        with support.temp_dir() as script_dir:
301            pkg_dir = os.path.join(script_dir, 'test_pkg')
302            make_pkg(pkg_dir)
303            script_name = _make_test_script(pkg_dir, 'script')
304            self._check_script(["-m", "test_pkg.script"], script_name, script_name,
305                               script_dir, 'test_pkg',
306                               importlib.machinery.SourceFileLoader,
307                               cwd=script_dir)
308
309    def test_module_in_package_in_zipfile(self):
310        with support.temp_dir() as script_dir:
311            zip_name, run_name = _make_test_zip_pkg(script_dir, 'test_zip', 'test_pkg', 'script')
312            self._check_script(["-m", "test_pkg.script"], run_name, run_name,
313                               script_dir, 'test_pkg', zipimport.zipimporter,
314                               PYTHONPATH=zip_name, cwd=script_dir)
315
316    def test_module_in_subpackage_in_zipfile(self):
317        with support.temp_dir() as script_dir:
318            zip_name, run_name = _make_test_zip_pkg(script_dir, 'test_zip', 'test_pkg', 'script', depth=2)
319            self._check_script(["-m", "test_pkg.test_pkg.script"], run_name, run_name,
320                               script_dir, 'test_pkg.test_pkg',
321                               zipimport.zipimporter,
322                               PYTHONPATH=zip_name, cwd=script_dir)
323
324    def test_package(self):
325        with support.temp_dir() as script_dir:
326            pkg_dir = os.path.join(script_dir, 'test_pkg')
327            make_pkg(pkg_dir)
328            script_name = _make_test_script(pkg_dir, '__main__')
329            self._check_script(["-m", "test_pkg"], script_name,
330                               script_name, script_dir, 'test_pkg',
331                               importlib.machinery.SourceFileLoader,
332                               cwd=script_dir)
333
334    def test_package_compiled(self):
335        with support.temp_dir() as script_dir:
336            pkg_dir = os.path.join(script_dir, 'test_pkg')
337            make_pkg(pkg_dir)
338            script_name = _make_test_script(pkg_dir, '__main__')
339            compiled_name = py_compile.compile(script_name, doraise=True)
340            os.remove(script_name)
341            pyc_file = support.make_legacy_pyc(script_name)
342            self._check_script(["-m", "test_pkg"], pyc_file,
343                               pyc_file, script_dir, 'test_pkg',
344                               importlib.machinery.SourcelessFileLoader,
345                               cwd=script_dir)
346
347    def test_package_error(self):
348        with support.temp_dir() as script_dir:
349            pkg_dir = os.path.join(script_dir, 'test_pkg')
350            make_pkg(pkg_dir)
351            msg = ("'test_pkg' is a package and cannot "
352                   "be directly executed")
353            self._check_import_error(["-m", "test_pkg"], msg, cwd=script_dir)
354
355    def test_package_recursion(self):
356        with support.temp_dir() as script_dir:
357            pkg_dir = os.path.join(script_dir, 'test_pkg')
358            make_pkg(pkg_dir)
359            main_dir = os.path.join(pkg_dir, '__main__')
360            make_pkg(main_dir)
361            msg = ("Cannot use package as __main__ module; "
362                   "'test_pkg' is a package and cannot "
363                   "be directly executed")
364            self._check_import_error(["-m", "test_pkg"], msg, cwd=script_dir)
365
366    def test_issue8202(self):
367        # Make sure package __init__ modules see "-m" in sys.argv0 while
368        # searching for the module to execute
369        with support.temp_dir() as script_dir:
370            with support.change_cwd(path=script_dir):
371                pkg_dir = os.path.join(script_dir, 'test_pkg')
372                make_pkg(pkg_dir, "import sys; print('init_argv0==%r' % sys.argv[0])")
373                script_name = _make_test_script(pkg_dir, 'script')
374                rc, out, err = assert_python_ok('-m', 'test_pkg.script', *example_args, __isolated=False)
375                if verbose > 1:
376                    print(repr(out))
377                expected = "init_argv0==%r" % '-m'
378                self.assertIn(expected.encode('utf-8'), out)
379                self._check_output(script_name, rc, out,
380                                   script_name, script_name, script_dir, 'test_pkg',
381                                   importlib.machinery.SourceFileLoader)
382
383    def test_issue8202_dash_c_file_ignored(self):
384        # Make sure a "-c" file in the current directory
385        # does not alter the value of sys.path[0]
386        with support.temp_dir() as script_dir:
387            with support.change_cwd(path=script_dir):
388                with open("-c", "w") as f:
389                    f.write("data")
390                    rc, out, err = assert_python_ok('-c',
391                        'import sys; print("sys.path[0]==%r" % sys.path[0])',
392                        __isolated=False)
393                    if verbose > 1:
394                        print(repr(out))
395                    expected = "sys.path[0]==%r" % ''
396                    self.assertIn(expected.encode('utf-8'), out)
397
398    def test_issue8202_dash_m_file_ignored(self):
399        # Make sure a "-m" file in the current directory
400        # does not alter the value of sys.path[0]
401        with support.temp_dir() as script_dir:
402            script_name = _make_test_script(script_dir, 'other')
403            with support.change_cwd(path=script_dir):
404                with open("-m", "w") as f:
405                    f.write("data")
406                    rc, out, err = assert_python_ok('-m', 'other', *example_args,
407                                                    __isolated=False)
408                    self._check_output(script_name, rc, out,
409                                      script_name, script_name, script_dir, '',
410                                      importlib.machinery.SourceFileLoader)
411
412    def test_issue20884(self):
413        # On Windows, script with encoding cookie and LF line ending
414        # will be failed.
415        with support.temp_dir() as script_dir:
416            script_name = os.path.join(script_dir, "issue20884.py")
417            with open(script_name, "w", newline='\n') as f:
418                f.write("#coding: iso-8859-1\n")
419                f.write('"""\n')
420                for _ in range(30):
421                    f.write('x'*80 + '\n')
422                f.write('"""\n')
423
424            with support.change_cwd(path=script_dir):
425                rc, out, err = assert_python_ok(script_name)
426            self.assertEqual(b"", out)
427            self.assertEqual(b"", err)
428
429    @contextlib.contextmanager
430    def setup_test_pkg(self, *args):
431        with support.temp_dir() as script_dir, \
432                support.change_cwd(path=script_dir):
433            pkg_dir = os.path.join(script_dir, 'test_pkg')
434            make_pkg(pkg_dir, *args)
435            yield pkg_dir
436
437    def check_dash_m_failure(self, *args):
438        rc, out, err = assert_python_failure('-m', *args, __isolated=False)
439        if verbose > 1:
440            print(repr(out))
441        self.assertEqual(rc, 1)
442        return err
443
444    def test_dash_m_error_code_is_one(self):
445        # If a module is invoked with the -m command line flag
446        # and results in an error that the return code to the
447        # shell is '1'
448        with self.setup_test_pkg() as pkg_dir:
449            script_name = _make_test_script(pkg_dir, 'other',
450                                            "if __name__ == '__main__': raise ValueError")
451            err = self.check_dash_m_failure('test_pkg.other', *example_args)
452            self.assertIn(b'ValueError', err)
453
454    def test_dash_m_errors(self):
455        # Exercise error reporting for various invalid package executions
456        tests = (
457            ('builtins', br'No code object available'),
458            ('builtins.x', br'Error while finding module specification.*'
459                br'ModuleNotFoundError'),
460            ('builtins.x.y', br'Error while finding module specification.*'
461                br'ModuleNotFoundError.*No module named.*not a package'),
462            ('os.path', br'loader.*cannot handle'),
463            ('importlib', br'No module named.*'
464                br'is a package and cannot be directly executed'),
465            ('importlib.nonexistent', br'No module named'),
466            ('.unittest', br'Relative module names not supported'),
467        )
468        for name, regex in tests:
469            with self.subTest(name):
470                rc, _, err = assert_python_failure('-m', name)
471                self.assertEqual(rc, 1)
472                self.assertRegex(err, regex)
473                self.assertNotIn(b'Traceback', err)
474
475    def test_dash_m_bad_pyc(self):
476        with support.temp_dir() as script_dir, \
477                support.change_cwd(path=script_dir):
478            os.mkdir('test_pkg')
479            # Create invalid *.pyc as empty file
480            with open('test_pkg/__init__.pyc', 'wb'):
481                pass
482            err = self.check_dash_m_failure('test_pkg')
483            self.assertRegex(err,
484                br'Error while finding module specification.*'
485                br'ImportError.*bad magic number')
486            self.assertNotIn(b'is a package', err)
487            self.assertNotIn(b'Traceback', err)
488
489    def test_dash_m_init_traceback(self):
490        # These were wrapped in an ImportError and tracebacks were
491        # suppressed; see Issue 14285
492        exceptions = (ImportError, AttributeError, TypeError, ValueError)
493        for exception in exceptions:
494            exception = exception.__name__
495            init = "raise {0}('Exception in __init__.py')".format(exception)
496            with self.subTest(exception), \
497                    self.setup_test_pkg(init) as pkg_dir:
498                err = self.check_dash_m_failure('test_pkg')
499                self.assertIn(exception.encode('ascii'), err)
500                self.assertIn(b'Exception in __init__.py', err)
501                self.assertIn(b'Traceback', err)
502
503    def test_dash_m_main_traceback(self):
504        # Ensure that an ImportError's traceback is reported
505        with self.setup_test_pkg() as pkg_dir:
506            main = "raise ImportError('Exception in __main__ module')"
507            _make_test_script(pkg_dir, '__main__', main)
508            err = self.check_dash_m_failure('test_pkg')
509            self.assertIn(b'ImportError', err)
510            self.assertIn(b'Exception in __main__ module', err)
511            self.assertIn(b'Traceback', err)
512
513    def test_pep_409_verbiage(self):
514        # Make sure PEP 409 syntax properly suppresses
515        # the context of an exception
516        script = textwrap.dedent("""\
517            try:
518                raise ValueError
519            except:
520                raise NameError from None
521            """)
522        with support.temp_dir() as script_dir:
523            script_name = _make_test_script(script_dir, 'script', script)
524            exitcode, stdout, stderr = assert_python_failure(script_name)
525            text = stderr.decode('ascii').split('\n')
526            self.assertEqual(len(text), 4)
527            self.assertTrue(text[0].startswith('Traceback'))
528            self.assertTrue(text[1].startswith('  File '))
529            self.assertTrue(text[3].startswith('NameError'))
530
531    def test_non_ascii(self):
532        # Mac OS X denies the creation of a file with an invalid UTF-8 name.
533        # Windows allows creating a name with an arbitrary bytes name, but
534        # Python cannot a undecodable bytes argument to a subprocess.
535        if (support.TESTFN_UNDECODABLE
536        and sys.platform not in ('win32', 'darwin')):
537            name = os.fsdecode(support.TESTFN_UNDECODABLE)
538        elif support.TESTFN_NONASCII:
539            name = support.TESTFN_NONASCII
540        else:
541            self.skipTest("need support.TESTFN_NONASCII")
542
543        # Issue #16218
544        source = 'print(ascii(__file__))\n'
545        script_name = _make_test_script(os.curdir, name, source)
546        self.addCleanup(support.unlink, script_name)
547        rc, stdout, stderr = assert_python_ok(script_name)
548        self.assertEqual(
549            ascii(script_name),
550            stdout.rstrip().decode('ascii'),
551            'stdout=%r stderr=%r' % (stdout, stderr))
552        self.assertEqual(0, rc)
553
554    def test_issue20500_exit_with_exception_value(self):
555        script = textwrap.dedent("""\
556            import sys
557            error = None
558            try:
559                raise ValueError('some text')
560            except ValueError as err:
561                error = err
562
563            if error:
564                sys.exit(error)
565            """)
566        with support.temp_dir() as script_dir:
567            script_name = _make_test_script(script_dir, 'script', script)
568            exitcode, stdout, stderr = assert_python_failure(script_name)
569            text = stderr.decode('ascii')
570            self.assertEqual(text, "some text")
571
572    def test_syntaxerror_unindented_caret_position(self):
573        script = "1 + 1 = 2\n"
574        with support.temp_dir() as script_dir:
575            script_name = _make_test_script(script_dir, 'script', script)
576            exitcode, stdout, stderr = assert_python_failure(script_name)
577            text = io.TextIOWrapper(io.BytesIO(stderr), 'ascii').read()
578            # Confirm that the caret is located under the first 1 character
579            self.assertIn("\n    1 + 1 = 2\n    ^", text)
580
581    def test_syntaxerror_indented_caret_position(self):
582        script = textwrap.dedent("""\
583            if True:
584                1 + 1 = 2
585            """)
586        with support.temp_dir() as script_dir:
587            script_name = _make_test_script(script_dir, 'script', script)
588            exitcode, stdout, stderr = assert_python_failure(script_name)
589            text = io.TextIOWrapper(io.BytesIO(stderr), 'ascii').read()
590            # Confirm that the caret is located under the first 1 character
591            self.assertIn("\n    1 + 1 = 2\n    ^", text)
592
593            # Try the same with a form feed at the start of the indented line
594            script = (
595                "if True:\n"
596                "\f    1 + 1 = 2\n"
597            )
598            script_name = _make_test_script(script_dir, "script", script)
599            exitcode, stdout, stderr = assert_python_failure(script_name)
600            text = io.TextIOWrapper(io.BytesIO(stderr), "ascii").read()
601            self.assertNotIn("\f", text)
602            self.assertIn("\n    1 + 1 = 2\n    ^", text)
603
604    def test_syntaxerror_multi_line_fstring(self):
605        script = 'foo = f"""{}\nfoo"""\n'
606        with support.temp_dir() as script_dir:
607            script_name = _make_test_script(script_dir, 'script', script)
608            exitcode, stdout, stderr = assert_python_failure(script_name)
609            self.assertEqual(
610                stderr.splitlines()[-3:],
611                [
612                    b'    foo = f"""{}',
613                    b'          ^',
614                    b'SyntaxError: f-string: empty expression not allowed',
615                ],
616            )
617
618    def test_syntaxerror_invalid_escape_sequence_multi_line(self):
619        script = 'foo = """\\q\n"""\n'
620        with support.temp_dir() as script_dir:
621            script_name = _make_test_script(script_dir, 'script', script)
622            exitcode, stdout, stderr = assert_python_failure(
623                '-Werror', script_name,
624            )
625            self.assertEqual(
626                stderr.splitlines()[-3:],
627                [
628                    b'    foo = """\\q',
629                    b'          ^',
630                    b'SyntaxError: invalid escape sequence \\q',
631                ],
632            )
633
634    def test_consistent_sys_path_for_direct_execution(self):
635        # This test case ensures that the following all give the same
636        # sys.path configuration:
637        #
638        #    ./python -s script_dir/__main__.py
639        #    ./python -s script_dir
640        #    ./python -I script_dir
641        script = textwrap.dedent("""\
642            import sys
643            for entry in sys.path:
644                print(entry)
645            """)
646        # Always show full path diffs on errors
647        self.maxDiff = None
648        with support.temp_dir() as work_dir, support.temp_dir() as script_dir:
649            script_name = _make_test_script(script_dir, '__main__', script)
650            # Reference output comes from directly executing __main__.py
651            # We omit PYTHONPATH and user site to align with isolated mode
652            p = spawn_python("-Es", script_name, cwd=work_dir)
653            out_by_name = kill_python(p).decode().splitlines()
654            self.assertEqual(out_by_name[0], script_dir)
655            self.assertNotIn(work_dir, out_by_name)
656            # Directory execution should give the same output
657            p = spawn_python("-Es", script_dir, cwd=work_dir)
658            out_by_dir = kill_python(p).decode().splitlines()
659            self.assertEqual(out_by_dir, out_by_name)
660            # As should directory execution in isolated mode
661            p = spawn_python("-I", script_dir, cwd=work_dir)
662            out_by_dir_isolated = kill_python(p).decode().splitlines()
663            self.assertEqual(out_by_dir_isolated, out_by_dir, out_by_name)
664
665    def test_consistent_sys_path_for_module_execution(self):
666        # This test case ensures that the following both give the same
667        # sys.path configuration:
668        #    ./python -sm script_pkg.__main__
669        #    ./python -sm script_pkg
670        #
671        # And that this fails as unable to find the package:
672        #    ./python -Im script_pkg
673        script = textwrap.dedent("""\
674            import sys
675            for entry in sys.path:
676                print(entry)
677            """)
678        # Always show full path diffs on errors
679        self.maxDiff = None
680        with support.temp_dir() as work_dir:
681            script_dir = os.path.join(work_dir, "script_pkg")
682            os.mkdir(script_dir)
683            script_name = _make_test_script(script_dir, '__main__', script)
684            # Reference output comes from `-m script_pkg.__main__`
685            # We omit PYTHONPATH and user site to better align with the
686            # direct execution test cases
687            p = spawn_python("-sm", "script_pkg.__main__", cwd=work_dir)
688            out_by_module = kill_python(p).decode().splitlines()
689            self.assertEqual(out_by_module[0], work_dir)
690            self.assertNotIn(script_dir, out_by_module)
691            # Package execution should give the same output
692            p = spawn_python("-sm", "script_pkg", cwd=work_dir)
693            out_by_package = kill_python(p).decode().splitlines()
694            self.assertEqual(out_by_package, out_by_module)
695            # Isolated mode should fail with an import error
696            exitcode, stdout, stderr = assert_python_failure(
697                "-Im", "script_pkg", cwd=work_dir
698            )
699            traceback_lines = stderr.decode().splitlines()
700            self.assertIn("No module named script_pkg", traceback_lines[-1])
701
702    def test_nonexisting_script(self):
703        # bpo-34783: "./python script.py" must not crash
704        # if the script file doesn't exist.
705        # (Skip test for macOS framework builds because sys.excutable name
706        #  is not the actual Python executable file name.
707        script = 'nonexistingscript.py'
708        self.assertFalse(os.path.exists(script))
709
710        proc = spawn_python(script, text=True,
711                            stdout=subprocess.PIPE,
712                            stderr=subprocess.PIPE)
713        out, err = proc.communicate()
714        self.assertIn(": can't open file ", err)
715        self.assertNotEqual(proc.returncode, 0)
716
717
718def test_main():
719    support.run_unittest(CmdLineTest)
720    support.reap_children()
721
722if __name__ == '__main__':
723    test_main()
724