1# Test the runpy module 2import contextlib 3import importlib.machinery, importlib.util 4import os.path 5import pathlib 6import py_compile 7import re 8import signal 9import subprocess 10import sys 11import tempfile 12import textwrap 13import unittest 14import warnings 15from test.support import ( 16 forget, make_legacy_pyc, unload, verbose, no_tracing, 17 create_empty_file, temp_dir) 18from test.support.script_helper import make_script, make_zip_script 19 20 21import runpy 22from runpy import _run_code, _run_module_code, run_module, run_path 23# Note: This module can't safely test _run_module_as_main as it 24# runs its tests in the current process, which would mess with the 25# real __main__ module (usually test.regrtest) 26# See test_cmd_line_script for a test that executes that code path 27 28 29# Set up the test code and expected results 30example_source = """\ 31# Check basic code execution 32result = ['Top level assignment'] 33def f(): 34 result.append('Lower level reference') 35f() 36del f 37# Check the sys module 38import sys 39run_argv0 = sys.argv[0] 40run_name_in_sys_modules = __name__ in sys.modules 41module_in_sys_modules = (run_name_in_sys_modules and 42 globals() is sys.modules[__name__].__dict__) 43# Check nested operation 44import runpy 45nested = runpy._run_module_code('x=1\\n', mod_name='<run>') 46""" 47 48implicit_namespace = { 49 "__name__": None, 50 "__file__": None, 51 "__cached__": None, 52 "__package__": None, 53 "__doc__": None, 54 "__spec__": None 55} 56example_namespace = { 57 "sys": sys, 58 "runpy": runpy, 59 "result": ["Top level assignment", "Lower level reference"], 60 "run_argv0": sys.argv[0], 61 "run_name_in_sys_modules": False, 62 "module_in_sys_modules": False, 63 "nested": dict(implicit_namespace, 64 x=1, __name__="<run>", __loader__=None), 65} 66example_namespace.update(implicit_namespace) 67 68class CodeExecutionMixin: 69 # Issue #15230 (run_path not handling run_name correctly) highlighted a 70 # problem with the way arguments were being passed from higher level APIs 71 # down to lower level code. This mixin makes it easier to ensure full 72 # testing occurs at those upper layers as well, not just at the utility 73 # layer 74 75 # Figuring out the loader details in advance is hard to do, so we skip 76 # checking the full details of loader and loader_state 77 CHECKED_SPEC_ATTRIBUTES = ["name", "parent", "origin", "cached", 78 "has_location", "submodule_search_locations"] 79 80 def assertNamespaceMatches(self, result_ns, expected_ns): 81 """Check two namespaces match. 82 83 Ignores any unspecified interpreter created names 84 """ 85 # Avoid side effects 86 result_ns = result_ns.copy() 87 expected_ns = expected_ns.copy() 88 # Impls are permitted to add extra names, so filter them out 89 for k in list(result_ns): 90 if k.startswith("__") and k.endswith("__"): 91 if k not in expected_ns: 92 result_ns.pop(k) 93 if k not in expected_ns["nested"]: 94 result_ns["nested"].pop(k) 95 # Spec equality includes the loader, so we take the spec out of the 96 # result namespace and check that separately 97 result_spec = result_ns.pop("__spec__") 98 expected_spec = expected_ns.pop("__spec__") 99 if expected_spec is None: 100 self.assertIsNone(result_spec) 101 else: 102 # If an expected loader is set, we just check we got the right 103 # type, rather than checking for full equality 104 if expected_spec.loader is not None: 105 self.assertEqual(type(result_spec.loader), 106 type(expected_spec.loader)) 107 for attr in self.CHECKED_SPEC_ATTRIBUTES: 108 k = "__spec__." + attr 109 actual = (k, getattr(result_spec, attr)) 110 expected = (k, getattr(expected_spec, attr)) 111 self.assertEqual(actual, expected) 112 # For the rest, we still don't use direct dict comparison on the 113 # namespace, as the diffs are too hard to debug if anything breaks 114 self.assertEqual(set(result_ns), set(expected_ns)) 115 for k in result_ns: 116 actual = (k, result_ns[k]) 117 expected = (k, expected_ns[k]) 118 self.assertEqual(actual, expected) 119 120 def check_code_execution(self, create_namespace, expected_namespace): 121 """Check that an interface runs the example code correctly 122 123 First argument is a callable accepting the initial globals and 124 using them to create the actual namespace 125 Second argument is the expected result 126 """ 127 sentinel = object() 128 expected_ns = expected_namespace.copy() 129 run_name = expected_ns["__name__"] 130 saved_argv0 = sys.argv[0] 131 saved_mod = sys.modules.get(run_name, sentinel) 132 # Check without initial globals 133 result_ns = create_namespace(None) 134 self.assertNamespaceMatches(result_ns, expected_ns) 135 self.assertIs(sys.argv[0], saved_argv0) 136 self.assertIs(sys.modules.get(run_name, sentinel), saved_mod) 137 # And then with initial globals 138 initial_ns = {"sentinel": sentinel} 139 expected_ns["sentinel"] = sentinel 140 result_ns = create_namespace(initial_ns) 141 self.assertIsNot(result_ns, initial_ns) 142 self.assertNamespaceMatches(result_ns, expected_ns) 143 self.assertIs(sys.argv[0], saved_argv0) 144 self.assertIs(sys.modules.get(run_name, sentinel), saved_mod) 145 146 147class ExecutionLayerTestCase(unittest.TestCase, CodeExecutionMixin): 148 """Unit tests for runpy._run_code and runpy._run_module_code""" 149 150 def test_run_code(self): 151 expected_ns = example_namespace.copy() 152 expected_ns.update({ 153 "__loader__": None, 154 }) 155 def create_ns(init_globals): 156 return _run_code(example_source, {}, init_globals) 157 self.check_code_execution(create_ns, expected_ns) 158 159 def test_run_module_code(self): 160 mod_name = "<Nonsense>" 161 mod_fname = "Some other nonsense" 162 mod_loader = "Now you're just being silly" 163 mod_package = '' # Treat as a top level module 164 mod_spec = importlib.machinery.ModuleSpec(mod_name, 165 origin=mod_fname, 166 loader=mod_loader) 167 expected_ns = example_namespace.copy() 168 expected_ns.update({ 169 "__name__": mod_name, 170 "__file__": mod_fname, 171 "__loader__": mod_loader, 172 "__package__": mod_package, 173 "__spec__": mod_spec, 174 "run_argv0": mod_fname, 175 "run_name_in_sys_modules": True, 176 "module_in_sys_modules": True, 177 }) 178 def create_ns(init_globals): 179 return _run_module_code(example_source, 180 init_globals, 181 mod_name, 182 mod_spec) 183 self.check_code_execution(create_ns, expected_ns) 184 185# TODO: Use self.addCleanup to get rid of a lot of try-finally blocks 186class RunModuleTestCase(unittest.TestCase, CodeExecutionMixin): 187 """Unit tests for runpy.run_module""" 188 189 def expect_import_error(self, mod_name): 190 try: 191 run_module(mod_name) 192 except ImportError: 193 pass 194 else: 195 self.fail("Expected import error for " + mod_name) 196 197 def test_invalid_names(self): 198 # Builtin module 199 self.expect_import_error("sys") 200 # Non-existent modules 201 self.expect_import_error("sys.imp.eric") 202 self.expect_import_error("os.path.half") 203 self.expect_import_error("a.bee") 204 # Relative names not allowed 205 self.expect_import_error(".howard") 206 self.expect_import_error("..eaten") 207 self.expect_import_error(".test_runpy") 208 self.expect_import_error(".unittest") 209 # Package without __main__.py 210 self.expect_import_error("multiprocessing") 211 212 def test_library_module(self): 213 self.assertEqual(run_module("runpy")["__name__"], "runpy") 214 215 def _add_pkg_dir(self, pkg_dir, namespace=False): 216 os.mkdir(pkg_dir) 217 if namespace: 218 return None 219 pkg_fname = os.path.join(pkg_dir, "__init__.py") 220 create_empty_file(pkg_fname) 221 return pkg_fname 222 223 def _make_pkg(self, source, depth, mod_base="runpy_test", 224 *, namespace=False, parent_namespaces=False): 225 # Enforce a couple of internal sanity checks on test cases 226 if (namespace or parent_namespaces) and not depth: 227 raise RuntimeError("Can't mark top level module as a " 228 "namespace package") 229 pkg_name = "__runpy_pkg__" 230 test_fname = mod_base+os.extsep+"py" 231 pkg_dir = sub_dir = os.path.realpath(tempfile.mkdtemp()) 232 if verbose > 1: print(" Package tree in:", sub_dir) 233 sys.path.insert(0, pkg_dir) 234 if verbose > 1: print(" Updated sys.path:", sys.path[0]) 235 if depth: 236 namespace_flags = [parent_namespaces] * depth 237 namespace_flags[-1] = namespace 238 for namespace_flag in namespace_flags: 239 sub_dir = os.path.join(sub_dir, pkg_name) 240 pkg_fname = self._add_pkg_dir(sub_dir, namespace_flag) 241 if verbose > 1: print(" Next level in:", sub_dir) 242 if verbose > 1: print(" Created:", pkg_fname) 243 mod_fname = os.path.join(sub_dir, test_fname) 244 with open(mod_fname, "w") as mod_file: 245 mod_file.write(source) 246 if verbose > 1: print(" Created:", mod_fname) 247 mod_name = (pkg_name+".")*depth + mod_base 248 mod_spec = importlib.util.spec_from_file_location(mod_name, 249 mod_fname) 250 return pkg_dir, mod_fname, mod_name, mod_spec 251 252 def _del_pkg(self, top): 253 for entry in list(sys.modules): 254 if entry.startswith("__runpy_pkg__"): 255 del sys.modules[entry] 256 if verbose > 1: print(" Removed sys.modules entries") 257 del sys.path[0] 258 if verbose > 1: print(" Removed sys.path entry") 259 for root, dirs, files in os.walk(top, topdown=False): 260 for name in files: 261 try: 262 os.remove(os.path.join(root, name)) 263 except OSError as ex: 264 if verbose > 1: print(ex) # Persist with cleaning up 265 for name in dirs: 266 fullname = os.path.join(root, name) 267 try: 268 os.rmdir(fullname) 269 except OSError as ex: 270 if verbose > 1: print(ex) # Persist with cleaning up 271 try: 272 os.rmdir(top) 273 if verbose > 1: print(" Removed package tree") 274 except OSError as ex: 275 if verbose > 1: print(ex) # Persist with cleaning up 276 277 def _fix_ns_for_legacy_pyc(self, ns, alter_sys): 278 char_to_add = "c" 279 ns["__file__"] += char_to_add 280 ns["__cached__"] = ns["__file__"] 281 spec = ns["__spec__"] 282 new_spec = importlib.util.spec_from_file_location(spec.name, 283 ns["__file__"]) 284 ns["__spec__"] = new_spec 285 if alter_sys: 286 ns["run_argv0"] += char_to_add 287 288 289 def _check_module(self, depth, alter_sys=False, 290 *, namespace=False, parent_namespaces=False): 291 pkg_dir, mod_fname, mod_name, mod_spec = ( 292 self._make_pkg(example_source, depth, 293 namespace=namespace, 294 parent_namespaces=parent_namespaces)) 295 forget(mod_name) 296 expected_ns = example_namespace.copy() 297 expected_ns.update({ 298 "__name__": mod_name, 299 "__file__": mod_fname, 300 "__cached__": mod_spec.cached, 301 "__package__": mod_name.rpartition(".")[0], 302 "__spec__": mod_spec, 303 }) 304 if alter_sys: 305 expected_ns.update({ 306 "run_argv0": mod_fname, 307 "run_name_in_sys_modules": True, 308 "module_in_sys_modules": True, 309 }) 310 def create_ns(init_globals): 311 return run_module(mod_name, init_globals, alter_sys=alter_sys) 312 try: 313 if verbose > 1: print("Running from source:", mod_name) 314 self.check_code_execution(create_ns, expected_ns) 315 importlib.invalidate_caches() 316 __import__(mod_name) 317 os.remove(mod_fname) 318 if not sys.dont_write_bytecode: 319 make_legacy_pyc(mod_fname) 320 unload(mod_name) # In case loader caches paths 321 importlib.invalidate_caches() 322 if verbose > 1: print("Running from compiled:", mod_name) 323 self._fix_ns_for_legacy_pyc(expected_ns, alter_sys) 324 self.check_code_execution(create_ns, expected_ns) 325 finally: 326 self._del_pkg(pkg_dir) 327 if verbose > 1: print("Module executed successfully") 328 329 def _check_package(self, depth, alter_sys=False, 330 *, namespace=False, parent_namespaces=False): 331 pkg_dir, mod_fname, mod_name, mod_spec = ( 332 self._make_pkg(example_source, depth, "__main__", 333 namespace=namespace, 334 parent_namespaces=parent_namespaces)) 335 pkg_name = mod_name.rpartition(".")[0] 336 forget(mod_name) 337 expected_ns = example_namespace.copy() 338 expected_ns.update({ 339 "__name__": mod_name, 340 "__file__": mod_fname, 341 "__cached__": importlib.util.cache_from_source(mod_fname), 342 "__package__": pkg_name, 343 "__spec__": mod_spec, 344 }) 345 if alter_sys: 346 expected_ns.update({ 347 "run_argv0": mod_fname, 348 "run_name_in_sys_modules": True, 349 "module_in_sys_modules": True, 350 }) 351 def create_ns(init_globals): 352 return run_module(pkg_name, init_globals, alter_sys=alter_sys) 353 try: 354 if verbose > 1: print("Running from source:", pkg_name) 355 self.check_code_execution(create_ns, expected_ns) 356 importlib.invalidate_caches() 357 __import__(mod_name) 358 os.remove(mod_fname) 359 if not sys.dont_write_bytecode: 360 make_legacy_pyc(mod_fname) 361 unload(mod_name) # In case loader caches paths 362 if verbose > 1: print("Running from compiled:", pkg_name) 363 importlib.invalidate_caches() 364 self._fix_ns_for_legacy_pyc(expected_ns, alter_sys) 365 self.check_code_execution(create_ns, expected_ns) 366 finally: 367 self._del_pkg(pkg_dir) 368 if verbose > 1: print("Package executed successfully") 369 370 def _add_relative_modules(self, base_dir, source, depth): 371 if depth <= 1: 372 raise ValueError("Relative module test needs depth > 1") 373 pkg_name = "__runpy_pkg__" 374 module_dir = base_dir 375 for i in range(depth): 376 parent_dir = module_dir 377 module_dir = os.path.join(module_dir, pkg_name) 378 # Add sibling module 379 sibling_fname = os.path.join(module_dir, "sibling.py") 380 create_empty_file(sibling_fname) 381 if verbose > 1: print(" Added sibling module:", sibling_fname) 382 # Add nephew module 383 uncle_dir = os.path.join(parent_dir, "uncle") 384 self._add_pkg_dir(uncle_dir) 385 if verbose > 1: print(" Added uncle package:", uncle_dir) 386 cousin_dir = os.path.join(uncle_dir, "cousin") 387 self._add_pkg_dir(cousin_dir) 388 if verbose > 1: print(" Added cousin package:", cousin_dir) 389 nephew_fname = os.path.join(cousin_dir, "nephew.py") 390 create_empty_file(nephew_fname) 391 if verbose > 1: print(" Added nephew module:", nephew_fname) 392 393 def _check_relative_imports(self, depth, run_name=None): 394 contents = r"""\ 395from __future__ import absolute_import 396from . import sibling 397from ..uncle.cousin import nephew 398""" 399 pkg_dir, mod_fname, mod_name, mod_spec = ( 400 self._make_pkg(contents, depth)) 401 if run_name is None: 402 expected_name = mod_name 403 else: 404 expected_name = run_name 405 try: 406 self._add_relative_modules(pkg_dir, contents, depth) 407 pkg_name = mod_name.rpartition('.')[0] 408 if verbose > 1: print("Running from source:", mod_name) 409 d1 = run_module(mod_name, run_name=run_name) # Read from source 410 self.assertEqual(d1["__name__"], expected_name) 411 self.assertEqual(d1["__package__"], pkg_name) 412 self.assertIn("sibling", d1) 413 self.assertIn("nephew", d1) 414 del d1 # Ensure __loader__ entry doesn't keep file open 415 importlib.invalidate_caches() 416 __import__(mod_name) 417 os.remove(mod_fname) 418 if not sys.dont_write_bytecode: 419 make_legacy_pyc(mod_fname) 420 unload(mod_name) # In case the loader caches paths 421 if verbose > 1: print("Running from compiled:", mod_name) 422 importlib.invalidate_caches() 423 d2 = run_module(mod_name, run_name=run_name) # Read from bytecode 424 self.assertEqual(d2["__name__"], expected_name) 425 self.assertEqual(d2["__package__"], pkg_name) 426 self.assertIn("sibling", d2) 427 self.assertIn("nephew", d2) 428 del d2 # Ensure __loader__ entry doesn't keep file open 429 finally: 430 self._del_pkg(pkg_dir) 431 if verbose > 1: print("Module executed successfully") 432 433 def test_run_module(self): 434 for depth in range(4): 435 if verbose > 1: print("Testing package depth:", depth) 436 self._check_module(depth) 437 438 def test_run_module_in_namespace_package(self): 439 for depth in range(1, 4): 440 if verbose > 1: print("Testing package depth:", depth) 441 self._check_module(depth, namespace=True, parent_namespaces=True) 442 443 def test_run_package(self): 444 for depth in range(1, 4): 445 if verbose > 1: print("Testing package depth:", depth) 446 self._check_package(depth) 447 448 def test_run_package_init_exceptions(self): 449 # These were previously wrapped in an ImportError; see Issue 14285 450 result = self._make_pkg("", 1, "__main__") 451 pkg_dir, _, mod_name, _ = result 452 mod_name = mod_name.replace(".__main__", "") 453 self.addCleanup(self._del_pkg, pkg_dir) 454 init = os.path.join(pkg_dir, "__runpy_pkg__", "__init__.py") 455 456 exceptions = (ImportError, AttributeError, TypeError, ValueError) 457 for exception in exceptions: 458 name = exception.__name__ 459 with self.subTest(name): 460 source = "raise {0}('{0} in __init__.py.')".format(name) 461 with open(init, "wt", encoding="ascii") as mod_file: 462 mod_file.write(source) 463 try: 464 run_module(mod_name) 465 except exception as err: 466 self.assertNotIn("finding spec", format(err)) 467 else: 468 self.fail("Nothing raised; expected {}".format(name)) 469 try: 470 run_module(mod_name + ".submodule") 471 except exception as err: 472 self.assertNotIn("finding spec", format(err)) 473 else: 474 self.fail("Nothing raised; expected {}".format(name)) 475 476 def test_submodule_imported_warning(self): 477 pkg_dir, _, mod_name, _ = self._make_pkg("", 1) 478 try: 479 __import__(mod_name) 480 with self.assertWarnsRegex(RuntimeWarning, 481 r"found in sys\.modules"): 482 run_module(mod_name) 483 finally: 484 self._del_pkg(pkg_dir) 485 486 def test_package_imported_no_warning(self): 487 pkg_dir, _, mod_name, _ = self._make_pkg("", 1, "__main__") 488 self.addCleanup(self._del_pkg, pkg_dir) 489 package = mod_name.replace(".__main__", "") 490 # No warning should occur if we only imported the parent package 491 __import__(package) 492 self.assertIn(package, sys.modules) 493 with warnings.catch_warnings(): 494 warnings.simplefilter("error", RuntimeWarning) 495 run_module(package) 496 # But the warning should occur if we imported the __main__ submodule 497 __import__(mod_name) 498 with self.assertWarnsRegex(RuntimeWarning, r"found in sys\.modules"): 499 run_module(package) 500 501 def test_run_package_in_namespace_package(self): 502 for depth in range(1, 4): 503 if verbose > 1: print("Testing package depth:", depth) 504 self._check_package(depth, parent_namespaces=True) 505 506 def test_run_namespace_package(self): 507 for depth in range(1, 4): 508 if verbose > 1: print("Testing package depth:", depth) 509 self._check_package(depth, namespace=True) 510 511 def test_run_namespace_package_in_namespace_package(self): 512 for depth in range(1, 4): 513 if verbose > 1: print("Testing package depth:", depth) 514 self._check_package(depth, namespace=True, parent_namespaces=True) 515 516 def test_run_module_alter_sys(self): 517 for depth in range(4): 518 if verbose > 1: print("Testing package depth:", depth) 519 self._check_module(depth, alter_sys=True) 520 521 def test_run_package_alter_sys(self): 522 for depth in range(1, 4): 523 if verbose > 1: print("Testing package depth:", depth) 524 self._check_package(depth, alter_sys=True) 525 526 def test_explicit_relative_import(self): 527 for depth in range(2, 5): 528 if verbose > 1: print("Testing relative imports at depth:", depth) 529 self._check_relative_imports(depth) 530 531 def test_main_relative_import(self): 532 for depth in range(2, 5): 533 if verbose > 1: print("Testing main relative imports at depth:", depth) 534 self._check_relative_imports(depth, "__main__") 535 536 def test_run_name(self): 537 depth = 1 538 run_name = "And now for something completely different" 539 pkg_dir, mod_fname, mod_name, mod_spec = ( 540 self._make_pkg(example_source, depth)) 541 forget(mod_name) 542 expected_ns = example_namespace.copy() 543 expected_ns.update({ 544 "__name__": run_name, 545 "__file__": mod_fname, 546 "__cached__": importlib.util.cache_from_source(mod_fname), 547 "__package__": mod_name.rpartition(".")[0], 548 "__spec__": mod_spec, 549 }) 550 def create_ns(init_globals): 551 return run_module(mod_name, init_globals, run_name) 552 try: 553 self.check_code_execution(create_ns, expected_ns) 554 finally: 555 self._del_pkg(pkg_dir) 556 557 def test_pkgutil_walk_packages(self): 558 # This is a dodgy hack to use the test_runpy infrastructure to test 559 # issue #15343. Issue #15348 declares this is indeed a dodgy hack ;) 560 import pkgutil 561 max_depth = 4 562 base_name = "__runpy_pkg__" 563 package_suffixes = ["uncle", "uncle.cousin"] 564 module_suffixes = ["uncle.cousin.nephew", base_name + ".sibling"] 565 expected_packages = set() 566 expected_modules = set() 567 for depth in range(1, max_depth): 568 pkg_name = ".".join([base_name] * depth) 569 expected_packages.add(pkg_name) 570 for name in package_suffixes: 571 expected_packages.add(pkg_name + "." + name) 572 for name in module_suffixes: 573 expected_modules.add(pkg_name + "." + name) 574 pkg_name = ".".join([base_name] * max_depth) 575 expected_packages.add(pkg_name) 576 expected_modules.add(pkg_name + ".runpy_test") 577 pkg_dir, mod_fname, mod_name, mod_spec = ( 578 self._make_pkg("", max_depth)) 579 self.addCleanup(self._del_pkg, pkg_dir) 580 for depth in range(2, max_depth+1): 581 self._add_relative_modules(pkg_dir, "", depth) 582 for moduleinfo in pkgutil.walk_packages([pkg_dir]): 583 self.assertIsInstance(moduleinfo, pkgutil.ModuleInfo) 584 self.assertIsInstance(moduleinfo.module_finder, 585 importlib.machinery.FileFinder) 586 if moduleinfo.ispkg: 587 expected_packages.remove(moduleinfo.name) 588 else: 589 expected_modules.remove(moduleinfo.name) 590 self.assertEqual(len(expected_packages), 0, expected_packages) 591 self.assertEqual(len(expected_modules), 0, expected_modules) 592 593class RunPathTestCase(unittest.TestCase, CodeExecutionMixin): 594 """Unit tests for runpy.run_path""" 595 596 def _make_test_script(self, script_dir, script_basename, 597 source=None, omit_suffix=False): 598 if source is None: 599 source = example_source 600 return make_script(script_dir, script_basename, 601 source, omit_suffix) 602 603 def _check_script(self, script_name, expected_name, expected_file, 604 expected_argv0, mod_name=None, 605 expect_spec=True, check_loader=True): 606 # First check is without run_name 607 def create_ns(init_globals): 608 return run_path(script_name, init_globals) 609 expected_ns = example_namespace.copy() 610 if mod_name is None: 611 spec_name = expected_name 612 else: 613 spec_name = mod_name 614 if expect_spec: 615 mod_spec = importlib.util.spec_from_file_location(spec_name, 616 expected_file) 617 mod_cached = mod_spec.cached 618 if not check_loader: 619 mod_spec.loader = None 620 else: 621 mod_spec = mod_cached = None 622 623 expected_ns.update({ 624 "__name__": expected_name, 625 "__file__": expected_file, 626 "__cached__": mod_cached, 627 "__package__": "", 628 "__spec__": mod_spec, 629 "run_argv0": expected_argv0, 630 "run_name_in_sys_modules": True, 631 "module_in_sys_modules": True, 632 }) 633 self.check_code_execution(create_ns, expected_ns) 634 # Second check makes sure run_name works in all cases 635 run_name = "prove.issue15230.is.fixed" 636 def create_ns(init_globals): 637 return run_path(script_name, init_globals, run_name) 638 if expect_spec and mod_name is None: 639 mod_spec = importlib.util.spec_from_file_location(run_name, 640 expected_file) 641 if not check_loader: 642 mod_spec.loader = None 643 expected_ns["__spec__"] = mod_spec 644 expected_ns["__name__"] = run_name 645 expected_ns["__package__"] = run_name.rpartition(".")[0] 646 self.check_code_execution(create_ns, expected_ns) 647 648 def _check_import_error(self, script_name, msg): 649 msg = re.escape(msg) 650 self.assertRaisesRegex(ImportError, msg, run_path, script_name) 651 652 def test_basic_script(self): 653 with temp_dir() as script_dir: 654 mod_name = 'script' 655 script_name = self._make_test_script(script_dir, mod_name) 656 self._check_script(script_name, "<run_path>", script_name, 657 script_name, expect_spec=False) 658 659 def test_basic_script_with_path_object(self): 660 with temp_dir() as script_dir: 661 mod_name = 'script' 662 script_name = pathlib.Path(self._make_test_script(script_dir, 663 mod_name)) 664 self._check_script(script_name, "<run_path>", script_name, 665 script_name, expect_spec=False) 666 667 def test_basic_script_no_suffix(self): 668 with temp_dir() as script_dir: 669 mod_name = 'script' 670 script_name = self._make_test_script(script_dir, mod_name, 671 omit_suffix=True) 672 self._check_script(script_name, "<run_path>", script_name, 673 script_name, expect_spec=False) 674 675 def test_script_compiled(self): 676 with temp_dir() as script_dir: 677 mod_name = 'script' 678 script_name = self._make_test_script(script_dir, mod_name) 679 compiled_name = py_compile.compile(script_name, doraise=True) 680 os.remove(script_name) 681 self._check_script(compiled_name, "<run_path>", compiled_name, 682 compiled_name, expect_spec=False) 683 684 def test_directory(self): 685 with temp_dir() as script_dir: 686 mod_name = '__main__' 687 script_name = self._make_test_script(script_dir, mod_name) 688 self._check_script(script_dir, "<run_path>", script_name, 689 script_dir, mod_name=mod_name) 690 691 def test_directory_compiled(self): 692 with temp_dir() as script_dir: 693 mod_name = '__main__' 694 script_name = self._make_test_script(script_dir, mod_name) 695 compiled_name = py_compile.compile(script_name, doraise=True) 696 os.remove(script_name) 697 if not sys.dont_write_bytecode: 698 legacy_pyc = make_legacy_pyc(script_name) 699 self._check_script(script_dir, "<run_path>", legacy_pyc, 700 script_dir, mod_name=mod_name) 701 702 def test_directory_error(self): 703 with temp_dir() as script_dir: 704 mod_name = 'not_main' 705 script_name = self._make_test_script(script_dir, mod_name) 706 msg = "can't find '__main__' module in %r" % script_dir 707 self._check_import_error(script_dir, msg) 708 709 def test_zipfile(self): 710 with temp_dir() as script_dir: 711 mod_name = '__main__' 712 script_name = self._make_test_script(script_dir, mod_name) 713 zip_name, fname = make_zip_script(script_dir, 'test_zip', script_name) 714 self._check_script(zip_name, "<run_path>", fname, zip_name, 715 mod_name=mod_name, check_loader=False) 716 717 def test_zipfile_compiled(self): 718 with temp_dir() as script_dir: 719 mod_name = '__main__' 720 script_name = self._make_test_script(script_dir, mod_name) 721 compiled_name = py_compile.compile(script_name, doraise=True) 722 zip_name, fname = make_zip_script(script_dir, 'test_zip', 723 compiled_name) 724 self._check_script(zip_name, "<run_path>", fname, zip_name, 725 mod_name=mod_name, check_loader=False) 726 727 def test_zipfile_error(self): 728 with temp_dir() as script_dir: 729 mod_name = 'not_main' 730 script_name = self._make_test_script(script_dir, mod_name) 731 zip_name, fname = make_zip_script(script_dir, 'test_zip', script_name) 732 msg = "can't find '__main__' module in %r" % zip_name 733 self._check_import_error(zip_name, msg) 734 735 @no_tracing 736 def test_main_recursion_error(self): 737 with temp_dir() as script_dir, temp_dir() as dummy_dir: 738 mod_name = '__main__' 739 source = ("import runpy\n" 740 "runpy.run_path(%r)\n") % dummy_dir 741 script_name = self._make_test_script(script_dir, mod_name, source) 742 zip_name, fname = make_zip_script(script_dir, 'test_zip', script_name) 743 msg = "recursion depth exceeded" 744 self.assertRaisesRegex(RecursionError, msg, run_path, zip_name) 745 746 def test_encoding(self): 747 with temp_dir() as script_dir: 748 filename = os.path.join(script_dir, 'script.py') 749 with open(filename, 'w', encoding='latin1') as f: 750 f.write(""" 751#coding:latin1 752s = "non-ASCII: h\xe9" 753""") 754 result = run_path(filename) 755 self.assertEqual(result['s'], "non-ASCII: h\xe9") 756 757 758class TestExit(unittest.TestCase): 759 STATUS_CONTROL_C_EXIT = 0xC000013A 760 EXPECTED_CODE = ( 761 STATUS_CONTROL_C_EXIT 762 if sys.platform == "win32" 763 else -signal.SIGINT 764 ) 765 @staticmethod 766 @contextlib.contextmanager 767 def tmp_path(*args, **kwargs): 768 with temp_dir() as tmp_fn: 769 yield pathlib.Path(tmp_fn) 770 771 772 def run(self, *args, **kwargs): 773 with self.tmp_path() as tmp: 774 self.ham = ham = tmp / "ham.py" 775 ham.write_text( 776 textwrap.dedent( 777 """\ 778 raise KeyboardInterrupt 779 """ 780 ) 781 ) 782 super().run(*args, **kwargs) 783 784 def assertSigInt(self, *args, **kwargs): 785 proc = subprocess.run(*args, **kwargs, text=True, stderr=subprocess.PIPE) 786 self.assertTrue(proc.stderr.endswith("\nKeyboardInterrupt\n")) 787 self.assertEqual(proc.returncode, self.EXPECTED_CODE) 788 789 def test_pymain_run_file(self): 790 self.assertSigInt([sys.executable, self.ham]) 791 792 def test_pymain_run_file_runpy_run_module(self): 793 tmp = self.ham.parent 794 run_module = tmp / "run_module.py" 795 run_module.write_text( 796 textwrap.dedent( 797 """\ 798 import runpy 799 runpy.run_module("ham") 800 """ 801 ) 802 ) 803 self.assertSigInt([sys.executable, run_module], cwd=tmp) 804 805 def test_pymain_run_file_runpy_run_module_as_main(self): 806 tmp = self.ham.parent 807 run_module_as_main = tmp / "run_module_as_main.py" 808 run_module_as_main.write_text( 809 textwrap.dedent( 810 """\ 811 import runpy 812 runpy._run_module_as_main("ham") 813 """ 814 ) 815 ) 816 self.assertSigInt([sys.executable, run_module_as_main], cwd=tmp) 817 818 def test_pymain_run_command_run_module(self): 819 self.assertSigInt( 820 [sys.executable, "-c", "import runpy; runpy.run_module('ham')"], 821 cwd=self.ham.parent, 822 ) 823 824 def test_pymain_run_command(self): 825 self.assertSigInt([sys.executable, "-c", "import ham"], cwd=self.ham.parent) 826 827 def test_pymain_run_stdin(self): 828 self.assertSigInt([sys.executable], input="import ham", cwd=self.ham.parent) 829 830 def test_pymain_run_module(self): 831 ham = self.ham 832 self.assertSigInt([sys.executable, "-m", ham.stem], cwd=ham.parent) 833 834 835if __name__ == "__main__": 836 unittest.main() 837