diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2018-10-29 17:30:16 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-29 17:30:16 (GMT) |
commit | c93c58b5d560cfe44d9884ff02c9b18e06333360 (patch) | |
tree | 5a52914e13a0d5990b3644a61da2e7d948d655e8 /Lib/importlib | |
parent | 542497aa9f71c664768c3d5b7398c03679d3a7e1 (diff) | |
download | cpython-c93c58b5d560cfe44d9884ff02c9b18e06333360.zip cpython-c93c58b5d560cfe44d9884ff02c9b18e06333360.tar.gz cpython-c93c58b5d560cfe44d9884ff02c9b18e06333360.tar.bz2 |
bpo-33331: Clean modules in the reversed order in PyImport_Cleanup(). (GH-6565)
Modules imported last are now cleared first at interpreter shutdown.
A newly imported module is moved to the end of sys.modules, behind
modules on which it depends.
Diffstat (limited to 'Lib/importlib')
-rw-r--r-- | Lib/importlib/_bootstrap.py | 118 |
1 files changed, 62 insertions, 56 deletions
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index ba5a053..857583a 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -302,33 +302,6 @@ def _module_repr(module): return '<module {!r} from {!r}>'.format(name, filename) -class _installed_safely: - - def __init__(self, module): - self._module = module - self._spec = module.__spec__ - - def __enter__(self): - # This must be done before putting the module in sys.modules - # (otherwise an optimization shortcut in import.c becomes - # wrong) - self._spec._initializing = True - sys.modules[self._spec.name] = self._module - - def __exit__(self, *args): - try: - spec = self._spec - if any(arg is not None for arg in args): - try: - del sys.modules[spec.name] - except KeyError: - pass - else: - _verbose_message('import {!r} # {!r}', spec.name, spec.loader) - finally: - self._spec._initializing = False - - class ModuleSpec: """The specification for a module, used for loading. @@ -614,30 +587,44 @@ def _exec(spec, module): if sys.modules.get(name) is not module: msg = 'module {!r} not in sys.modules'.format(name) raise ImportError(msg, name=name) - if spec.loader is None: - if spec.submodule_search_locations is None: - raise ImportError('missing loader', name=spec.name) - # namespace package - _init_module_attrs(spec, module, override=True) - return module - _init_module_attrs(spec, module, override=True) - if not hasattr(spec.loader, 'exec_module'): - # (issue19713) Once BuiltinImporter and ExtensionFileLoader - # have exec_module() implemented, we can add a deprecation - # warning here. - spec.loader.load_module(name) - else: - spec.loader.exec_module(module) - return sys.modules[name] + try: + if spec.loader is None: + if spec.submodule_search_locations is None: + raise ImportError('missing loader', name=spec.name) + # Namespace package. + _init_module_attrs(spec, module, override=True) + else: + _init_module_attrs(spec, module, override=True) + if not hasattr(spec.loader, 'exec_module'): + # (issue19713) Once BuiltinImporter and ExtensionFileLoader + # have exec_module() implemented, we can add a deprecation + # warning here. + spec.loader.load_module(name) + else: + spec.loader.exec_module(module) + finally: + # Update the order of insertion into sys.modules for module + # clean-up at shutdown. + module = sys.modules.pop(spec.name) + sys.modules[spec.name] = module + return module def _load_backward_compatible(spec): # (issue19713) Once BuiltinImporter and ExtensionFileLoader # have exec_module() implemented, we can add a deprecation # warning here. - spec.loader.load_module(spec.name) + try: + spec.loader.load_module(spec.name) + except: + if spec.name in sys.modules: + module = sys.modules.pop(spec.name) + sys.modules[spec.name] = module + raise # The module must be in sys.modules at this point! - module = sys.modules[spec.name] + # Move it to the end of sys.modules. + module = sys.modules.pop(spec.name) + sys.modules[spec.name] = module if getattr(module, '__loader__', None) is None: try: module.__loader__ = spec.loader @@ -663,23 +650,42 @@ def _load_backward_compatible(spec): def _load_unlocked(spec): # A helper for direct use by the import system. if spec.loader is not None: - # not a namespace package + # Not a namespace package. if not hasattr(spec.loader, 'exec_module'): return _load_backward_compatible(spec) module = module_from_spec(spec) - with _installed_safely(module): - if spec.loader is None: - if spec.submodule_search_locations is None: - raise ImportError('missing loader', name=spec.name) - # A namespace package so do nothing. - else: - spec.loader.exec_module(module) - # We don't ensure that the import-related module attributes get - # set in the sys.modules replacement case. Such modules are on - # their own. - return sys.modules[spec.name] + # This must be done before putting the module in sys.modules + # (otherwise an optimization shortcut in import.c becomes + # wrong). + spec._initializing = True + try: + sys.modules[spec.name] = module + try: + if spec.loader is None: + if spec.submodule_search_locations is None: + raise ImportError('missing loader', name=spec.name) + # A namespace package so do nothing. + else: + spec.loader.exec_module(module) + except: + try: + del sys.modules[spec.name] + except KeyError: + pass + raise + # Move the module to the end of sys.modules. + # We don't ensure that the import-related module attributes get + # set in the sys.modules replacement case. Such modules are on + # their own. + module = sys.modules.pop(spec.name) + sys.modules[spec.name] = module + _verbose_message('import {!r} # {!r}', spec.name, spec.loader) + finally: + spec._initializing = False + + return module # A method used during testing of _load_unlocked() and by # _load_module_shim(). |