[Distutils] [issue140] [PATCH] Make 'develop --egg-path' respect specified path in easy_install.pth

R. Andrew Ohana setuptools at bugs.python.org
Wed Jul 4 06:11:41 CEST 2012


New submission from R. Andrew Ohana <andrew.ohana at gmail.com>:

Currently easy_install.pth will either contain paths relative to the site-packages directory, or the normalized_path. This is because the framework has been setup to use normalized paths everywhere. The patch should properly extend the current framework to make the argument to the egg-path option actually respected.

----------
files: respect-egg-path.patch
messages: 663
nosy: ohanar
priority: feature
status: unread
title: [PATCH] Make 'develop --egg-path' respect specified path in easy_install.pth
Added file: http://bugs.python.org/setuptools/file82/respect-egg-path.patch

_______________________________________________
Setuptools tracker <setuptools at bugs.python.org>
<http://bugs.python.org/setuptools/issue140>
_______________________________________________
-------------- next part --------------
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


More information about the Distutils-SIG mailing list