(root)/
Python-3.12.0/
Lib/
test/
test_runpy.py
       1  # Test the runpy module
       2  import contextlib
       3  import importlib.machinery, importlib.util
       4  import os.path
       5  import pathlib
       6  import py_compile
       7  import re
       8  import signal
       9  import subprocess
      10  import sys
      11  import tempfile
      12  import textwrap
      13  import unittest
      14  import warnings
      15  from test.support import no_tracing, verbose, requires_subprocess, requires_resource
      16  from test.support.import_helper import forget, make_legacy_pyc, unload
      17  from test.support.os_helper import create_empty_file, temp_dir
      18  from test.support.script_helper import make_script, make_zip_script
      19  
      20  
      21  import runpy
      22  from 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
      30  example_source = """\
      31  # Check basic code execution
      32  result = ['Top level assignment']
      33  def f():
      34      result.append('Lower level reference')
      35  f()
      36  del f
      37  # Check the sys module
      38  import sys
      39  run_argv0 = sys.argv[0]
      40  run_name_in_sys_modules = __name__ in sys.modules
      41  module_in_sys_modules = (run_name_in_sys_modules and
      42                           globals() is sys.modules[__name__].__dict__)
      43  # Check nested operation
      44  import runpy
      45  nested = runpy._run_module_code('x=1\\n', mod_name='<run>')
      46  """
      47  
      48  implicit_namespace = {
      49      "__name__": None,
      50      "__file__": None,
      51      "__cached__": None,
      52      "__package__": None,
      53      "__doc__": None,
      54      "__spec__": None
      55  }
      56  example_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  }
      66  example_namespace.update(implicit_namespace)
      67  
      68  class ESC[4;38;5;81mCodeExecutionMixin:
      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  
     147  class ESC[4;38;5;81mExecutionLayerTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase, ESC[4;38;5;149mCodeExecutionMixin):
     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
     186  class ESC[4;38;5;81mRunModuleTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase, ESC[4;38;5;149mCodeExecutionMixin):
     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"""\
     395  from __future__ import absolute_import
     396  from . import sibling
     397  from ..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  
     593  class ESC[4;38;5;81mRunPathTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase, ESC[4;38;5;149mCodeExecutionMixin):
     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      @requires_resource('cpu')
     737      def test_main_recursion_error(self):
     738          with temp_dir() as script_dir, temp_dir() as dummy_dir:
     739              mod_name = '__main__'
     740              source = ("import runpy\n"
     741                        "runpy.run_path(%r)\n") % dummy_dir
     742              script_name = self._make_test_script(script_dir, mod_name, source)
     743              zip_name, fname = make_zip_script(script_dir, 'test_zip', script_name)
     744              self.assertRaises(RecursionError, 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
     752  s = "non-ASCII: h\xe9"
     753  """)
     754              result = run_path(filename)
     755              self.assertEqual(result['s'], "non-ASCII: h\xe9")
     756  
     757  
     758  class ESC[4;38;5;81mTestExit(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     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      @requires_subprocess()
     785      def assertSigInt(self, cmd, *args, **kwargs):
     786          # Use -E to ignore PYTHONSAFEPATH
     787          cmd = [sys.executable, '-E', *cmd]
     788          proc = subprocess.run(cmd, *args, **kwargs, text=True, stderr=subprocess.PIPE)
     789          self.assertTrue(proc.stderr.endswith("\nKeyboardInterrupt\n"), proc.stderr)
     790          self.assertEqual(proc.returncode, self.EXPECTED_CODE)
     791  
     792      def test_pymain_run_file(self):
     793          self.assertSigInt([self.ham])
     794  
     795      def test_pymain_run_file_runpy_run_module(self):
     796          tmp = self.ham.parent
     797          run_module = tmp / "run_module.py"
     798          run_module.write_text(
     799              textwrap.dedent(
     800                  """\
     801                  import runpy
     802                  runpy.run_module("ham")
     803                  """
     804              )
     805          )
     806          self.assertSigInt([run_module], cwd=tmp)
     807  
     808      def test_pymain_run_file_runpy_run_module_as_main(self):
     809          tmp = self.ham.parent
     810          run_module_as_main = tmp / "run_module_as_main.py"
     811          run_module_as_main.write_text(
     812              textwrap.dedent(
     813                  """\
     814                  import runpy
     815                  runpy._run_module_as_main("ham")
     816                  """
     817              )
     818          )
     819          self.assertSigInt([run_module_as_main], cwd=tmp)
     820  
     821      def test_pymain_run_command_run_module(self):
     822          self.assertSigInt(
     823              ["-c", "import runpy; runpy.run_module('ham')"],
     824              cwd=self.ham.parent,
     825          )
     826  
     827      def test_pymain_run_command(self):
     828          self.assertSigInt(["-c", "import ham"], cwd=self.ham.parent)
     829  
     830      def test_pymain_run_stdin(self):
     831          self.assertSigInt([], input="import ham", cwd=self.ham.parent)
     832  
     833      def test_pymain_run_module(self):
     834          ham = self.ham
     835          self.assertSigInt(["-m", ham.stem], cwd=ham.parent)
     836  
     837  
     838  if __name__ == "__main__":
     839      unittest.main()