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