summaryrefslogtreecommitdiff
path: root/Lib/runpy.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/runpy.py')
-rw-r--r--Lib/runpy.py72
1 files changed, 22 insertions, 50 deletions
diff --git a/Lib/runpy.py b/Lib/runpy.py
index a14a62e128..39c0e9f7dd 100644
--- a/Lib/runpy.py
+++ b/Lib/runpy.py
@@ -9,13 +9,12 @@ importers when locating support scripts as well as when importing modules.
# Written by Nick Coghlan <ncoghlan at gmail.com>
# to implement PEP 338 (Executing Modules as Scripts)
+
+import os
import sys
+import importlib.machinery # importlib first so we can test #15386 via -m
import imp
-from pkgutil import read_code
-try:
- from imp import get_loader
-except ImportError:
- from pkgutil import get_loader
+from pkgutil import read_code, get_loader, get_importer
__all__ = [
"run_module", "run_path",
@@ -95,7 +94,7 @@ def _get_filename(loader, mod_name):
for attr in ("get_filename", "_get_filename"):
meth = getattr(loader, attr, None)
if meth is not None:
- return meth(mod_name)
+ return os.path.abspath(meth(mod_name))
return None
# Helper to get the loader, code and filename for a module
@@ -181,47 +180,23 @@ def run_module(mod_name, init_globals=None,
def _get_main_module_details():
# Helper that gives a nicer error message when attempting to
# execute a zipfile or directory by invoking __main__.py
+ # Also moves the standard __main__ out of the way so that the
+ # preexisting __loader__ entry doesn't cause issues
main_name = "__main__"
+ saved_main = sys.modules[main_name]
+ del sys.modules[main_name]
try:
return _get_module_details(main_name)
except ImportError as exc:
if main_name in str(exc):
raise ImportError("can't find %r module in %r" %
- (main_name, sys.path[0]))
+ (main_name, sys.path[0])) from exc
raise
+ finally:
+ sys.modules[main_name] = saved_main
-# XXX (ncoghlan): Perhaps expose the C API function
-# as imp.get_importer instead of reimplementing it in Python?
-def _get_importer(path_name):
- """Python version of PyImport_GetImporter C API function"""
- cache = sys.path_importer_cache
- try:
- importer = cache[path_name]
- except KeyError:
- # Not yet cached. Flag as using the
- # standard machinery until we finish
- # checking the hooks
- cache[path_name] = None
- for hook in sys.path_hooks:
- try:
- importer = hook(path_name)
- break
- except ImportError:
- pass
- else:
- # The following check looks a bit odd. The trick is that
- # NullImporter raises ImportError if the supplied path is a
- # *valid* directory entry (and hence able to be handled
- # by the standard import machinery)
- try:
- importer = imp.NullImporter(path_name)
- except ImportError:
- return None
- cache[path_name] = importer
- return importer
-
-def _get_code_from_file(fname):
+def _get_code_from_file(run_name, fname):
# Check for a compiled file first
with open(fname, "rb") as f:
code = read_code(f)
@@ -229,7 +204,10 @@ def _get_code_from_file(fname):
# That didn't work, so try it as normal source code
with open(fname, "rb") as f:
code = compile(f.read(), fname, 'exec')
- return code
+ loader = importlib.machinery.SourceFileLoader(run_name, fname)
+ else:
+ loader = importlib.machinery.SourcelessFileLoader(run_name, fname)
+ return code, loader
def run_path(path_name, init_globals=None, run_name=None):
"""Execute code located at the specified filesystem location
@@ -244,13 +222,13 @@ def run_path(path_name, init_globals=None, run_name=None):
if run_name is None:
run_name = "<run_path>"
pkg_name = run_name.rpartition(".")[0]
- importer = _get_importer(path_name)
- if isinstance(importer, imp.NullImporter):
+ importer = get_importer(path_name)
+ if isinstance(importer, (type(None), imp.NullImporter)):
# Not a valid sys.path entry, so run the code directly
# execfile() doesn't help as we want to allow compiled files
- code = _get_code_from_file(path_name)
+ code, mod_loader = _get_code_from_file(run_name, path_name)
return _run_module_code(code, init_globals, run_name, path_name,
- pkg_name=pkg_name)
+ mod_loader, pkg_name)
else:
# Importer is defined for path, so add it to
# the start of sys.path
@@ -262,13 +240,7 @@ def run_path(path_name, init_globals=None, run_name=None):
# have no choice and we have to remove it even while we read the
# code. If we don't do this, a __loader__ attribute in the
# existing __main__ module may prevent location of the new module.
- main_name = "__main__"
- saved_main = sys.modules[main_name]
- del sys.modules[main_name]
- try:
- mod_name, loader, code, fname = _get_main_module_details()
- finally:
- sys.modules[main_name] = saved_main
+ mod_name, loader, code, fname = _get_main_module_details()
with _TempModule(run_name) as temp_module, \
_ModifiedArgv0(path_name):
mod_globals = temp_module.module.__dict__