[Python-checkins] r74110 - in python/branches/py3k: Doc/library/importlib.rst Lib/importlib/_bootstrap.py Lib/importlib/abc.py Lib/importlib/test/source/test_abc_loader.py Lib/importlib/test/test_abc.py Misc/NEWS

brett.cannon python-checkins at python.org
Mon Jul 20 06:23:49 CEST 2009


Author: brett.cannon
Date: Mon Jul 20 06:23:48 2009
New Revision: 74110

Log:
Implement the PEP 302 protocol for get_filename() as
importlib.abc.ExecutionLoader. PyLoader now inherits from this ABC instead of
InspectLoader directly. Both PyLoader and PyPycLoader provide concrete
implementations of get_filename in terms of source_path and bytecode_path.


Modified:
   python/branches/py3k/Doc/library/importlib.rst
   python/branches/py3k/Lib/importlib/_bootstrap.py
   python/branches/py3k/Lib/importlib/abc.py
   python/branches/py3k/Lib/importlib/test/source/test_abc_loader.py
   python/branches/py3k/Lib/importlib/test/test_abc.py
   python/branches/py3k/Misc/NEWS

Modified: python/branches/py3k/Doc/library/importlib.rst
==============================================================================
--- python/branches/py3k/Doc/library/importlib.rst	(original)
+++ python/branches/py3k/Doc/library/importlib.rst	Mon Jul 20 06:23:48 2009
@@ -202,10 +202,24 @@
         :term:`loader` cannot find the module.
 
 
+.. class:: ExecutionLoader
+
+    An abstract base class which inherits from :class:`InspectLoader` that,
+    when implemented, allows a module to be executed as a script. The ABC
+    represents an optional :pep:`302` protocol.
+
+    .. method:: get_filename(fullname)
+
+        An abstract method that is to return the value for :attr:`__file__` for
+        the specified module. If no path is available, :exc:`ImportError` is
+        raised.
+
+
 .. class:: PyLoader
 
-    An abstract base class inheriting from :class:`importlib.abc.InspectLoader`
-    and :class:`importlib.abc.ResourceLoader` designed to ease the loading of
+    An abstract base class inheriting from
+    :class:`importlib.abc.ExecutionLoader` and
+    :class:`importlib.abc.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
     implementing this ABC will only need to worry about exposing how the source
@@ -218,6 +232,13 @@
         module. Should return :keyword:`None` if there is no source code.
         :exc:`ImportError` if the module cannot be found.
 
+    .. method:: get_filename(fullname)
+
+        A concrete implementation of
+        :meth:`importlib.abc.ExecutionLoader.get_filename` that
+        relies on :meth:`source_path`. If :meth:`source_path` returns
+        :keyword:`None`, then :exc:`ImportError` is raised.
+
     .. method:: load_module(fullname)
 
         A concrete implementation of :meth:`importlib.abc.Loader.load_module`
@@ -238,8 +259,8 @@
 
         A concrete implementation of
         :meth:`importlib.abc.InspectLoader.get_source`. Uses
-        :meth:`importlib.abc.InspectLoader.get_data` and :meth:`source_path` to
-        get the source code.  It tries to guess the source encoding using
+        :meth:`importlib.abc.ResourceLoader.get_data` and :meth:`source_path`
+        to get the source code.  It tries to guess the source encoding using
         :func:`tokenize.detect_encoding`.
 
 
@@ -253,7 +274,7 @@
 
         An abstract method which returns the modification time for the source
         code of the specified module. The modification time should be an
-        integer. If there is no source code, return :keyword:`None. If the
+        integer. If there is no source code, return :keyword:`None`. If the
         module cannot be found then :exc:`ImportError` is raised.
 
     .. method:: bytecode_path(fullname)
@@ -263,6 +284,16 @@
         if no bytecode exists (yet).
         Raises :exc:`ImportError` if the module is not found.
 
