summaryrefslogtreecommitdiff
path: root/pkg_resources.py
diff options
context:
space:
mode:
Diffstat (limited to 'pkg_resources.py')
-rw-r--r--pkg_resources.py119
1 files changed, 101 insertions, 18 deletions
diff --git a/pkg_resources.py b/pkg_resources.py
index a16b67b4..2a8876bb 100644
--- a/pkg_resources.py
+++ b/pkg_resources.py
@@ -23,6 +23,7 @@ __all__ = [
'get_importer', 'find_distributions', 'find_on_path', 'register_finder',
'split_sections', 'declare_namespace', 'register_namespace_handler',
'safe_name', 'safe_version', 'run_main', 'BINARY_DIST', 'run_script',
+ 'get_default_cache',
]
import sys, os, zipimport, time, re, imp
@@ -38,7 +39,6 @@ from sets import ImmutableSet
-
class ResolutionError(Exception):
"""Abstract base for dependency resolution errors"""
@@ -421,16 +421,15 @@ class ResourceManager:
obtain an extraction location, and only for names they intend to
extract, as it tracks the generated names for possible cleanup later.
"""
- extract_path = self.extraction_path
- extract_path = extract_path or os.path.expanduser('~/.python-eggs')
- target_path = os.path.join(extract_path, archive_name, *names)
+ extract_path = self.extraction_path or get_default_cache()
+ target_path = os.path.join(extract_path, archive_name+'-tmp', *names)
ensure_directory(target_path)
self.cached_files[target_path] = 1
return target_path
- def postprocess(self, filename):
- """Perform any platform-specific postprocessing of file `filename`
+ def postprocess(self, tempname, filename):
+ """Perform any platform-specific postprocessing of `tempname`
This is where Mac header rewrites should be done; other platforms don't
have anything special they should do.
@@ -438,6 +437,10 @@ class ResourceManager:
Resource providers should call this method ONLY after successfully
extracting a compressed resource. They must NOT call it on resources
that are already in the filesystem.
+
+ `tempname` is the current (temporary) name of the file, and `filename`
+ is the name it will be renamed to by the caller after this routine
+ returns.
"""
# XXX
@@ -446,9 +449,6 @@ class ResourceManager:
-
-
-
def set_extraction_path(self, path):
"""Set the base path where resources will be extracted to, if needed.
@@ -490,6 +490,48 @@ class ResourceManager:
+def get_default_cache():
+ """Determine the default cache location
+
+ This returns the ``PYTHON_EGG_CACHE`` environment variable, if set.
+ Otherwise, on Windows, it returns a "Python-Eggs" subdirectory of the
+ "Application Data" directory. On all other systems, it's "~/.python-eggs".
+ """
+ try:
+ return os.environ['PYTHON_EGG_CACHE']
+ except KeyError:
+ pass
+
+ if os.name!='nt':
+ return os.path.expanduser('~/.python-eggs')
+
+ app_data = 'Application Data' # XXX this may be locale-specific!
+ app_homes = [
+ (('APPDATA',), None), # best option, should be locale-safe
+ (('USERPROFILE',), app_data),
+ (('HOMEDRIVE','HOMEPATH'), app_data),
+ (('HOMEPATH',), app_data),
+ (('HOME',), None),
+ (('WINDIR',), app_data), # 95/98/ME
+ ]
+
+ for keys, subdir in app_homes:
+ dirname = ''
+ for key in keys:
+ if key in os.environ:
+ dirname = os.path.join(os.environ[key])
+ else:
+ break
+ else:
+ if subdir:
+ dirname = os.path.join(dirname,subdir)
+ return os.path.join(dirname, 'Python-Eggs')
+ else:
+ raise RuntimeError(
+ "Please set the PYTHON_EGG_CACHE enviroment variable"
+ )
+
+
def require(*requirements):
"""Ensure that distributions matching `requirements` are on ``sys.path``
@@ -719,7 +761,7 @@ class ZipProvider(DefaultProvider):
"resource_filename() only supported for .egg, not .zip"
)
- # should lock for extraction here
+ # no need to lock for extraction, since we use temp names
eagers = self._get_eager_resources()
if resource_name in eagers:
for name in eagers:
@@ -757,14 +799,26 @@ class ZipProvider(DefaultProvider):
# size and stamp match, don't bother extracting
return real_path
- # print "extracting", zip_path
+ from tempfile import mkstemp
+ outf, tmpnam = mkstemp(".$extract", dir=os.path.dirname(real_path))
+ os.write(outf, self.loader.get_data(zip_path))
+ os.close(outf)
+ os.utime(tmpnam, (timestamp,timestamp))
+ manager.postprocess(tmpnam, real_path)
+ try:
+ os.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
+ raise
- data = self.loader.get_data(zip_path)
- open(real_path, 'wb').write(data)
- os.utime(real_path, (timestamp,timestamp))
- manager.postprocess(real_path)
return real_path
+
def _get_eager_resources(self):
if self.eagers is None:
eagers = []
@@ -774,9 +828,6 @@ class ZipProvider(DefaultProvider):
self.eagers = eagers
return self.eagers
-
-
-
def _index(self):
try:
return self._dirindex
@@ -806,6 +857,9 @@ class ZipProvider(DefaultProvider):
def _listdir(self,path):
return list(self._index().get(self._dir_name(path), ()))
+
+
+
def _dir_name(self,path):
if path.startswith(self.module_path+os.sep):
path = path[len(self.module_path+os.sep):]
@@ -818,6 +872,35 @@ class ZipProvider(DefaultProvider):
register_loader_type(zipimport.zipimporter, ZipProvider)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
class PathMetadata(DefaultProvider):
"""Metadata provider for egg directories