diff --git a/pkg_resources.py b/pkg_resources.py index 79db00b..5eacb69 100644 --- a/pkg_resources.py +++ b/pkg_resources.py @@ -1650,13 +1650,13 @@ def register_finder(importer_type, distribution_finder): _distribution_finders[importer_type] = distribution_finder -def find_distributions(path_item, only=False): +def find_distributions(path_item, only=False, basedir=None): """Yield distributions accessible via `path_item`""" importer = get_importer(path_item) finder = _find_adapter(_distribution_finders, importer) - return finder(importer, path_item, only) + return finder(importer, path_item, only, basedir) -def find_in_zip(importer, path_item, only=False): +def find_in_zip(importer, path_item, only=False, basedir=None): metadata = EggMetadata(importer) if metadata.has_metadata('PKG-INFO'): yield Distribution.from_filename(path_item, metadata=metadata) @@ -1679,13 +1679,15 @@ def StringIO(*args, **kw): from StringIO import StringIO return StringIO(*args,**kw) -def find_nothing(importer, path_item, only=False): +def find_nothing(importer, path_item, only=False, basedir=None): return () register_finder(object,find_nothing) -def find_on_path(importer, path_item, only=False): +def find_on_path(importer, path_item, only=False, basedir=None): """Yield distributions accessible on a sys.path directory""" - path_item = _normalize_cached(path_item) + normal_path_item = _normalize_cached(path_item, basedir=basedir) + if basedir is None or path_item.startswith(basedir): + path_item = normal_path_item if os.path.isdir(path_item) and os.access(path_item, os.R_OK): if path_item.lower().endswith('.egg'): @@ -1824,15 +1826,24 @@ def null_ns_handler(importer, path_item, packageName, module): register_namespace_handler(object,null_ns_handler) -def normalize_path(filename): +def normalize_path(filename,basedir=None): """Normalize a file/dir name for comparison purposes""" + if basedir is not None and not os.path.isabs(filename): + filename = os.path.join(basedir, filename) return os.path.normcase(os.path.realpath(filename)) -def _normalize_cached(filename,_cache={}): +def _normalize_cached(filename,_cache={},basedir=None): try: - return _cache[filename] + if basedir is None: + return _cache[filename] + else: + return _cache[(filename,basedir)] except KeyError: - _cache[filename] = result = normalize_path(filename) + result = normalize_path(filename,basedir) + if basedir is None: + _cache[filename] = result + else: + _cache[(filename,basedir)] = result return result def _set_parent_ns(packageName): diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index f128b80..fb1c05e 100755 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -53,10 +53,15 @@ class develop(easy_install): self.egg_link = os.path.join(self.install_dir, ei.egg_name+'.egg-link') self.egg_base = ei.egg_base + + target = normalize_path(self.egg_base) + if self.egg_path is None: self.egg_path = os.path.abspath(ei.egg_base) + dist_location = target + else: + dist_location = self.egg_path - target = normalize_path(self.egg_base) if normalize_path(os.path.join(self.install_dir, self.egg_path)) != target: raise DistutilsOptionError( "--egg-path must be a relative path from the install" @@ -65,7 +70,7 @@ class develop(easy_install): # Make a distribution for the package's source self.dist = Distribution( - target, + dist_location, PathMetadata(target, os.path.abspath(ei.egg_info)), project_name = ei.egg_name ) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index af4e349..f1c9ff9 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -465,7 +465,7 @@ Please make the appropriate changes for your system and try again. # at this point, we know it's a local .egg, we just don't know if # it's already installed. for dist in self.local_index[spec.project_name]: - if dist.location==download: + if not self.pth_file.cmp_paths(dist.location, download): break else: install_needed = True # it's not in the local index @@ -946,18 +946,26 @@ See the setuptools documentation for the "develop" command for more info. return for d in self.pth_file[dist.key]: # drop old entries - if self.multi_version or d.location != dist.location: + if (self.multi_version or + self.pth_file.cmp_paths(d.location, dist.location)): log.info("Removing %s from easy-install.pth file", d) self.pth_file.remove(d) if d.location in self.shadow_path: self.shadow_path.remove(d.location) if not self.multi_version: - if dist.location in self.pth_file.paths: - log.info( - "%s is already the active version in easy-install.pth", - dist - ) + if dist in self.pth_file: + updated = self.pth_file.update(dist) + if updated: + log.info( + "Updated %s to the active version in easy-install.pth", + dist + ) + else: + log.info( + "%s is already the active version in easy-install.pth", + dist + ) else: log.info("Adding %s to easy-install.pth file", dist) self.pth_file.add(dist) # add new entry @@ -1320,12 +1328,19 @@ class PthDistributions(Environment): self.basedir = normalize_path(os.path.dirname(self.filename)) self._load(); Environment.__init__(self, [], None, None) for path in yield_lines(self.paths): - map(self.add, find_distributions(path, True)) + map(self.add, find_distributions(path, True, self.basedir)) + + def cmp_paths(self,left,right): + """Return 0 iff left and right refer to the same location + relative to self.basedir + """ + return int(normalize_path(left, self.basedir) != \ + normalize_path(right, self.basedir)) def _load(self): self.paths = [] + self.normalized_paths = dict.fromkeys(self.sitedirs) saw_import = False - seen = dict.fromkeys(self.sitedirs) if os.path.isfile(self.filename): for line in open(self.filename,'rt'): if line.startswith('import'): @@ -1337,14 +1352,14 @@ class PthDistributions(Environment): continue # skip non-existent paths, in case somebody deleted a package # manually, and duplicate paths as well - path = self.paths[-1] = normalize_path( - os.path.join(self.basedir,path) - ) - if not os.path.exists(path) or path in seen: + real_path = normalize_path(path, self.basedir) + if not os.path.exists(real_path) or real_path in self.normalized_paths: self.paths.pop() # skip it self.dirty = True # we cleaned up, so we're dirty now :) continue - seen[path] = 1 + if real_path.startswith(self.basedir): + path = self.paths[-1] = real_path + self.normalized_paths[real_path] = path if self.paths and not saw_import: self.dirty = True # ensure anything we touch has import wrappers @@ -1379,21 +1394,40 @@ class PthDistributions(Environment): self.dirty = False + def __contains__(self,dist): + """Return True iff `dist` is already in the distribution map""" + return normalize_path(dist.location, self.basedir) in self.normalized_paths + def add(self,dist): """Add `dist` to the distribution map""" - if dist.location not in self.paths and dist.location not in self.sitedirs: + path = normalize_path(dist.location, self.basedir) + if path not in self.normalized_paths and path not in self.sitedirs: + self.normalized_paths[path] = dist.location self.paths.append(dist.location); self.dirty = True Environment.add(self,dist) def remove(self,dist): """Remove `dist` from the distribution map""" - while dist.location in self.paths: - self.paths.remove(dist.location); self.dirty = True + path = normalize_path(dist.location, self.basedir) + if path in self.normalized_paths: + self.paths.remove(self.normalized_paths[path]) + del self.normalized_paths[path]; self.dirty = True Environment.remove(self,dist) + def update(self,dist): + """Update `dist` in the distribution map""" + path = normalize_path(dist.location, self.basedir) + if path in self.normalized_paths and dist.location not in self.paths: + self.paths.remove(self.normalized_paths[path]) + self.paths.append(dist.location) + self.normalized_paths[path] = dist.location; self.dirty = True + return True + return False def make_relative(self,path): - npath, last = os.path.split(normalize_path(path)) + if not path.startswith(self.basedir): + return path + npath, last = os.path.split(normalize_path(path, self.basedir)) baselen = len(self.basedir) parts = [last] sep = os.altsep=='/' and '/' or os.sep