From 2dee597e0593a8f7b477f58afe5e46f94b994541 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 21 Feb 2009 03:15:37 +0000 Subject: [PATCH] Do some cleanup in importlib: + Ditch using arguments to super(). + Ditch subclassing from object directly. + Move directory check out of chaining path hook to file path hook/finder. + Rename some classes to better reflect they are finders, not importers. --- Lib/importlib/_bootstrap.py | 100 ++++++++---------- .../test/extension/test_case_sensitivity.py | 2 +- Lib/importlib/test/extension/test_finder.py | 2 +- .../test/extension/test_path_hook.py | 2 +- Lib/importlib/test/import_/test_path.py | 6 +- .../test/source/test_case_sensitivity.py | 2 +- Lib/importlib/test/source/test_finder.py | 2 +- Lib/importlib/test/source/test_path_hook.py | 2 +- 8 files changed, 56 insertions(+), 62 deletions(-) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 65e9f1786e..4db61da079 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -174,51 +174,43 @@ class FrozenImporter: raise -class ChainedImporter(object): +def chained_path_hook(*path_hooks): + """Create a closure which sequentially checks path hooks to see which ones + (if any) can work with a path.""" + def path_hook(entry): + """Check to see if 'entry' matches any of the enclosed path hooks.""" + finders = [] + for hook in path_hooks: + try: + finder = hook(entry) + except ImportError: + continue + else: + finders.append(finder) + if not finders: + raise ImportError("no finder found") + else: + return ChainedFinder(*finders) + + return path_hook + + +class ChainedFinder: """Finder that sequentially calls other finders.""" - def __init__(self, *importers): - self._importers = importers + def __init__(self, *finders): + self._finders = finders def find_module(self, fullname, path=None): - for importer in self._importers: - result = importer.find_module(fullname, path) + for finder in self._finders: + result = finder.find_module(fullname, path) if result: return result else: return None -# XXX Don't make filesystem-specific and instead make generic for any path -# hooks. -def chaining_fs_path_hook(*path_hooks): - """Create a closure which calls the path hooks sequentially looking for - which path hooks can handle a path entry. - - - Passed-in path hooks work as any other path hooks, raising ImportError if - they cannot handle the path, otherwise returning a finder. - - """ - def chained_fs_path_hook(path_entry): - """Closure which sees which of the captured path hooks can handle the - path entry.""" - absolute_path = _path_absolute(path_entry) - if not _path_isdir(absolute_path): - raise ImportError("only directories are supported") - accepted = [] - for path_hook in path_hooks: - try: - accepted.append(path_hook(absolute_path)) - except ImportError: - continue - if not accepted: - raise ImportError("no path hooks could handle %s" % path_entry) - return ChainedImporter(*accepted) - return chained_fs_path_hook - - def check_name(method): """Decorator to verify that the module being requested matches the one the loader can handle. @@ -235,11 +227,11 @@ def check_name(method): return inner -class _ExtensionFileLoader(object): +class _ExtensionFileLoader: """Loader for extension modules. - The constructor is designed to work with FileImporter. + The constructor is designed to work with FileFinder. """ @@ -323,10 +315,10 @@ def module_for_loader(fxn): return decorated -class _PyFileLoader(object): +class _PyFileLoader: # XXX Still smart to have this as a separate class? Or would it work - # better to integrate with PyFileImporter? Could cache _is_pkg info. - # FileImporter can be changed to return self instead of a specific loader + # better to integrate with PyFileFinder? Could cache _is_pkg info. + # FileFinder can be changed to return self instead of a specific loader # call. Otherwise _base_path can be calculated on the fly without issue if # it is known whether a module should be treated as a path or package to # minimize stat calls. Could even go as far as to stat the directory the @@ -515,9 +507,9 @@ class _PyFileLoader(object): return self._is_pkg -class FileImporter(object): +class FileFinder: - """Base class for file importers. + """Base class for file finders. Subclasses are expected to define the following attributes: @@ -541,10 +533,13 @@ class FileImporter(object): Can be used as an entry on sys.path_hook. """ - self._path_entry = path_entry + absolute_path = _path_absolute(path_entry) + if not _path_isdir(absolute_path): + raise ImportError("only directories are supported") + self._path_entry = absolute_path def find_module(self, fullname, path=None): - tail_module = fullname.rsplit('.', 1)[-1] + tail_module = fullname.rpartition('.')[2] package_directory = None if self._possible_package: for ext in self._suffixes: @@ -571,7 +566,7 @@ class FileImporter(object): return None -class ExtensionFileImporter(FileImporter): +class ExtensionFileFinder(FileFinder): """Importer for extension files.""" @@ -582,10 +577,10 @@ class ExtensionFileImporter(FileImporter): # Assigning to _suffixes here instead of at the class level because # imp is not imported at the time of class creation. self._suffixes = suffix_list(imp.C_EXTENSION) - super(ExtensionFileImporter, self).__init__(path_entry) + super().__init__(path_entry) -class PyFileImporter(FileImporter): +class PyFileFinder(FileFinder): """Importer for source/bytecode files.""" @@ -598,7 +593,7 @@ class PyFileImporter(FileImporter): # optimization by the loader. self._suffixes = suffix_list(imp.PY_SOURCE) self._suffixes += suffix_list(imp.PY_COMPILED) - super(PyFileImporter, self).__init__(path_entry) + super().__init__(path_entry) class PathFinder: @@ -664,31 +659,30 @@ class PathFinder: return None +_DEFAULT_PATH_HOOK = chained_path_hook(ExtensionFileFinder, PyFileFinder) + 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] + implicit_hooks = [_DEFAULT_PATH_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) + return super()._path_importer_cache(path, _DEFAULT_PATH_HOOK) -class ImportLockContext(object): +class ImportLockContext: """Context manager for the import lock.""" diff --git a/Lib/importlib/test/extension/test_case_sensitivity.py b/Lib/importlib/test/extension/test_case_sensitivity.py index eb05ad3b9a..39e6facc99 100644 --- a/Lib/importlib/test/extension/test_case_sensitivity.py +++ b/Lib/importlib/test/extension/test_case_sensitivity.py @@ -13,7 +13,7 @@ class ExtensionModuleCaseSensitivityTest(unittest.TestCase): good_name = ext_util.NAME bad_name = good_name.upper() assert good_name != bad_name - finder = importlib.ExtensionFileImporter(ext_util.PATH) + finder = importlib.ExtensionFileFinder(ext_util.PATH) return finder.find_module(bad_name) def test_case_sensitive(self): diff --git a/Lib/importlib/test/extension/test_finder.py b/Lib/importlib/test/extension/test_finder.py index 7321e312bb..3fc9b0b77e 100644 --- a/Lib/importlib/test/extension/test_finder.py +++ b/Lib/importlib/test/extension/test_finder.py @@ -9,7 +9,7 @@ class FinderTests(abc.FinderTests): """Test the finder for extension modules.""" def find_module(self, fullname): - importer = importlib.ExtensionFileImporter(util.PATH) + importer = importlib.ExtensionFileFinder(util.PATH) return importer.find_module(fullname) def test_module(self): diff --git a/Lib/importlib/test/extension/test_path_hook.py b/Lib/importlib/test/extension/test_path_hook.py index 674fd90317..76f4995ad8 100644 --- a/Lib/importlib/test/extension/test_path_hook.py +++ b/Lib/importlib/test/extension/test_path_hook.py @@ -14,7 +14,7 @@ class PathHookTests(unittest.TestCase): # XXX Should it only work for directories containing an extension module? def hook(self, entry): - return importlib.ExtensionFileImporter(entry) + return importlib.ExtensionFileFinder(entry) def test_success(self): # Path hook should handle a directory where a known extension module diff --git a/Lib/importlib/test/import_/test_path.py b/Lib/importlib/test/import_/test_path.py index 8cf5699764..3d9b1a1ccd 100644 --- a/Lib/importlib/test/import_/test_path.py +++ b/Lib/importlib/test/import_/test_path.py @@ -87,16 +87,16 @@ class DefaultPathFinderTests(unittest.TestCase): importer = util.mock_modules(module) path = '' # XXX Not blackbox. - original_hook = _bootstrap._DefaultPathFinder._default_hook + original_hook = _bootstrap._DEFAULT_PATH_HOOK mock_hook = import_util.mock_path_hook(path, importer=importer) - _bootstrap._DefaultPathFinder._default_hook = staticmethod(mock_hook) + _bootstrap._DEFAULT_PATH_HOOK = 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 + _bootstrap._DEFAULT_PATH_HOOK = original_hook def test_main(): diff --git a/Lib/importlib/test/source/test_case_sensitivity.py b/Lib/importlib/test/source/test_case_sensitivity.py index 6bd86cfcd9..df6d3bcff6 100644 --- a/Lib/importlib/test/source/test_case_sensitivity.py +++ b/Lib/importlib/test/source/test_case_sensitivity.py @@ -19,7 +19,7 @@ class CaseSensitivityTest(unittest.TestCase): assert name != name.lower() def find(self, path): - finder = importlib.PyFileImporter(path) + finder = importlib.PyFileFinder(path) return finder.find_module(self.name) def sensitivity_test(self): diff --git a/Lib/importlib/test/source/test_finder.py b/Lib/importlib/test/source/test_finder.py index 0f1f549baa..0cfb14f3e1 100644 --- a/Lib/importlib/test/source/test_finder.py +++ b/Lib/importlib/test/source/test_finder.py @@ -32,7 +32,7 @@ class FinderTests(abc.FinderTests): """ def import_(self, root, module): - finder = importlib.PyFileImporter(root) + finder = importlib.PyFileFinder(root) return finder.find_module(module) def run_test(self, test, create=None, *, compile_=None, unlink=None): diff --git a/Lib/importlib/test/source/test_path_hook.py b/Lib/importlib/test/source/test_path_hook.py index 5aac42e5ff..9a4ccbe3d7 100644 --- a/Lib/importlib/test/source/test_path_hook.py +++ b/Lib/importlib/test/source/test_path_hook.py @@ -10,7 +10,7 @@ class PathHookTest(unittest.TestCase): def test_success(self): # XXX Only work on existing directories? with source_util.create_modules('dummy') as mapping: - self.assert_(hasattr(importlib.FileImporter(mapping['.root']), + self.assert_(hasattr(importlib.FileFinder(mapping['.root']), 'find_module')) -- 2.40.0