Repository: pypa/pip

Issue:
pip misses support for subversion 1.7 metadata handling
Hi,

metadata handling changed completely in subversion 1.7. The result is that calling get_url from pip/vcs/subversion.py returns None as URL. It just logs that the entry format (now 12) is unknown and continues, leading to 

File "/usr/lib/python2.6/urllib.py", line 1184, in unquote
    res = s.split('%')
AttributeError: 'NoneType' object has no attribute 'split'

I hacked a quick fix for me, maybe thats a start:

rsommer/pip@f50222a8de2e4d5e27224910255c3fb2ca3e168f

Thank you ...



Discussion:
Except hack the code manually and change svn version, is there any other  suggestions?

Is it time to switch the svn backend to just use the CLI like every other VCS backend? Are there major blockers to doing that?

+1 on using the CLI instead.

why not update some code to fix this?

Hi,

Hmm, I also seem to be hitting this issue when using to install django-trunk from Subversion.

Is there any update on this issue, or when it might be fixed?

Cheers,
Victor

This is open source; it'll get fixed when somebody who uses svn is motivated to fix it. Pull requests welcome :-)

fix works for me too, just save it and apply with patch command

heya,

Is that patch likely to hit mainline?

Or are we just switching over to command-line svn?

Cheers,
Victor

The linked work around is not likely to land in pip, a command-line option would be preferred (like we do with the other VCS).


Resolving patch:
diff --git a/pip/vcs/subversion.py b/pip/vcs/subversion.py
--- a/pip/vcs/subversion.py
+++ b/pip/vcs/subversion.py
@@ -1,6 +1,6 @@
 import os
 import re
-from pip import call_subprocess
+from pip import call_subprocess, InstallationError
 from pip.index import Link
 from pip.util import rmtree, display_path
 from pip.log import logger
@@ -10,7 +10,8 @@
 _svn_rev_re = re.compile('committed-rev="(\d+)"')
 _svn_url_re = re.compile(r'URL: (.+)')
 _svn_revision_re = re.compile(r'Revision: (.+)')
-
+_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"')
+_svn_info_xml_url_re = re.compile(r'<url>(.*)</url>')
 
 class Subversion(VersionControl):
     name = 'svn'
@@ -119,33 +120,12 @@ def get_revision(self, location):
             if not os.path.exists(entries_fn):
                 ## FIXME: should we warn?
                 continue
-            f = open(entries_fn)
-            data = f.read()
-            f.close()
-
-            if data.startswith('8') or data.startswith('9') or data.startswith('10'):
-                data = list(map(str.splitlines, data.split('\n\x0c\n')))
-                del data[0][0]  # get rid of the '8'
-                dirurl = data[0][3]
-                revs = [int(d[9]) for d in data if len(d)>9 and d[9]]+[0]
-                if revs:
-                    localrev = max(revs)
-                else:
-                    localrev = 0
-            elif data.startswith('<?xml'):
-                dirurl = _svn_xml_url_re.search(data).group(1)    # get repository URL
-                revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)]+[0]
-                if revs:
-                    localrev = max(revs)
-                else:
-                    localrev = 0
-            else:
-                logger.warn("Unrecognized .svn/entries format; skipping %s", base)
-                dirs[:] = []
-                continue
+
+            dirurl, localrev = self._get_svn_url_rev(base)
+
             if base == location:
                 base_url = dirurl+'/'   # save the root url
-            elif not dirurl.startswith(base_url):
+            elif not dirurl or not dirurl.startswith(base_url):
                 dirs[:] = []
                 continue    # not part of the same svn tree, skip it
             revision = max(revision, localrev)
@@ -170,22 +150,39 @@ def get_url(self, location):
                 logger.warn("Could not find setup.py for directory %s (tried all parent directories)"
                             % orig_location)
                 return None
+
+        return self._get_svn_url_rev(location)[0]
+
+    def _get_svn_url_rev(self, location):
         f = open(os.path.join(location, self.dirname, 'entries'))
         data = f.read()
         f.close()
         if data.startswith('8') or data.startswith('9') or data.startswith('10'):
             data = list(map(str.splitlines, data.split('\n\x0c\n')))
             del data[0][0]  # get rid of the '8'
-            return data[0][3]
+            url = data[0][3]
+            revs = [int(d[9]) for d in data if len(d)>9 and d[9]]+[0]
         elif data.startswith('<?xml'):
             match = _svn_xml_url_re.search(data)
             if not match:
                 raise ValueError('Badly formatted data: %r' % data)
-            return match.group(1)    # get repository URL
+            url = match.group(1)    # get repository URL
+            revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)]+[0]
         else:
-            logger.warn("Unrecognized .svn/entries format in %s" % location)
-            # Or raise exception?
-            return None
+            try:
+                # subversion >= 1.7
+                xml = call_subprocess([self.cmd, 'info', '--xml', location], show_stdout=False)
+                url = _svn_info_xml_url_re.search(xml).group(1)
+                revs = [int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml)]
+            except InstallationError:
+                url, revs = None, []
+
+        if revs:
+            rev = max(revs)
+        else:
+            rev = 0
+
+        return url, rev
 
     def get_tag_revs(self, svn_tag_url):
         stdout = call_subprocess(


Test patch: