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