mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Change importlib.machinery.PathFinder to not have implicit semantics (that's
not handled by importlib._bootstrap._DefaultPathFinder).
This commit is contained in:
parent
4b4a4a5922
commit
32732e3fbd
3 changed files with 72 additions and 57 deletions
|
@ -610,36 +610,25 @@ class PathFinder:
|
||||||
|
|
||||||
"""Meta path finder for sys.(path|path_hooks|path_importer_cache)."""
|
"""Meta path finder for sys.(path|path_hooks|path_importer_cache)."""
|
||||||
|
|
||||||
_default_hook = staticmethod(chaining_fs_path_hook(ExtensionFileImporter,
|
|
||||||
PyFileImporter))
|
|
||||||
|
|
||||||
# The list of implicit hooks cannot be a class attribute because of
|
|
||||||
# bootstrapping issues for accessing imp.
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _implicit_hooks(cls):
|
def _path_hooks(cls, path, hooks=None):
|
||||||
"""Return a list of the implicit path hooks."""
|
"""Search sequence of hooks for a finder for 'path'.
|
||||||
return [cls._default_hook, imp.NullImporter]
|
|
||||||
|
|
||||||
@classmethod
|
If 'hooks' is false then use sys.path_hooks.
|
||||||
def _path_hooks(cls, path):
|
|
||||||
"""Search sys.path_hooks for a finder for 'path'.
|
|
||||||
|
|
||||||
Guaranteed to return a finder for the path as NullImporter is the
|
|
||||||
default importer for any path that does not have an explicit finder.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
for hook in sys.path_hooks + cls._implicit_hooks():
|
if not hooks:
|
||||||
|
hooks = sys.path_hooks
|
||||||
|
for hook in hooks:
|
||||||
try:
|
try:
|
||||||
return hook(path)
|
return hook(path)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
# This point should never be reached thanks to NullImporter.
|
raise ImportError("no path hook found for {0}".format(path))
|
||||||
raise SystemError("no hook could find an importer for "
|
|
||||||
"{0}".format(path))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _path_importer_cache(cls, path):
|
def _path_importer_cache(cls, path, default=None):
|
||||||
"""Get the finder for the path from sys.path_importer_cache.
|
"""Get the finder for the path from sys.path_importer_cache.
|
||||||
|
|
||||||
If the path is not in the cache, find the appropriate finder and cache
|
If the path is not in the cache, find the appropriate finder and cache
|
||||||
|
@ -657,9 +646,9 @@ class PathFinder:
|
||||||
finder = cls._path_hooks(path)
|
finder = cls._path_hooks(path)
|
||||||
sys.path_importer_cache[path] = finder
|
sys.path_importer_cache[path] = finder
|
||||||
else:
|
else:
|
||||||
if finder is None:
|
if finder is None and default:
|
||||||
# Raises ImportError on failure.
|
# Raises ImportError on failure.
|
||||||
finder = cls._default_hook(path)
|
finder = default(path)
|
||||||
sys.path_importer_cache[path] = finder
|
sys.path_importer_cache[path] = finder
|
||||||
return finder
|
return finder
|
||||||
|
|
||||||
|
@ -680,6 +669,30 @@ class PathFinder:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class _DefaultPathFinder(PathFinder):
|
||||||
|
|
||||||
|
"""Subclass of PathFinder that implements implicit semantics for
|
||||||
|
__import__."""
|
||||||
|
|
||||||
|
_default_hook = staticmethod(chaining_fs_path_hook(ExtensionFileImporter,
|
||||||
|
PyFileImporter))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _path_hooks(cls, path):
|
||||||
|
"""Search sys.path_hooks as well as implicit path hooks."""
|
||||||
|
try:
|
||||||
|
return super()._path_hooks(path)
|
||||||
|
except ImportError:
|
||||||
|
implicit_hooks = [cls._default_hook, imp.NullImporter]
|
||||||
|
return super()._path_hooks(path, implicit_hooks)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _path_importer_cache(cls, path):
|
||||||
|
"""Use the default path hook when None is stored in
|
||||||
|
sys.path_importer_cache."""
|
||||||
|
return super()._path_importer_cache(path, cls._default_hook)
|
||||||
|
|
||||||
|
|
||||||
class ImportLockContext(object):
|
class ImportLockContext(object):
|
||||||
|
|
||||||
"""Context manager for the import lock."""
|
"""Context manager for the import lock."""
|
||||||
|
@ -693,6 +706,8 @@ class ImportLockContext(object):
|
||||||
imp.release_lock()
|
imp.release_lock()
|
||||||
|
|
||||||
|
|
||||||
|
_IMPLICIT_META_PATH = [BuiltinImporter, FrozenImporter, _DefaultPathFinder]
|
||||||
|
|
||||||
def _gcd_import(name, package=None, level=0):
|
def _gcd_import(name, package=None, level=0):
|
||||||
"""Import and return the module based on its name, the package the call is
|
"""Import and return the module based on its name, the package the call is
|
||||||
being made from, and the level adjustment.
|
being made from, and the level adjustment.
|
||||||
|
@ -736,8 +751,7 @@ def _gcd_import(name, package=None, level=0):
|
||||||
# Backwards-compatibility; be nicer to skip the dict lookup.
|
# Backwards-compatibility; be nicer to skip the dict lookup.
|
||||||
parent_module = sys.modules[parent]
|
parent_module = sys.modules[parent]
|
||||||
path = parent_module.__path__
|
path = parent_module.__path__
|
||||||
meta_path = (sys.meta_path +
|
meta_path = sys.meta_path + _IMPLICIT_META_PATH
|
||||||
[BuiltinImporter, FrozenImporter, PathFinder])
|
|
||||||
for finder in meta_path:
|
for finder in meta_path:
|
||||||
loader = finder.find_module(name, path)
|
loader = finder.find_module(name, path)
|
||||||
if loader is not None:
|
if loader is not None:
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from importlib import _bootstrap
|
||||||
from importlib import machinery
|
from importlib import machinery
|
||||||
from .. import util
|
from .. import util
|
||||||
from . import util as import_util
|
from . import util as import_util
|
||||||
|
@ -12,20 +13,13 @@ import unittest
|
||||||
|
|
||||||
class FinderTests(unittest.TestCase):
|
class FinderTests(unittest.TestCase):
|
||||||
|
|
||||||
"""Tests for SysPathImporter."""
|
"""Tests for PathFinder."""
|
||||||
|
|
||||||
def test_failure(self):
|
def test_failure(self):
|
||||||
# Test None returned upon not finding a suitable finder.
|
# Test None returned upon not finding a suitable finder.
|
||||||
def mock_implicit_hooks():
|
module = '<test module>'
|
||||||
return []
|
with util.import_state():
|
||||||
# XXX Not blackbox.
|
self.assert_(machinery.PathFinder.find_module(module) is None)
|
||||||
original_hooks = machinery.PathFinder._implicit_hooks
|
|
||||||
machinery.PathFinder._implicit_hooks = staticmethod(mock_implicit_hooks)
|
|
||||||
try:
|
|
||||||
with util.import_state():
|
|
||||||
self.assert_(machinery.PathFinder.find_module('XXX') is None)
|
|
||||||
finally:
|
|
||||||
machinery.PathFinder._implicit_hooks = original_hooks
|
|
||||||
|
|
||||||
def test_sys_path(self):
|
def test_sys_path(self):
|
||||||
# Test that sys.path is used when 'path' is None.
|
# Test that sys.path is used when 'path' is None.
|
||||||
|
@ -48,23 +42,6 @@ class FinderTests(unittest.TestCase):
|
||||||
loader = machinery.PathFinder.find_module(module, [path])
|
loader = machinery.PathFinder.find_module(module, [path])
|
||||||
self.assert_(loader is importer)
|
self.assert_(loader is importer)
|
||||||
|
|
||||||
def test_path_importer_cache_has_None(self):
|
|
||||||
# Test that the default hook is used when sys.path_importer_cache
|
|
||||||
# contains None for a path.
|
|
||||||
module = '<test module>'
|
|
||||||
importer = util.mock_modules(module)
|
|
||||||
path = '<test path>'
|
|
||||||
# XXX Not blackbox.
|
|
||||||
original_hook = machinery.PathFinder._default_hook
|
|
||||||
mock_hook = import_util.mock_path_hook(path, importer=importer)
|
|
||||||
machinery.PathFinder._default_hook = staticmethod(mock_hook)
|
|
||||||
try:
|
|
||||||
with util.import_state(path_importer_cache={path: None}):
|
|
||||||
loader = machinery.PathFinder.find_module(module, path=[path])
|
|
||||||
self.assert_(loader is importer)
|
|
||||||
finally:
|
|
||||||
machinery.PathFinder._default_hook = original_hook
|
|
||||||
|
|
||||||
def test_path_hooks(self):
|
def test_path_hooks(self):
|
||||||
# Test that sys.path_hooks is used.
|
# Test that sys.path_hooks is used.
|
||||||
# Test that sys.path_importer_cache is set.
|
# Test that sys.path_importer_cache is set.
|
||||||
|
@ -78,6 +55,11 @@ class FinderTests(unittest.TestCase):
|
||||||
self.assert_(path in sys.path_importer_cache)
|
self.assert_(path in sys.path_importer_cache)
|
||||||
self.assert_(sys.path_importer_cache[path] is importer)
|
self.assert_(sys.path_importer_cache[path] is importer)
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultPathFinderTests(unittest.TestCase):
|
||||||
|
|
||||||
|
"""Test importlib._bootstrap._DefaultPathFinder."""
|
||||||
|
|
||||||
def test_implicit_hooks(self):
|
def test_implicit_hooks(self):
|
||||||
# Test that the implicit path hooks are used.
|
# Test that the implicit path hooks are used.
|
||||||
existing_path = os.path.dirname(support.TESTFN)
|
existing_path = os.path.dirname(support.TESTFN)
|
||||||
|
@ -85,22 +67,41 @@ class FinderTests(unittest.TestCase):
|
||||||
module = '<module>'
|
module = '<module>'
|
||||||
assert not os.path.exists(bad_path)
|
assert not os.path.exists(bad_path)
|
||||||
with util.import_state():
|
with util.import_state():
|
||||||
nothing = machinery.PathFinder.find_module(module,
|
nothing = _bootstrap._DefaultPathFinder.find_module(module,
|
||||||
path=[existing_path])
|
path=[existing_path])
|
||||||
self.assert_(nothing is None)
|
self.assert_(nothing is None)
|
||||||
self.assert_(existing_path in sys.path_importer_cache)
|
self.assert_(existing_path in sys.path_importer_cache)
|
||||||
self.assert_(not isinstance(sys.path_importer_cache[existing_path],
|
self.assert_(not isinstance(sys.path_importer_cache[existing_path],
|
||||||
imp.NullImporter))
|
imp.NullImporter))
|
||||||
nothing = machinery.PathFinder.find_module(module, path=[bad_path])
|
nothing = _bootstrap._DefaultPathFinder.find_module(module,
|
||||||
|
path=[bad_path])
|
||||||
self.assert_(nothing is None)
|
self.assert_(nothing is None)
|
||||||
self.assert_(bad_path in sys.path_importer_cache)
|
self.assert_(bad_path in sys.path_importer_cache)
|
||||||
self.assert_(isinstance(sys.path_importer_cache[bad_path],
|
self.assert_(isinstance(sys.path_importer_cache[bad_path],
|
||||||
imp.NullImporter))
|
imp.NullImporter))
|
||||||
|
|
||||||
|
def test_path_importer_cache_has_None(self):
|
||||||
|
# Test that the default hook is used when sys.path_importer_cache
|
||||||
|
# contains None for a path.
|
||||||
|
module = '<test module>'
|
||||||
|
importer = util.mock_modules(module)
|
||||||
|
path = '<test path>'
|
||||||
|
# XXX Not blackbox.
|
||||||
|
original_hook = _bootstrap._DefaultPathFinder._default_hook
|
||||||
|
mock_hook = import_util.mock_path_hook(path, importer=importer)
|
||||||
|
_bootstrap._DefaultPathFinder._default_hook = staticmethod(mock_hook)
|
||||||
|
try:
|
||||||
|
with util.import_state(path_importer_cache={path: None}):
|
||||||
|
loader = _bootstrap._DefaultPathFinder.find_module(module,
|
||||||
|
path=[path])
|
||||||
|
self.assert_(loader is importer)
|
||||||
|
finally:
|
||||||
|
_bootstrap._DefaultPathFinder._default_hook = original_hook
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
from test.support import run_unittest
|
from test.support import run_unittest
|
||||||
run_unittest(PathTests, __path__Tests, FinderTests)
|
run_unittest(FinderTests, DefaultPathFinderTests)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
test_main()
|
test_main()
|
||||||
|
|
|
@ -9,8 +9,8 @@ def import_(*args, **kwargs):
|
||||||
"""Delegate to allow for injecting different implementations of import."""
|
"""Delegate to allow for injecting different implementations of import."""
|
||||||
if using___import__:
|
if using___import__:
|
||||||
return __import__(*args, **kwargs)
|
return __import__(*args, **kwargs)
|
||||||
#return importlib.Import()(*args, **kwargs)
|
else:
|
||||||
return importlib._bootstrap._import(*args, **kwargs)
|
return importlib._bootstrap._import(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def importlib_only(fxn):
|
def importlib_only(fxn):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue