diff options
author | Steve Dower <steve.dower@microsoft.com> | 2017-02-04 15:39:38 -0800 |
---|---|---|
committer | Steve Dower <steve.dower@microsoft.com> | 2017-02-04 15:39:38 -0800 |
commit | 1a5780aabb550cae175ad8711e2f33ba644d0ddb (patch) | |
tree | 68e47eaafb4ccc17bbdb7668c6058984945b332d /Lib/importlib/_bootstrap.py | |
parent | 956c7cfa7111ab5458e2f69868a05b7b84fc6843 (diff) | |
parent | d1d8706cdb77e2adbbb4110338dcda0e1811f892 (diff) | |
download | cpython-1a5780aabb550cae175ad8711e2f33ba644d0ddb.tar.gz |
Issue #29319: Prevent RunMainFromImporter overwriting sys.path[0].
Diffstat (limited to 'Lib/importlib/_bootstrap.py')
-rw-r--r-- | Lib/importlib/_bootstrap.py | 70 |
1 files changed, 37 insertions, 33 deletions
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 9eecbfe967..a531a0351d 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -36,23 +36,6 @@ def _new_module(name): return type(sys)(name) -class _ManageReload: - - """Manages the possible clean-up of sys.modules for load_module().""" - - def __init__(self, name): - self._name = name - - def __enter__(self): - self._is_reload = self._name in sys.modules - - def __exit__(self, *args): - if any(arg is not None for arg in args) and not self._is_reload: - try: - del sys.modules[self._name] - except KeyError: - pass - # Module-level locking ######################################################## # A dict mapping module names to weakrefs of _ModuleLock instances @@ -270,7 +253,7 @@ def _load_module_shim(self, fullname): # Module specifications ####################################################### def _module_repr(module): - # The implementation of ModuleType__repr__(). + # The implementation of ModuleType.__repr__(). loader = getattr(module, '__loader__', None) if hasattr(loader, 'module_repr'): # As soon as BuiltinImporter, FrozenImporter, and NamespaceLoader @@ -576,9 +559,8 @@ def module_from_spec(spec): # module creation should be used. module = spec.loader.create_module(spec) elif hasattr(spec.loader, 'exec_module'): - _warnings.warn('starting in Python 3.6, loaders defining exec_module() ' - 'must also define create_module()', - DeprecationWarning, stacklevel=2) + raise ImportError('loaders that define exec_module() ' + 'must also define create_module()') if module is None: module = _new_module(spec.name) _init_module_attrs(spec, module) @@ -603,7 +585,7 @@ def _module_repr_from_spec(spec): # Used by importlib.reload() and _load_module_shim(). def _exec(spec, module): - """Execute the spec in an existing module's namespace.""" + """Execute the spec's specified module in an existing module's namespace.""" name = spec.name _imp.acquire_lock() with _ModuleLockManager(name): @@ -877,14 +859,21 @@ def _find_spec_legacy(finder, name, path): def _find_spec(name, path, target=None): - """Find a module's loader.""" - if sys.meta_path is not None and not sys.meta_path: + """Find a module's spec.""" + meta_path = sys.meta_path + if meta_path is None: + # PyImport_Cleanup() is running or has been called. + raise ImportError("sys.meta_path is None, Python is likely " + "shutting down") + + if not meta_path: _warnings.warn('sys.meta_path is empty', ImportWarning) + # We check sys.modules here for the reload case. While a passed-in # target will usually indicate a reload there is no guarantee, whereas # sys.modules provides one. is_reload = name in sys.modules - for finder in sys.meta_path: + for finder in meta_path: with _ImportLockContext(): try: find_spec = finder.find_spec @@ -925,6 +914,9 @@ def _sanity_check(name, package, level): if level > 0: if not isinstance(package, str): raise TypeError('__package__ not set to a string') + elif not package: + raise ImportError('attempted relative import with no known parent ' + 'package') elif package not in sys.modules: msg = ('Parent module {!r} not loaded, cannot perform relative ' 'import') @@ -950,10 +942,10 @@ def _find_and_load_unlocked(name, import_): path = parent_module.__path__ except AttributeError: msg = (_ERR_MSG + '; {!r} is not a package').format(name, parent) - raise ImportError(msg, name=name) from None + raise ModuleNotFoundError(msg, name=name) from None spec = _find_spec(name, path) if spec is None: - raise ImportError(_ERR_MSG.format(name), name=name) + raise ModuleNotFoundError(_ERR_MSG.format(name), name=name) else: module = _load_unlocked(spec) if parent: @@ -989,10 +981,11 @@ def _gcd_import(name, package=None, level=0): _imp.release_lock() message = ('import of {} halted; ' 'None in sys.modules'.format(name)) - raise ImportError(message, name=name) + raise ModuleNotFoundError(message, name=name) _lock_unlock_module(name) return module + def _handle_fromlist(module, fromlist, import_): """Figure out what __import__ should return. @@ -1014,13 +1007,12 @@ def _handle_fromlist(module, fromlist, import_): from_name = '{}.{}'.format(module.__name__, x) try: _call_with_frames_removed(import_, from_name) - except ImportError as exc: + except ModuleNotFoundError as exc: # Backwards-compatibility dictates we ignore failed # imports triggered by fromlist for modules that don't # exist. - if str(exc).startswith(_ERR_MSG_PREFIX): - if exc.name == from_name: - continue + if exc.name == from_name: + continue raise return module @@ -1033,7 +1025,19 @@ def _calc___package__(globals): """ package = globals.get('__package__') - if package is None: + spec = globals.get('__spec__') + if package is not None: + if spec is not None and package != spec.parent: + _warnings.warn("__package__ != __spec__.parent " + f"({package!r} != {spec.parent!r})", + ImportWarning, stacklevel=3) + return package + elif spec is not None: + return spec.parent + else: + _warnings.warn("can't resolve package from __spec__ or __package__, " + "falling back on __name__ and __path__", + ImportWarning, stacklevel=3) package = globals['__name__'] if '__path__' not in globals: package = package.rpartition('.')[0] |