summaryrefslogtreecommitdiff
path: root/Lib/importlib/_bootstrap.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/importlib/_bootstrap.py')
-rw-r--r--Lib/importlib/_bootstrap.py70
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]