[Python-checkins] r57306 - in sandbox/trunk/import_in_py: Py3K_TODO _importlib.py controlled_importlib.py tests/test_fs_loader.py

brett.cannon python-checkins at python.org
Thu Aug 23 02:03:06 CEST 2007


Author: brett.cannon
Date: Thu Aug 23 02:03:06 2007
New Revision: 57306

Modified:
   sandbox/trunk/import_in_py/Py3K_TODO
   sandbox/trunk/import_in_py/_importlib.py
   sandbox/trunk/import_in_py/controlled_importlib.py
   sandbox/trunk/import_in_py/tests/test_fs_loader.py
Log:
Make the source loader stateless.  Ups the amount of stat calls that need to be
made slightly, but it is not atrocious.


Modified: sandbox/trunk/import_in_py/Py3K_TODO
==============================================================================
--- sandbox/trunk/import_in_py/Py3K_TODO	(original)
+++ sandbox/trunk/import_in_py/Py3K_TODO	Thu Aug 23 02:03:06 2007
@@ -4,8 +4,8 @@
 - 2.6-specific
     * Be backwards-compatible.
         + All tests must pass!
-            - importlib tests.
-            - 2.6 stdlib tests using importlib (see regrtest.sh).
+            - 2.6 stdlib tests using importlib.
+                * Use regrtest.sh editing so all tests are run.
     * Rewrite zipimport.
     * Put into 2.6 stdlib.
 - Py3K-specific

Modified: sandbox/trunk/import_in_py/_importlib.py
==============================================================================
--- sandbox/trunk/import_in_py/_importlib.py	(original)
+++ sandbox/trunk/import_in_py/_importlib.py	Thu Aug 23 02:03:06 2007
@@ -95,9 +95,13 @@
     """Replacement for os.path.isdir."""
     return _path_is_mode_type(path, 0040000)
 
-def _path_without_ext(path):
+def _path_without_ext(path, ext_type):
     """Replacement for os.path.splitext()[0]."""
-    return path.rsplit('.', 1)[0]
+    for suffix in suffix_list(ext_type):
+        if path.endswith(suffix):
+            return path[:-len(suffix)]
+    else:
+        raise ValueError("path is not of the specified type")
 
 def _path_absolute(path):
     """Replacement for os.path.abspath."""
@@ -286,7 +290,9 @@
     code object in a two-item sequence."""
     # Request paths instead of just booleans since 'compile' needs it for
     # source.
-    assert source_path or bytecode_path
+    if not source_path and not bytecode_path:
+        raise ValueError("neither source nor bytecode was specified as "
+                            "available")
     source_timestamp = None
     # Try to use bytecode if it is available.
     if bytecode_path:
@@ -340,33 +346,44 @@
     def __init__(self, name, path, is_pkg):
         self._name = name
         self._is_pkg = is_pkg
-        self._source_path = None
-        self._bytecode_path = None
-        # Figure out whether source and/or bytecode exists.
-        for suffix in suffix_list(imp.PY_SOURCE):
-            if path.endswith(suffix):
-                # Source was found, but we don't know about the bytecode as the
-                # importer guarantees to check for source first.
-                self._source_path = path
-                base_path = _path_without_ext(path)
-                for suffix in suffix_list(imp.PY_COMPILED):
-                    bytecode_path = base_path + suffix
-                    if _path_exists(bytecode_path):
-                        self._bytecode_path = bytecode_path
-                        break
-                # Source was found, but no corresponding bytecode exists.
-                # Since source was found, the loop should be stopped.
-                break
-        else:
-            # The loader is being asked to load a bytecode file since it was
-            # not asked to load a source file and that is searched for first.
-            self._bytecode_path = path
+        # Figure out the base path based on whether it was source or bytecode
+        # that was found.
+        try:
+            self._base_path = _path_without_ext(path, imp.PY_SOURCE)
+        except ValueError:
+            self._base_path = _path_without_ext(path, imp.PY_COMPILED)
+
+    def _find_path(self, ext_type):
+        """Find a path from the base path and the specified extension type that
+        exists, returning None if one is not found."""
+        for suffix in suffix_list(ext_type):
+            path = self._base_path + suffix
+            if _path_exists(path):
+                return path
+        else:
+            return None
+
+
+    def _source_path(self):
+        """Return the path to an existing source file for the module, or None
+        if one cannot be found."""
+        # Not a property so that it is easy to override.
+        return self._find_path(imp.PY_SOURCE)
+
+    def _bytecode_path(self):
+        """Return the path to a bytecode file, or None if one does not
+        exist."""
+        # Not a property for easy overriding.
+        return self._find_path(imp.PY_COMPILED)
 
     @check_name
     def load_module(self, fullname):
         """Load a Python source or bytecode file."""
-        code_object, path = self._handler(fullname, self._source_path,
-                                            self._bytecode_path)
+        try:
+            code_object, path = self._handler(fullname, self._source_path(),
+                                                self._bytecode_path())
+        except ValueError:
+            raise ImportError("loader cannot handle %s" % fullname)
         try:
             return self._module_init(code_object, fullname, path, self._is_pkg)
         except:
@@ -383,8 +400,9 @@
         if the loader cannot handle the module.
 
         """
