[Python-checkins] r43426 - sandbox/trunk/setuptools/pkg_resources.py sandbox/trunk/setuptools/pkg_resources.txt

phillip.eby python-checkins at python.org
Wed Mar 29 23:11:23 CEST 2006


Author: phillip.eby
Date: Wed Mar 29 23:11:22 2006
New Revision: 43426

Modified:
   sandbox/trunk/setuptools/pkg_resources.py
   sandbox/trunk/setuptools/pkg_resources.txt
Log:
Added ``ExtractionError`` and ``ResourceManager.extraction_error()`` so that
cache permission problems get a more user-friendly explanation of the
problem, and so that programs can catch and handle extraction errors if they
need to.


Modified: sandbox/trunk/setuptools/pkg_resources.py
==============================================================================
--- sandbox/trunk/setuptools/pkg_resources.py	(original)
+++ sandbox/trunk/setuptools/pkg_resources.py	Wed Mar 29 23:11:22 2006
@@ -57,7 +57,8 @@
 
     # Exceptions
     'ResolutionError','VersionConflict','DistributionNotFound','UnknownExtra',
-
+    'ExtractionError',
+    
     # Parsing functions and string utilities
     'parse_requirements', 'parse_version', 'safe_name', 'safe_version',
     'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections',
@@ -79,7 +80,6 @@
     # Deprecated/backward compatibility only
     'run_main', 'AvailableDistributions',
 ]
-
 class ResolutionError(Exception):
     """Abstract base for dependency resolution errors"""
 
@@ -759,20 +759,20 @@
 AvailableDistributions = Environment    # XXX backward compatibility
 
 
+class ExtractionError(RuntimeError):
+    """An error occurred extracting a resource
 
+    The following attributes are available from instances of this exception:
 
+    manager
+        The resource manager that raised this exception
 
+    cache_path
+        The base directory for resource extraction
 
-
-
-
-
-
-
-
-
-
-
+    original_error
+        The exception instance that caused extraction to fail
+    """
 
 
 
@@ -818,6 +818,47 @@
             resource_name
         )
 
+    def extraction_error(self):
+        """Give an error message for problems extracting file(s)"""
+
+        old_exc = sys.exc_info()[1]
+        cache_path = self.extraction_path or get_default_cache()
+        
+        err = ExtractionError("""Can't extract file(s) to egg cache
+
+The following error occurred while trying to extract file(s) to the Python egg
+cache:
+
+  %s
+
+The Python egg cache directory is currently set to:
+
+  %s
+
+Perhaps your account does not have write access to this directory?  You can
+change the cache directory by setting the PYTHON_EGG_CACHE environment
+variable to point to an accessible directory.
+"""         % (old_exc, cache_path)
+        )
+        err.manager        = self
+        err.cache_path     = cache_path
+        err.original_error = old_exc
+        raise err
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
     def get_cache_path(self, archive_name, names=()):
         """Return absolute location in cache for `archive_name` and `names`
 
@@ -833,7 +874,11 @@
         """
         extract_path = self.extraction_path or get_default_cache()
         target_path = os.path.join(extract_path, archive_name+'-tmp', *names)
-        ensure_directory(target_path)
+        try:
+            ensure_directory(target_path)
+        except:
+            self.extraction_error()
+           
         self.cached_files[target_path] = 1
         return target_path
 
@@ -855,10 +900,6 @@
         # XXX
 
 
