runpy.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. """runpy.py - locating and running Python code using the module namespace
  2. Provides support for locating and running Python scripts using the Python
  3. module namespace instead of the native filesystem.
  4. This allows Python code to play nicely with non-filesystem based PEP 302
  5. importers when locating support scripts as well as when importing modules.
  6. """
  7. # Written by Nick Coghlan <ncoghlan at gmail.com>
  8. # to implement PEP 338 (Executing Modules as Scripts)
  9. import sys
  10. import importlib.machinery # importlib first so we can test #15386 via -m
  11. import importlib.util
  12. import io
  13. import types
  14. import os
  15. from pkgutil import read_code, get_importer
  16. __all__ = [
  17. "run_module", "run_path",
  18. ]
  19. class _TempModule(object):
  20. """Temporarily replace a module in sys.modules with an empty namespace"""
  21. def __init__(self, mod_name):
  22. self.mod_name = mod_name
  23. self.module = types.ModuleType(mod_name)
  24. self._saved_module = []
  25. def __enter__(self):
  26. mod_name = self.mod_name
  27. try:
  28. self._saved_module.append(sys.modules[mod_name])
  29. except KeyError:
  30. pass
  31. sys.modules[mod_name] = self.module
  32. return self
  33. def __exit__(self, *args):
  34. if self._saved_module:
  35. sys.modules[self.mod_name] = self._saved_module[0]
  36. else:
  37. del sys.modules[self.mod_name]
  38. self._saved_module = []
  39. class _ModifiedArgv0(object):
  40. def __init__(self, value):
  41. self.value = value
  42. self._saved_value = self._sentinel = object()
  43. def __enter__(self):
  44. if self._saved_value is not self._sentinel:
  45. raise RuntimeError("Already preserving saved value")
  46. self._saved_value = sys.argv[0]
  47. sys.argv[0] = self.value
  48. def __exit__(self, *args):
  49. self.value = self._sentinel
  50. sys.argv[0] = self._saved_value
  51. # TODO: Replace these helpers with importlib._bootstrap_external functions.
  52. def _run_code(code, run_globals, init_globals=None,
  53. mod_name=None, mod_spec=None,
  54. pkg_name=None, script_name=None):
  55. """Helper to run code in nominated namespace"""
  56. if init_globals is not None:
  57. run_globals.update(init_globals)
  58. if mod_spec is None:
  59. loader = None
  60. fname = script_name
  61. cached = None
  62. else:
  63. loader = mod_spec.loader
  64. fname = mod_spec.origin
  65. cached = mod_spec.cached
  66. if pkg_name is None:
  67. pkg_name = mod_spec.parent
  68. run_globals.update(__name__ = mod_name,
  69. __file__ = fname,
  70. __cached__ = cached,
  71. __doc__ = None,
  72. __loader__ = loader,
  73. __package__ = pkg_name,
  74. __spec__ = mod_spec)
  75. exec(code, run_globals)
  76. return run_globals
  77. def _run_module_code(code, init_globals=None,
  78. mod_name=None, mod_spec=None,
  79. pkg_name=None, script_name=None):
  80. """Helper to run code in new namespace with sys modified"""
  81. fname = script_name if mod_spec is None else mod_spec.origin
  82. with _TempModule(mod_name) as temp_module, _ModifiedArgv0(fname):
  83. mod_globals = temp_module.module.__dict__
  84. _run_code(code, mod_globals, init_globals,
  85. mod_name, mod_spec, pkg_name, script_name)
  86. # Copy the globals of the temporary module, as they
  87. # may be cleared when the temporary module goes away
  88. return mod_globals.copy()
  89. # Helper to get the full name, spec and code for a module
  90. def _get_module_details(mod_name, error=ImportError):
  91. if mod_name.startswith("."):
  92. raise error("Relative module names not supported")
  93. pkg_name, _, _ = mod_name.rpartition(".")
  94. if pkg_name:
  95. # Try importing the parent to avoid catching initialization errors
  96. try:
  97. __import__(pkg_name)
  98. except ImportError as e:
  99. # If the parent or higher ancestor package is missing, let the
  100. # error be raised by find_spec() below and then be caught. But do
  101. # not allow other errors to be caught.
  102. if e.name is None or (e.name != pkg_name and
  103. not pkg_name.startswith(e.name + ".")):
  104. raise
  105. # Warn if the module has already been imported under its normal name
  106. existing = sys.modules.get(mod_name)
  107. if existing is not None and not hasattr(existing, "__path__"):
  108. from warnings import warn
  109. msg = "{mod_name!r} found in sys.modules after import of " \
  110. "package {pkg_name!r}, but prior to execution of " \
  111. "{mod_name!r}; this may result in unpredictable " \
  112. "behaviour".format(mod_name=mod_name, pkg_name=pkg_name)
  113. warn(RuntimeWarning(msg))
  114. try:
  115. spec = importlib.util.find_spec(mod_name)
  116. except (ImportError, AttributeError, TypeError, ValueError) as ex:
  117. # This hack fixes an impedance mismatch between pkgutil and
  118. # importlib, where the latter raises other errors for cases where
  119. # pkgutil previously raised ImportError
  120. msg = "Error while finding module specification for {!r} ({}: {})"
  121. if mod_name.endswith(".py"):
  122. msg += (f". Try using '{mod_name[:-3]}' instead of "
  123. f"'{mod_name}' as the module name.")
  124. raise error(msg.format(mod_name, type(ex).__name__, ex)) from ex
  125. if spec is None:
  126. raise error("No module named %s" % mod_name)
  127. if spec.submodule_search_locations is not None:
  128. if mod_name == "__main__" or mod_name.endswith(".__main__"):
  129. raise error("Cannot use package as __main__ module")
  130. try:
  131. pkg_main_name = mod_name + ".__main__"
  132. return _get_module_details(pkg_main_name, error)
  133. except error as e:
  134. if mod_name not in sys.modules:
  135. raise # No module loaded; being a package is irrelevant
  136. raise error(("%s; %r is a package and cannot " +
  137. "be directly executed") %(e, mod_name))
  138. loader = spec.loader
  139. if loader is None:
  140. raise error("%r is a namespace package and cannot be executed"
  141. % mod_name)
  142. try:
  143. code = loader.get_code(mod_name)
  144. except ImportError as e:
  145. raise error(format(e)) from e
  146. if code is None:
  147. raise error("No code object available for %s" % mod_name)
  148. return mod_name, spec, code
  149. class _Error(Exception):
  150. """Error that _run_module_as_main() should report without a traceback"""
  151. # XXX ncoghlan: Should this be documented and made public?
  152. # (Current thoughts: don't repeat the mistake that lead to its
  153. # creation when run_module() no longer met the needs of
  154. # mainmodule.c, but couldn't be changed because it was public)
  155. def _run_module_as_main(mod_name, alter_argv=True):
  156. """Runs the designated module in the __main__ namespace
  157. Note that the executed module will have full access to the
  158. __main__ namespace. If this is not desirable, the run_module()
  159. function should be used to run the module code in a fresh namespace.
  160. At the very least, these variables in __main__ will be overwritten:
  161. __name__
  162. __file__
  163. __cached__
  164. __loader__
  165. __package__
  166. """
  167. try:
  168. if alter_argv or mod_name != "__main__": # i.e. -m switch
  169. mod_name, mod_spec, code = _get_module_details(mod_name, _Error)
  170. else: # i.e. directory or zipfile execution
  171. mod_name, mod_spec, code = _get_main_module_details(_Error)
  172. except _Error as exc:
  173. msg = "%s: %s" % (sys.executable, exc)
  174. sys.exit(msg)
  175. main_globals = sys.modules["__main__"].__dict__
  176. if alter_argv:
  177. sys.argv[0] = mod_spec.origin
  178. return _run_code(code, main_globals, None,
  179. "__main__", mod_spec)
  180. def run_module(mod_name, init_globals=None,
  181. run_name=None, alter_sys=False):
  182. """Execute a module's code without importing it.
  183. mod_name -- an absolute module name or package name.
  184. Optional arguments:
  185. init_globals -- dictionary used to pre-populate the module’s
  186. globals dictionary before the code is executed.
  187. run_name -- if not None, this will be used for setting __name__;
  188. otherwise, __name__ will be set to mod_name + '__main__' if the
  189. named module is a package and to just mod_name otherwise.
  190. alter_sys -- if True, sys.argv[0] is updated with the value of
  191. __file__ and sys.modules[__name__] is updated with a temporary
  192. module object for the module being executed. Both are
  193. restored to their original values before the function returns.
  194. Returns the resulting module globals dictionary.
  195. """
  196. mod_name, mod_spec, code = _get_module_details(mod_name)
  197. if run_name is None:
  198. run_name = mod_name
  199. if alter_sys:
  200. return _run_module_code(code, init_globals, run_name, mod_spec)
  201. else:
  202. # Leave the sys module alone
  203. return _run_code(code, {}, init_globals, run_name, mod_spec)
  204. def _get_main_module_details(error=ImportError):
  205. # Helper that gives a nicer error message when attempting to
  206. # execute a zipfile or directory by invoking __main__.py
  207. # Also moves the standard __main__ out of the way so that the
  208. # preexisting __loader__ entry doesn't cause issues
  209. main_name = "__main__"
  210. saved_main = sys.modules[main_name]
  211. del sys.modules[main_name]
  212. try:
  213. return _get_module_details(main_name)
  214. except ImportError as exc:
  215. if main_name in str(exc):
  216. raise error("can't find %r module in %r" %
  217. (main_name, sys.path[0])) from exc
  218. raise
  219. finally:
  220. sys.modules[main_name] = saved_main
  221. def _get_code_from_file(run_name, fname):
  222. # Check for a compiled file first
  223. decoded_path = os.path.abspath(os.fsdecode(fname))
  224. with io.open_code(decoded_path) as f:
  225. code = read_code(f)
  226. if code is None:
  227. # That didn't work, so try it as normal source code
  228. with io.open_code(decoded_path) as f:
  229. code = compile(f.read(), fname, 'exec')
  230. return code, fname
  231. def run_path(path_name, init_globals=None, run_name=None):
  232. """Execute code located at the specified filesystem location.
  233. path_name -- filesystem location of a Python script, zipfile,
  234. or directory containing a top level __main__.py script.
  235. Optional arguments:
  236. init_globals -- dictionary used to pre-populate the module’s
  237. globals dictionary before the code is executed.
  238. run_name -- if not None, this will be used to set __name__;
  239. otherwise, '<run_path>' will be used for __name__.
  240. Returns the resulting module globals dictionary.
  241. """
  242. if run_name is None:
  243. run_name = "<run_path>"
  244. pkg_name = run_name.rpartition(".")[0]
  245. importer = get_importer(path_name)
  246. # Trying to avoid importing imp so as to not consume the deprecation warning.
  247. is_NullImporter = False
  248. if type(importer).__module__ == 'imp':
  249. if type(importer).__name__ == 'NullImporter':
  250. is_NullImporter = True
  251. if isinstance(importer, type(None)) or is_NullImporter:
  252. # Not a valid sys.path entry, so run the code directly
  253. # execfile() doesn't help as we want to allow compiled files
  254. code, fname = _get_code_from_file(run_name, path_name)
  255. return _run_module_code(code, init_globals, run_name,
  256. pkg_name=pkg_name, script_name=fname)
  257. else:
  258. # Finder is defined for path, so add it to
  259. # the start of sys.path
  260. sys.path.insert(0, path_name)
  261. try:
  262. # Here's where things are a little different from the run_module
  263. # case. There, we only had to replace the module in sys while the
  264. # code was running and doing so was somewhat optional. Here, we
  265. # have no choice and we have to remove it even while we read the
  266. # code. If we don't do this, a __loader__ attribute in the
  267. # existing __main__ module may prevent location of the new module.
  268. mod_name, mod_spec, code = _get_main_module_details()
  269. with _TempModule(run_name) as temp_module, \
  270. _ModifiedArgv0(path_name):
  271. mod_globals = temp_module.module.__dict__
  272. return _run_code(code, mod_globals, init_globals,
  273. run_name, mod_spec, pkg_name).copy()
  274. finally:
  275. try:
  276. sys.path.remove(path_name)
  277. except ValueError:
  278. pass
  279. if __name__ == "__main__":
  280. # Run the module specified as the next command line argument
  281. if len(sys.argv) < 2:
  282. print("No module specified for execution", file=sys.stderr)
  283. else:
  284. del sys.argv[0] # Make the requested module sys.argv[0]
  285. _run_module_as_main(sys.argv[0])