-        if self._source_path:
-            return int(_os.stat(self._source_path).st_mtime)
+        source_path = self._source_path()
+        if source_path:
+            return int(_os.stat(source_path).st_mtime)
         else:
             raise ValueError('no source for %s' % name)
 
@@ -396,9 +414,10 @@
         Raises ImportError if the loader cannot handle the module.
 
         """
-        if self._source_path and _path_exists(self._source_path):
-            return open(self._source_path, 'U').read()
-        elif self._bytecode_path and _path_exists(self._bytecode_path):
+        source_path = self._source_path()
+        if source_path:
+            return open(source_path, 'U').read()
+        elif self._bytecode_path():
             return None
         else:
             raise ImportError('no source or bytecode available')
@@ -413,7 +432,7 @@
 
         """
         try:
-            with open(self._bytecode_path, 'rb') as bytecode_file:
+            with open(self._bytecode_path(), 'rb') as bytecode_file:
                 data = bytecode_file.read()
             return data[:4], _r_long(data[4:8]), data[8:]
         except AttributeError:
@@ -429,11 +448,9 @@
         cannot be handled by the loader.
 
         """
-        if self._bytecode_path:
-            bytecode_path = self._bytecode_path
-        else:
-            base_path = _path_without_ext(self._source_path)
-            bytecode_path = base_path + suffix_list(imp.PY_COMPILED)[0]
+        bytecode_path = self._bytecode_path()
+        if not bytecode_path:
+            bytecode_path = self._base_path + suffix_list(imp.PY_COMPILED)[0]
         try:
             with open(bytecode_path, 'wb') as bytecode_file:
                 bytecode_file.write(imp.get_magic())
@@ -468,7 +485,11 @@
         specified module.
         
         """
-        return self._handler(fullname, self._source_path, self._bytecode_path)[0]
+        try:
+            return self._handler(fullname, self._source_path(),
+                                    self._bytecode_path())[0]
+        except ValueError:
+            raise ImportError("cannot get the code for %s" % fullname)
 
 
 class FileImporter(object):

Modified: sandbox/trunk/import_in_py/controlled_importlib.py
==============================================================================
--- sandbox/trunk/import_in_py/controlled_importlib.py	(original)
+++ sandbox/trunk/import_in_py/controlled_importlib.py	Thu Aug 23 02:03:06 2007
@@ -81,7 +81,7 @@
     def __init__(self, *args, **kwargs):
         """Remove the bytecode path."""
         super(PyOnlyFileLoader, self).__init__(*args, **kwargs)
-        self._bytecode_path = None
+        self._bytecode_path = lambda: None
 
     def write_bytecode(self, *args, **kwargs):
         """Do not write out any bytecode."""

Modified: sandbox/trunk/import_in_py/tests/test_fs_loader.py
==============================================================================
--- sandbox/trunk/import_in_py/tests/test_fs_loader.py	(original)
+++ sandbox/trunk/import_in_py/tests/test_fs_loader.py	Thu Aug 23 02:03:06 2007
@@ -132,6 +132,14 @@
         found = loader.load_module(self.module_name)
         self.verify_package(found, self.module_name)
 
+    def test_fails_with_no_files_post_init(self):
+        # The loader should fail gracefully if the files it would have used to
+        # load the module have been removed.
+        loader = importlib._PyFileLoader(self.module_name, self.py_path, False)
+        test_support.unlink(self.py_path)
+        test_support.unlink(self.pyc_path)
+        self.assertRaises(ImportError, loader.load_module, self.module_name)
+
 
 def log_call(instance, method_name):
     """Log a method call."""
@@ -360,6 +368,8 @@
         self.assertRaises(ImportError, loader.get_code, self.module_name)
         # Nothing available.
         os.unlink(self.pyc_path)
+        assert not os.path.exists(self.py_path)
+        assert not os.path.exists(self.pyc_path)
         loader = importlib._PyFileLoader(self.module_name, self.py_path, False)
         self.assertRaises(ImportError, loader.get_code, self.module_name)
 


More information about the Python-checkins mailing list