-
-
-
-
     def set_extraction_path(self, path):
         """Set the base path where resources will be extracted to, if needed.
 
@@ -1188,12 +1229,14 @@
         return self._extract_resource(manager, zip_path)
 
     def _extract_resource(self, manager, zip_path):
+
         if zip_path in self._index():
             for name in self._index()[zip_path]:
                 last = self._extract_resource(
                     manager, os.path.join(zip_path, name)
                 )
             return os.path.dirname(last)  # return the extracted directory name
+
         zip_stat = self.zipinfo[zip_path]
         t,d,size = zip_stat[5], zip_stat[6], zip_stat[3]
         date_time = (
@@ -1201,32 +1244,45 @@
             (t&0xFFFF)>>11, (t>>5)&0x3F, (t&0x1F) * 2, 0, 0, -1   # hms, etc.
         )
         timestamp = time.mktime(date_time)
-        real_path = manager.get_cache_path(self.egg_name, self._parts(zip_path))
-        if os.path.isfile(real_path):
-            stat = os.stat(real_path)
-            if stat.st_size==size and stat.st_mtime==timestamp:
-                # size and stamp match, don't bother extracting
-                return real_path
-        outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path))
-        os.write(outf, self.loader.get_data(zip_path))
-        os.close(outf)
-        utime(tmpnam, (timestamp,timestamp))
-        manager.postprocess(tmpnam, real_path)
-        try: rename(tmpnam, real_path)
-        except os.error:
+
+        try:
+            real_path = manager.get_cache_path(
+                self.egg_name, self._parts(zip_path)
+            )
+
             if os.path.isfile(real_path):
                 stat = os.stat(real_path)
                 if stat.st_size==size and stat.st_mtime==timestamp:
-                    # size and stamp match, somebody did it just ahead of us
-                    # so we're done
-                    return real_path
-                elif os.name=='nt':     # Windows, delete old file and retry
-                    unlink(real_path)
-                    rename(tmpnam, real_path)
+                    # size and stamp match, don't bother extracting
                     return real_path
-            raise
-        return real_path
 
+            outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path))
+            os.write(outf, self.loader.get_data(zip_path))
+            os.close(outf)
+            utime(tmpnam, (timestamp,timestamp))
+            manager.postprocess(tmpnam, real_path)
+
+            try:
+                rename(tmpnam, real_path)
+                
+            except os.error:               
+                if os.path.isfile(real_path):
+                    stat = os.stat(real_path)
+                    
+                    if stat.st_size==size and stat.st_mtime==timestamp:
+                        # size and stamp match, somebody did it just ahead of
+                        # us, so we're done
+                        return real_path
+                    elif os.name=='nt':     # Windows, del old file and retry
+                        unlink(real_path)
+                        rename(tmpnam, real_path)
+                        return real_path
+                raise
+
+        except os.error:
+            manager.extraction_error()  # report a user-friendly error
+
+        return real_path
 
     def _get_eager_resources(self):
         if self.eagers is None:
@@ -1264,11 +1320,6 @@
     def _listdir(self,fspath):
         return list(self._index().get(self._zipinfo_name(fspath), ()))
 
-
-
-
-
-
     def _eager_to_zip(self,resource_name):
         return self._zipinfo_name(self._fn(self.egg_root,resource_name))
 
@@ -1278,6 +1329,28 @@
 register_loader_type(zipimport.zipimporter, ZipProvider)
 
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 class FileMetadata(EmptyProvider):
     """Metadata handler for standalone PKG-INFO files
 
@@ -1310,6 +1383,15 @@
 
 
 
+
+
+
+
+
+
+
+
+
 class PathMetadata(DefaultProvider):
     """Metadata provider for egg directories
 

Modified: sandbox/trunk/setuptools/pkg_resources.txt
==============================================================================
--- sandbox/trunk/setuptools/pkg_resources.txt	(original)
+++ sandbox/trunk/setuptools/pkg_resources.txt	Wed Mar 29 23:11:22 2006
@@ -1194,6 +1194,14 @@
     obtain an extraction location, and only for names they intend to
     extract, as it tracks the generated names for possible cleanup later.
 
+``extraction_error()``
+    Raise an ``ExtractionError`` describing the active exception as interfering
+    with the extraction process.  You should call this if you encounter any
+    OS errors extracting the file to the cache path; it will format the
+    operating system exception for you, and add other information to the
+    ``ExtractionError`` instance that may be needed by programs that want to
+    wrap or handle extraction errors themselves.
+
 ``postprocess(tempname, filename)``
     Perform any platform-specific postprocessing of `tempname`.
     Resource providers should call this method ONLY after successfully
@@ -1277,6 +1285,8 @@
         VersionConflict
         UnknownExtra
 
+    ExtractionError
+
 ``ResolutionError``
     This class is used as a base class for the other three exceptions, so that
     you can catch all of them with a single "except" clause.  It is also raised
@@ -1294,6 +1304,19 @@
     One of the "extras" requested was not recognized by the distribution it
     was requested from.
 
+``ExtractionError``
+    A problem occurred extracting a resource to the Python Egg cache.  The
+    following attributes are available on instances of this exception:
+
+    manager
+        The resource manager that raised this exception
+
+    cache_path
+        The base directory for resource extraction
+
+    original_error
+        The exception instance that caused extraction to fail
+
 
 Supporting Custom Importers
 ===========================
@@ -1629,6 +1652,12 @@
 Release Notes/Change History
 ----------------------------
 
+0.6a11
+ * Added ``ExtractionError`` and ``ResourceManager.extraction_error()`` so that
+   cache permission problems get a more user-friendly explanation of the
+   problem, and so that programs can catch and handle extraction errors if they
+   need to.
+
 0.6a10
  * Added the ``extras`` attribute to ``Distribution``, the ``find_plugins()``
    method to ``WorkingSet``, and the ``__add__()`` and ``__iadd__()`` methods


More information about the Python-checkins mailing list