+    .. 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`.
+        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,
+        :exc:`ImportError` is raised.
+
     .. method:: write_bytecode(fullname, bytecode)
 
         An abstract method which has the loader write *bytecode* for future

Modified: python/branches/py3k/Lib/importlib/_bootstrap.py
==============================================================================
--- python/branches/py3k/Lib/importlib/_bootstrap.py	(original)
+++ python/branches/py3k/Lib/importlib/_bootstrap.py	Mon Jul 20 06:23:48 2009
@@ -315,16 +315,10 @@
 
     @module_for_loader
     def load_module(self, module):
-        """Load a source module."""
-        return self._load_module(module)
-
-    def _load_module(self, module):
-        """Initialize a module from source."""
+        """Initialize the module."""
         name = module.__name__
         code_object = self.get_code(module.__name__)
-        # __file__ may have been set by the caller, e.g. bytecode path.
-        if not hasattr(module, '__file__'):
-            module.__file__ = self.source_path(name)
+        module.__file__ = self.get_filename(name)
         if self.is_package(name):
             module.__path__  = [module.__file__.rsplit(path_sep, 1)[0]]
         module.__package__ = module.__name__
@@ -334,6 +328,15 @@
         exec(code_object, module.__dict__)
         return module
 
+    def get_filename(self, fullname):
+        """Return the path to the source file, else raise ImportError."""
+        path = self.source_path(fullname)
+        if path is not None:
+            return path
+        else:
+            raise ImportError("no source path available for "
+                                "{0!r}".format(fullname))
+
     def get_code(self, fullname):
         """Get a code object from source."""
         source_path = self.source_path(fullname)
@@ -388,15 +391,16 @@
 
     """
 
-    @module_for_loader
-    def load_module(self, module):
-        """Load a module from source or bytecode."""
-        name = module.__name__
-        source_path = self.source_path(name)
-        bytecode_path = self.bytecode_path(name)
-        # get_code can worry about no viable paths existing.
-        module.__file__ = source_path or bytecode_path
-        return self._load_module(module)
+    def get_filename(self, fullname):
+        """Return the source or bytecode file path."""
+        path = self.source_path(fullname)
+        if path is not None:
+            return path
+        path = self.bytecode_path(fullname)
+        if path is not None:
+            return path
+        raise ImportError("no source or bytecode path available for "
+                            "{0!r}".format(fullname))
 
     def get_code(self, fullname):
         """Get a code object from source or bytecode."""

Modified: python/branches/py3k/Lib/importlib/abc.py
==============================================================================
--- python/branches/py3k/Lib/importlib/abc.py	(original)
+++ python/branches/py3k/Lib/importlib/abc.py	Mon Jul 20 06:23:48 2009
@@ -76,7 +76,23 @@
 InspectLoader.register(machinery.FrozenImporter)
 
 
-class PyLoader(_bootstrap.PyLoader, ResourceLoader, InspectLoader):
+class ExecutionLoader(InspectLoader):
+
+    """Abstract base class for loaders that wish to support the execution of
+    modules as scripts.
+
+    This ABC represents one of the optional protocols specified in PEP 302.
+
+    """
+
+    @abc.abstractmethod
+    def get_filename(self, fullname:str) -> str:
+        """Abstract method which should return the value that __file__ is to be
+        set to."""
+        raise NotImplementedError
+
+
+class PyLoader(_bootstrap.PyLoader, ResourceLoader, ExecutionLoader):
 
     """Abstract base class to assist in loading source code by requiring only
     back-end storage methods to be implemented.

Modified: python/branches/py3k/Lib/importlib/test/source/test_abc_loader.py
==============================================================================
--- python/branches/py3k/Lib/importlib/test/source/test_abc_loader.py	(original)
+++ python/branches/py3k/Lib/importlib/test/source/test_abc_loader.py	Mon Jul 20 06:23:48 2009
@@ -218,6 +218,21 @@
         with util.uncache(name), self.assertRaises(ImportError):
             mock.load_module(name)
 
+    def test_get_filename_with_source_path(self):
+        # get_filename() should return what source_path() returns.
+        name = 'mod'
+        path = os.path.join('path', 'to', 'source')
+        mock = PyLoaderMock({name: path})
+        with util.uncache(name):
+            self.assertEqual(mock.get_filename(name), path)
+
+    def test_get_filename_no_source_path(self):
+        # get_filename() should raise ImportError if source_path returns None.
+        name = 'mod'
+        mock = PyLoaderMock({name: None})
+        with util.uncache(name), self.assertRaises(ImportError):
+            mock.get_filename(name)
+
 
 class PyLoaderGetSourceTests(unittest.TestCase):
 
@@ -283,6 +298,38 @@
         super().test_unloadable()
 
 
+class PyPycLoaderInterfaceTests(unittest.TestCase):
+
+    """Test for the interface of importlib.abc.PyPycLoader."""
+
+    def get_filename_check(self, src_path, bc_path, expect):
+        name = 'mod'
+        mock = PyPycLoaderMock({name: src_path}, {name: {'path': bc_path}})
+        with util.uncache(name):
+            assert mock.source_path(name) == src_path
+            assert mock.bytecode_path(name) == bc_path
+            self.assertEqual(mock.get_filename(name), expect)
+
+    def test_filename_with_source_bc(self):
+        # When source and bytecode paths present, return the source path.
+        self.get_filename_check('source_path', 'bc_path', 'source_path')
+
+    def test_filename_with_source_no_bc(self):
+        # With source but no bc, return source path.
+        self.get_filename_check('source_path', None, 'source_path')
+
+    def test_filename_with_no_source_bc(self):
+        # With not source but bc, return the bc path.
+        self.get_filename_check(None, 'bc_path', 'bc_path')
+
+    def test_filename_with_no_source_or_bc(self):
+        # With no source or bc, raise ImportError.
+        name = 'mod'
+        mock = PyPycLoaderMock({name: None}, {name: {'path': None}})
+        with util.uncache(name), self.assertRaises(ImportError):
+            mock.get_filename(name)
+
+
 class SkipWritingBytecodeTests(unittest.TestCase):
 
     """Test that bytecode is properly handled based on
@@ -421,9 +468,9 @@
 def test_main():
     from test.support import run_unittest
     run_unittest(PyLoaderTests, PyLoaderInterfaceTests, PyLoaderGetSourceTests,
-                    PyPycLoaderTests, SkipWritingBytecodeTests,
-                    RegeneratedBytecodeTests, BadBytecodeFailureTests,
-                    MissingPathsTests)
+                    PyPycLoaderTests, PyPycLoaderInterfaceTests,
+                    SkipWritingBytecodeTests, RegeneratedBytecodeTests,
+                    BadBytecodeFailureTests, MissingPathsTests)
 
 
 if __name__ == '__main__':

Modified: python/branches/py3k/Lib/importlib/test/test_abc.py
==============================================================================
--- python/branches/py3k/Lib/importlib/test/test_abc.py	(original)
+++ python/branches/py3k/Lib/importlib/test/test_abc.py	Mon Jul 20 06:23:48 2009
@@ -53,9 +53,15 @@
                     machinery.FrozenImporter]
 
 
+class ExecutionLoader(InheritanceTests, unittest.TestCase):
+
+    superclasses = [abc.InspectLoader]
+    subclasses = [abc.PyLoader]
+
+
 class PyLoader(InheritanceTests, unittest.TestCase):
 
-    superclasses = [abc.Loader, abc.ResourceLoader, abc.InspectLoader]
+    superclasses = [abc.Loader, abc.ResourceLoader, abc.ExecutionLoader]
 
 
 class PyPycLoader(InheritanceTests, unittest.TestCase):

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Mon Jul 20 06:23:48 2009
@@ -43,6 +43,11 @@
 Library
 -------
 
+- Add importlib.abc.ExecutionLoader to represent the PEP 302 protocol for
+  loaders that allow for modules to be executed. Both importlib.abc.PyLoader
+  and PyPycLoader inherit from this class and provide implementations in
+  relation to other methods required by the ABCs.
+
 - importlib.abc.PyLoader did not inherit from importlib.abc.ResourceLoader like
   the documentation said it did even though the code in PyLoader relied on the
   abstract method required by ResourceLoader.


More information about the Python-checkins mailing list