diff options
author | Brett Cannon <bcannon@gmail.com> | 2010-06-27 23:57:46 (GMT) |
---|---|---|
committer | Brett Cannon <bcannon@gmail.com> | 2010-06-27 23:57:46 (GMT) |
commit | f23e3744412ac8943bddaace3dc0e85522518319 (patch) | |
tree | 39eff8e07cc68ba5fc7f21cc673cba34ca5742f4 /Doc | |
parent | 22d0830a11c0948ccfd2c42a73d55dd63e35ef72 (diff) | |
download | cpython-f23e3744412ac8943bddaace3dc0e85522518319.zip cpython-f23e3744412ac8943bddaace3dc0e85522518319.tar.gz cpython-f23e3744412ac8943bddaace3dc0e85522518319.tar.bz2 |
Implement importlib.abc.SourceLoader and deprecate PyLoader and PyPycLoader.
SourceLoader is a simplification of both PyLoader and PyPycLoader. If one only
wants to use source, then they need to only implement get_data and
get_filename. To also use bytecode -- sourceless loading is not supported --
then two abstract methods -- path_mtime and set_data -- need to be implemented.
Compared to PyLoader and PyPycLoader, there are less abstract methods
introduced and bytecode files become an optimization controlled by the ABC and
hidden from the user (this need came about as PEP 3147 showed that not treating
bytecode as an optimization can cause problems for compatibility).
PyLoader is deprecated in favor of SourceLoader. To be compatible from Python
3.1 onwards, a subclass need only use simple methods for source_path and
is_package. Otherwise conditional subclassing based on whether Python 3.1 or
Python 3.2 is being is the only change. The documentation and docstring for
PyLoader explain what is exactly needed.
PyPycLoader is deprecated also in favor of SourceLoader. Because PEP 3147
shifted bytecode path details so much, there is no foolproof way to provide
backwards-compatibility with SourceLoader. Because of this the class is simply
deprecated and users should move to SourceLoader (and optionally PyLoader for
Python 3.1). This does lead to a loss of support for sourceless loading
unfortunately.
At some point before Python 3.2 is released, SourceLoader will be moved over to
importlib._bootstrap so that the core code of importlib relies on the new code
instead of the old PyPycLoader code. This commit is being done now so that
there is no issue in having the API in Python 3.1a1.
Diffstat (limited to 'Doc')
-rw-r--r-- | Doc/library/importlib.rst | 218 |
1 files changed, 107 insertions, 111 deletions
diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index e89582b..300653a 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -18,12 +18,12 @@ implementation of the :keyword:`import` statement (and thus, by extension, the :func:`__import__` function) in Python source code. This provides an implementation of :keyword:`import` which is portable to any Python interpreter. This also provides a reference implementation which is easier to -comprehend than one in a programming language other than Python. +comprehend than one implemented in a programming language other than Python. -Two, the components to implement :keyword:`import` can be exposed in this +Two, the components to implement :keyword:`import` are exposed in this package, making it easier for users to create their own custom objects (known generically as an :term:`importer`) to participate in the import process. -Details on providing custom importers can be found in :pep:`302`. +Details on custom importers can be found in :pep:`302`. .. seealso:: @@ -37,7 +37,7 @@ Details on providing custom importers can be found in :pep:`302`. The :func:`.__import__` function The built-in function for which the :keyword:`import` statement is - syntactic sugar. + syntactic sugar for. :pep:`235` Import on Case-Insensitive Platforms @@ -46,7 +46,7 @@ Details on providing custom importers can be found in :pep:`302`. Defining Python Source Code Encodings :pep:`302` - New Import Hooks. + New Import Hooks :pep:`328` Imports: Multi-Line and Absolute/Relative @@ -66,8 +66,7 @@ Functions .. function:: __import__(name, globals={}, locals={}, fromlist=list(), level=0) - An implementation of the built-in :func:`__import__` function. See the - built-in function's documentation for usage instructions. + An implementation of the built-in :func:`__import__` function. .. function:: import_module(name, package=None) @@ -213,22 +212,108 @@ are also provided to help in implementing the core ABCs. .. method:: get_filename(fullname) - An abstract method that is to return the value for :attr:`__file__` for + An abstract method that is to return the value of :attr:`__file__` for the specified module. If no path is available, :exc:`ImportError` is raised. + If source code is available, then the method should return the path to + the source file, regardless of whether a bytecode was used to load the + module. + + +.. class:: SourceLoader + + An abstract base class for implementing source (and optionally bytecode) + file loading. The class inherits from both :class:`ResourceLoader` and + :class:`ExecutionLoader`, requiring the implementation of: + + * :meth:`ResourceLoader.get_data` + * :meth:`ExecutionLoader.get_filename` + Implement to only return the path to the source file; sourceless + loading is not supported. + + The abstract methods defined by this class are to add optional bytecode + file support. Not implementing these optional methods causes the loader to + only work with source code. Implementing the methods allows the loader to + work with source *and* bytecode files; it does not allow for *sourceless* + loading where only bytecode is provided. Bytecode files are an + optimization to speed up loading by removing the parsing step of Python's + compiler, and so no bytecode-specific API is exposed. + + .. method:: path_mtime(self, path) + + Optional abstract method which returns the modification time for the + specified path. + + .. method:: set_data(self, path, data) + + Optional abstract method which writes the specified bytes to a file + path. + + .. method:: get_code(self, fullname) + + Concrete implementation of :meth:`InspectLoader.get_code`. + + .. method:: load_module(self, fullname) + + Concrete implementation of :meth:`Loader.load_module`. + + .. method:: get_source(self, fullname) + + Concrete implementation of :meth:`InspectLoader.get_source`. + + .. method:: is_package(self, fullname) + + Concrete implementation of :meth:`InspectLoader.is_package`. A module + is determined to be a package if its file path is a file named + ``__init__`` when the file extension is removed. + .. class:: PyLoader An abstract base class inheriting from - :class:`importlib.abc.ExecutionLoader` and - :class:`importlib.abc.ResourceLoader` designed to ease the loading of + :class:`ExecutionLoader` and + :class:`ResourceLoader` designed to ease the loading of Python source modules (bytecode is not handled; see - :class:`importlib.abc.PyPycLoader` for a source/bytecode ABC). A subclass + :class:`SourceLoader` for a source/bytecode ABC). A subclass implementing this ABC will only need to worry about exposing how the source code is stored; all other details for loading Python source code will be handled by the concrete implementations of key methods. + .. deprecated:: 3.2 + This class has been deprecated in favor of :class:`SourceLoader` and is + slated for removal in Python 3.4. See below for how to create a + subclass that is compatbile with Python 3.1 onwards. + + If compatibility with Python 3.1 is required, then use the following idiom + to implement a subclass that will work with Python 3.1 onwards (make sure + to implement :meth:`ExecutionLoader.get_filename`):: + + try: + from importlib.abc import SourceLoader + except ImportError: + from importlib.abc import PyLoader as SourceLoader + + + class CustomLoader(SourceLoader): + def get_filename(self, fullname): + """Return the path to the source file.""" + # Implement ... + + def source_path(self, fullname): + """Implement source_path in terms of get_filename.""" + try: + return self.get_filename(fullname) + except ImportError: + return None + + def is_package(self, fullname): + """Implement is_package by looking for an __init__ file + name as returned by get_filename.""" + filename = os.path.basename(self.get_filename(fullname)) + return os.path.splitext(filename)[0] == '__init__' + + .. method:: source_path(fullname) An abstract method that returns the path to the source code for a @@ -270,10 +355,18 @@ are also provided to help in implementing the core ABCs. .. class:: PyPycLoader - An abstract base class inheriting from :class:`importlib.abc.PyLoader`. + An abstract base class inheriting from :class:`PyLoader`. This ABC is meant to help in creating loaders that support both Python source and bytecode. + .. deprecated:: 3.2 + This class has been deprecated in favor of :class:`SourceLoader` and to + properly support :pep:`3147`. If compatibility is required with + Python 3.1, implement both :class:`SourceLoader` and :class:`PyLoader`; + instructions on how to do so are included in the documentation for + :class:`PyLoader`. Do note that this solution will not support + sourceless/bytecode-only loading; only source *and* bytecode loading. + .. method:: source_mtime(fullname) An abstract method which returns the modification time for the source @@ -292,8 +385,8 @@ are also provided to help in implementing the core ABCs. .. method:: get_filename(fullname) A concrete implementation of - :meth:`importlib.abc.ExecutionLoader.get_filename` that relies on - :meth:`importlib.abc.PyLoader.source_path` and :meth:`bytecode_path`. + :meth:`ExecutionLoader.get_filename` that relies on + :meth:`PyLoader.source_path` and :meth:`bytecode_path`. If :meth:`source_path` returns a path, then that value is returned. Else if :meth:`bytecode_path` returns a path, that path will be returned. If a path is not available from both methods, @@ -420,100 +513,3 @@ an :term:`importer`. attribute to be used at the global level of the module during initialization. - -Example -------- - -Below is an example meta path importer that uses a dict for back-end storage -for source code. While not an optimal solution -- manipulations of -:attr:`__path__` on packages does not influence import -- it does illustrate -what little is required to implement an importer. - -.. testcode:: - - """An importer where source is stored in a dict.""" - from importlib import abc - - - class DictImporter(abc.Finder, abc.PyLoader): - - """A meta path importer that stores source code in a dict. - - The keys are the module names -- packages must end in ``.__init__``. - The values must be something that can be passed to 'bytes'. - - """ - - def __init__(self, memory): - """Store the dict.""" - self.memory = memory - - def contains(self, name): - """See if a module or package is in the dict.""" - if name in self.memory: - return name - package_name = '{}.__init__'.format(name) - if package_name in self.memory: - return package_name - return False - - __contains__ = contains # Convenience. - - def find_module(self, fullname, path=None): - """Find the module in the dict.""" - if fullname in self: - return self - return None - - def source_path(self, fullname): - """Return the module name if the module is in the dict.""" - if not fullname in self: - raise ImportError - return fullname - - def get_data(self, path): - """Return the bytes for the source. - - The value found in the dict is passed through 'bytes' before being - returned. - - """ - name = self.contains(path) - if not name: - raise IOError - return bytes(self.memory[name]) - - def is_package(self, fullname): - """Tell if module is a package based on whether the dict contains the - name with ``.__init__`` appended to it.""" - if fullname not in self: - raise ImportError - if fullname in self.memory: - return False - # If name is in this importer but not as it is then it must end in - # ``__init__``. - else: - return True - -.. testcode:: - :hide: - - import importlib - import sys - - - # Build the dict; keys of name, value of __package__. - names = {'_top_level': '', '_pkg.__init__': '_pkg', '_pkg.mod': '_pkg'} - source = {name: "name = {!r}".format(name).encode() for name in names} - - # Register the meta path importer. - importer = DictImporter(source) - sys.meta_path.append(importer) - - # Sanity check. - for name in names: - module = importlib.import_module(name) - assert module.__name__ == name - assert getattr(module, 'name') == name - assert module.__loader__ is importer - assert module.__package__ == names[name] |