summaryrefslogtreecommitdiffstats
path: root/Lib/importlib/_bootstrap.py
diff options
context:
space:
mode:
authorBrett Cannon <brett@python.org>2012-04-22 23:58:33 (GMT)
committerBrett Cannon <brett@python.org>2012-04-22 23:58:33 (GMT)
commit938d44d59c9cc142d35f51a908cabf781b482f26 (patch)
tree93d2bd601f966f742ef2f6caca7b374e84f6a0b2 /Lib/importlib/_bootstrap.py
parent8c5e920ae3e98ebc5b37a105cf86e4c1e9649f57 (diff)
downloadcpython-938d44d59c9cc142d35f51a908cabf781b482f26.zip
cpython-938d44d59c9cc142d35f51a908cabf781b482f26.tar.gz
cpython-938d44d59c9cc142d35f51a908cabf781b482f26.tar.bz2
Issue #14605: Expose importlib.abc.FileLoader and
importlib.machinery.(FileFinder, SourceFileLoader, _SourcelessFileLoader, ExtensionFileLoader). This exposes all of importlib's mechanisms that will become public on the sys module.
Diffstat (limited to 'Lib/importlib/_bootstrap.py')
-rw-r--r--Lib/importlib/_bootstrap.py101
1 files changed, 47 insertions, 54 deletions
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
index 04ceb56..d9df2b7 100644
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -13,6 +13,9 @@ work. One should use importlib as the public-facing version of this module.
# reference any injected objects! This includes not only global code but also
# anything specified at the class level.
+# XXX Make sure all public names have no single leading underscore and all
+# others do.
+
# Bootstrap-related code ######################################################
@@ -283,7 +286,7 @@ def _check_name(method):
"""
def _check_name_wrapper(self, name, *args, **kwargs):
- if self._name != name:
+ if self.name != name:
raise ImportError("loader cannot handle %s" % name, name=name)
return method(self, name, *args, **kwargs)
_wrap(_check_name_wrapper, method)
@@ -423,7 +426,7 @@ class FrozenImporter:
class _LoaderBasics:
"""Base class of common code needed by both SourceLoader and
- _SourcelessFileLoader."""
+ SourcelessFileLoader."""
def is_package(self, fullname):
"""Concrete implementation of InspectLoader.is_package by checking if
@@ -608,7 +611,7 @@ class SourceLoader(_LoaderBasics):
return self._load_module(fullname)
-class _FileLoader:
+class FileLoader:
"""Base file loader class which implements the loader protocol methods that
require file system usage."""
@@ -616,13 +619,13 @@ class _FileLoader:
def __init__(self, fullname, path):
"""Cache the module name and the path to the file found by the
finder."""
- self._name = fullname
- self._path = path
+ self.name = fullname
+ self.path = path
@_check_name
def get_filename(self, fullname):
"""Return the path to the source file as found by the finder."""
- return self._path
+ return self.path
def get_data(self, path):
"""Return the data from path as raw bytes."""
@@ -630,7 +633,7 @@ class _FileLoader:
return file.read()
-class _SourceFileLoader(_FileLoader, SourceLoader):
+class SourceFileLoader(FileLoader, SourceLoader):
"""Concrete implementation of SourceLoader using the file system."""
@@ -668,7 +671,7 @@ class _SourceFileLoader(_FileLoader, SourceLoader):
pass
-class _SourcelessFileLoader(_FileLoader, _LoaderBasics):
+class _SourcelessFileLoader(FileLoader, _LoaderBasics):
"""Loader which handles sourceless file imports."""
@@ -692,7 +695,7 @@ class _SourcelessFileLoader(_FileLoader, _LoaderBasics):
return None
-class _ExtensionFileLoader:
+class ExtensionFileLoader:
"""Loader for extension modules.
@@ -701,8 +704,8 @@ class _ExtensionFileLoader:
"""
def __init__(self, name, path):
- self._name = name
- self._path = path
+ self.name = name
+ self.path = path
@_check_name
@set_package
@@ -711,8 +714,8 @@ class _ExtensionFileLoader:
"""Load an extension module."""
is_reload = fullname in sys.modules
try:
- module = _imp.load_dynamic(fullname, self._path)
- verbose_message('extension module loaded from {!r}', self._path)
+ module = _imp.load_dynamic(fullname, self.path)
+ verbose_message('extension module loaded from {!r}', self.path)
return module
except:
if not is_reload and fullname in sys.modules:
@@ -805,24 +808,25 @@ class PathFinder:
return None
-class _FileFinder:
+class FileFinder:
"""File-based finder.
- Constructor takes a list of objects detailing what file extensions their
- loader supports along with whether it can be used for a package.
+ Interactions with the file system are cached for performance, being
+ refreshed when the directory the finder is handling has been modified.
"""
def __init__(self, path, *details):
- """Initialize with finder details."""
+ """Initialize with the path to search on and a variable number of
+ 3-tuples containing the loader, file suffixes the loader recognizes, and
+ a boolean of whether the loader handles packages."""
packages = []
modules = []
- for detail in details:
- modules.extend((suffix, detail.loader) for suffix in detail.suffixes)
- if detail.supports_packages:
- packages.extend((suffix, detail.loader)
- for suffix in detail.suffixes)
+ for loader, suffixes, supports_packages in details:
+ modules.extend((suffix, loader) for suffix in suffixes)
+ if supports_packages:
+ packages.extend((suffix, loader) for suffix in suffixes)
self.packages = packages
self.modules = modules
# Base (directory) path
@@ -898,46 +902,29 @@ class _FileFinder:
if sys.platform.startswith(CASE_INSENSITIVE_PLATFORMS):
self._relaxed_path_cache = set(fn.lower() for fn in contents)
+ @classmethod
+ def path_hook(cls, *loader_details):
+ """A class method which returns a closure to use on sys.path_hook
+ which will return an instance using the specified loaders and the path
+ called on the closure.
-class _SourceFinderDetails:
-
- loader = _SourceFileLoader
- supports_packages = True
-
- def __init__(self):
- self.suffixes = _suffix_list(_imp.PY_SOURCE)
-
-class _SourcelessFinderDetails:
-
- loader = _SourcelessFileLoader
- supports_packages = True
-
- def __init__(self):
- self.suffixes = _suffix_list(_imp.PY_COMPILED)
-
+ If the path called on the closure is not a directory, ImportError is
+ raised.
-class _ExtensionFinderDetails:
+ """
+ def path_hook_for_FileFinder(path):
+ """Path hook for importlib.machinery.FileFinder."""
+ if not _path_isdir(path):
+ raise ImportError("only directories are supported", path=path)
+ return cls(path, *loader_details)
- loader = _ExtensionFileLoader
- supports_packages = False
+ return path_hook_for_FileFinder
- def __init__(self):
- self.suffixes = _suffix_list(_imp.C_EXTENSION)
# Import itself ###############################################################
-def _file_path_hook(path):
- """If the path is a directory, return a file-based finder."""
- if _path_isdir(path):
- return _FileFinder(path, _ExtensionFinderDetails(),
- _SourceFinderDetails(),
- _SourcelessFinderDetails())
- else:
- raise ImportError("only directories are supported", path=path)
-
-
-_DEFAULT_PATH_HOOK = _file_path_hook
+_DEFAULT_PATH_HOOK = None # Set in _setup()
class _DefaultPathFinder(PathFinder):
@@ -1209,6 +1196,12 @@ def _setup(sys_module, _imp_module):
if builtin_os == 'nt':
SOURCE_SUFFIXES.append('.pyw')
+ supported_loaders = [(ExtensionFileLoader, _suffix_list(3), False),
+ (SourceFileLoader, _suffix_list(1), True),
+ (_SourcelessFileLoader, _suffix_list(2), True)]
+ setattr(self_module, '_DEFAULT_PATH_HOOK',
+ FileFinder.path_hook(*supported_loaders))
+
def _install(sys_module, _imp_module):
"""Install importlib as the implementation of import.