[Python-checkins] r56881 - in sandbox/trunk/import_in_py: NOTES _importlib.py tests/test_path_hooks.py

brett.cannon python-checkins at python.org
Fri Aug 10 03:42:10 CEST 2007


Author: brett.cannon
Date: Fri Aug 10 03:42:09 2007
New Revision: 56881

Added:
   sandbox/trunk/import_in_py/NOTES   (contents, props changed)
   sandbox/trunk/import_in_py/tests/test_path_hooks.py   (contents, props changed)
Modified:
   sandbox/trunk/import_in_py/_importlib.py
Log:
Implement a generic chaining path hook that returns a chaining importer.
Include tests for the path hook (but not for the importer yet).

The eventual goal is a reworking of the filesystem importer and loader so that
it is not quite so generic in order to have a more reasonable API for loaders
as expected by the py/pyc handler.


Added: sandbox/trunk/import_in_py/NOTES
==============================================================================
--- (empty file)
+++ sandbox/trunk/import_in_py/NOTES	Fri Aug 10 03:42:09 2007
@@ -0,0 +1,28 @@
+py/pyc loader API (beyond load_module):
+* mod_time(module_name) -> int
+    Find out the modification time for a source module.  If no source module
+    exists, raise XXX.
+* source(module_name) -> str
+    Return the text for a source module.
+    XXX Can this be replaced by get_source?  Only if expected to be defined on
+    a loader.
+* bytecode(module_name) -> bytes|str
+    Return the bytes for a bytecode module.
+    XXX Rename to get_bytecode if get_source is viable for source().
+* write_bytecode(module_name, data:bytes|str) -> bool
+    Write out the new representation of a bytecode module.  Returns a boolean
+    representing whether the bytes were actually written out or not.
+
+
+Handler API:
+* cannot_handle(name:str) -> bool
+    Can the handler handle with the specified module?
+* handle_code(loader:object, mod_name:str, file_path:str, 
+                source:bool, bytecode:bool,
+                package_path:str|None=None) -> object
+    Handle the loading of the specified module.  All needed objects to be set
+    as attributes on the module are passed in as arguments (__file__,
+    __loader__, and __path__).  Also any other information that must be known
+    for any proper import to occur is specified up front.  All other
+    information that is optional and varies depending on what the loader can
+    provide is done through a separate API provided by the loader.

Modified: sandbox/trunk/import_in_py/_importlib.py
==============================================================================
--- sandbox/trunk/import_in_py/_importlib.py	(original)
+++ sandbox/trunk/import_in_py/_importlib.py	Fri Aug 10 03:42:09 2007
@@ -161,6 +161,44 @@
         self._load = imp.init_frozen
 
 
+class ChainedImporter(object):
+
+    """Importer that sequentially calls other importers."""
+
+    def __init__(self, *importers):
+        self._importers = importers
+
+    def find_module(fullname, path=None):
+        for importer in self._importers:
+            result = importer.find_module(fullname, path)
+            if result:
+                return result
+        else:
+            return None
+
+
+def chaining_fs_path_hook(*path_hooks):
+    """Create a path hook that sequentially asks other path hooks if they can
+    handle a sys.path entry, returning a chained importer for those that said
+    they could."""
+    def chained_fs_path_hook(path_entry):
+        """Check which of the associated importers are able to handle the
+        filesystem 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
+
+
 class FileSystemFactory(object):
 
     """Factory function for sys.path_hooks for directory entries on sys.path.

Added: sandbox/trunk/import_in_py/tests/test_path_hooks.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/import_in_py/tests/test_path_hooks.py	Fri Aug 10 03:42:09 2007
@@ -0,0 +1,61 @@
+import importlib
+from tests import mock_importlib
+
+import os
+from test import test_support
+import unittest
+
+
+class ChainingPathHook(unittest.TestCase):
+
+    def test_single_hook(self):
+        # A single hook argument should work as expected.
+        hook = importlib.chaining_fs_path_hook(mock_importlib.SucceedImporter())
+        self.assert_(hook('.'))
+        hook = importlib.chaining_fs_path_hook(mock_importlib.PassImporter())
+        self.assertRaises(ImportError, hook, '.')
+
+    def test_path_acceptance(self):
+        # Valid paths should be accepted, but non-existent paths should be
+        # rejected outright.
+        succeed_hook = mock_importlib.SucceedImporter()
+        hook = importlib.chaining_fs_path_hook(succeed_hook)
+        self.assert_(hook('.'))
+        self.assertRaises(ImportError, hook, '_nonexistentdirectory')
+
+    def test_multiple_hooks(self):
+        # Multiple hooks should work.
+        succeed_hook = mock_importlib.SucceedImporter()
+        pass_hook = mock_importlib.PassImporter()
+        hook = importlib.chaining_fs_path_hook(pass_hook, succeed_hook)
+        self.assert_(hook('.'))
+        hook = importlib.chaining_fs_path_hook(succeed_hook, pass_hook)
+        self.assert_(hook('.'))
+        hook = importlib.chaining_fs_path_hook(pass_hook, pass_hook)
+        self.assertRaises(ImportError, hook, '.')
+
+    # Dependent on implementation details.
+    def test_order_preservation(self):
+        # The order of importers to call should be maintained.
+        succeed1 = mock_importlib.SucceedImporter()
+        succeed2 = mock_importlib.SucceedImporter()
+        hook = importlib.chaining_fs_path_hook(succeed1, succeed2)
+        importer = hook('.')
+        self.assert_(importer._importers[0] is succeed1)
+        self.assert_(importer._importers[1] is succeed2)
+
+    def test_absolute_path(self):
+        # The path hooks that are called should be passed an absolute path.
+        succeed = mock_importlib.SucceedImporter()
+        hook = importlib.chaining_fs_path_hook(succeed)
+        hook('.')
+        absolute_path = os.path.abspath('.')
+        self.assertEquals(os.path.normpath(succeed.path_entries[0]), absolute_path)
+
+
+def test_main():
+    test_support.run_unittest(ChainingPathHook)
+
+
+if __name__ == '__main__':
+    test_main()


More information about the Python-checkins mailing list