philip_thiem avatar philip_thiem committed 9dd3bcf

Added a legacy fallback test
Added in code to after a deprecation warning parse the .svn files
Should also parse externals.

Comments (0)

Files changed (4)

setuptools.egg-info/entry_points.txt

-[setuptools.installation]
-eggsecutable = setuptools.command.easy_install:bootstrap
-
-[console_scripts]
-easy_install = setuptools.command.easy_install:main
-easy_install-3.3 = setuptools.command.easy_install:main
-
-[distutils.setup_keywords]
-use_2to3 = setuptools.dist:assert_bool
-namespace_packages = setuptools.dist:check_nsp
-package_data = setuptools.dist:check_package_data
-use_2to3_exclude_fixers = setuptools.dist:assert_string_list
-dependency_links = setuptools.dist:assert_string_list
-use_2to3_fixers = setuptools.dist:assert_string_list
-test_suite = setuptools.dist:check_test_suite
-exclude_package_data = setuptools.dist:check_package_data
-extras_require = setuptools.dist:check_extras
-install_requires = setuptools.dist:check_requirements
-eager_resources = setuptools.dist:assert_string_list
-include_package_data = setuptools.dist:assert_bool
-packages = setuptools.dist:check_packages
-entry_points = setuptools.dist:check_entry_points
-zip_safe = setuptools.dist:assert_bool
-tests_require = setuptools.dist:check_requirements
-convert_2to3_doctests = setuptools.dist:assert_string_list
-test_loader = setuptools.dist:check_importable
-
-[setuptools.file_finders]
-svn_cvs = setuptools.command.sdist:_default_revctrl
-
-[egg_info.writers]
-top_level.txt = setuptools.command.egg_info:write_toplevel_names
-PKG-INFO = setuptools.command.egg_info:write_pkg_info
-eager_resources.txt = setuptools.command.egg_info:overwrite_arg
-namespace_packages.txt = setuptools.command.egg_info:overwrite_arg
-depends.txt = setuptools.command.egg_info:warn_depends_obsolete
-dependency_links.txt = setuptools.command.egg_info:overwrite_arg
-entry_points.txt = setuptools.command.egg_info:write_entries
-requires.txt = setuptools.command.egg_info:write_requirements
-
-[distutils.commands]
-test = setuptools.command.test:test
-bdist_wininst = setuptools.command.bdist_wininst:bdist_wininst
-alias = setuptools.command.alias:alias
-sdist = setuptools.command.sdist:sdist
-develop = setuptools.command.develop:develop
-bdist_egg = setuptools.command.bdist_egg:bdist_egg
-setopt = setuptools.command.setopt:setopt
-egg_info = setuptools.command.egg_info:egg_info
-build_ext = setuptools.command.build_ext:build_ext
-upload_docs = setuptools.command.upload_docs:upload_docs
-easy_install = setuptools.command.easy_install:easy_install
-install = setuptools.command.install:install
-install_egg_info = setuptools.command.install_egg_info:install_egg_info
-bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm
-install_lib = setuptools.command.install_lib:install_lib
-rotate = setuptools.command.rotate:rotate
-saveopts = setuptools.command.saveopts:saveopts
-install_scripts = setuptools.command.install_scripts:install_scripts
-build_py = setuptools.command.build_py:build_py
-register = setuptools.command.register:register
-
+[setuptools.installation]
+eggsecutable = setuptools.command.easy_install:bootstrap
+
+[console_scripts]
+easy_install = setuptools.command.easy_install:main
+easy_install-3.3 = setuptools.command.easy_install:main
+
+[distutils.setup_keywords]
+use_2to3 = setuptools.dist:assert_bool
+namespace_packages = setuptools.dist:check_nsp
+package_data = setuptools.dist:check_package_data
+use_2to3_exclude_fixers = setuptools.dist:assert_string_list
+dependency_links = setuptools.dist:assert_string_list
+use_2to3_fixers = setuptools.dist:assert_string_list
+test_suite = setuptools.dist:check_test_suite
+exclude_package_data = setuptools.dist:check_package_data
+extras_require = setuptools.dist:check_extras
+install_requires = setuptools.dist:check_requirements
+eager_resources = setuptools.dist:assert_string_list
+include_package_data = setuptools.dist:assert_bool
+packages = setuptools.dist:check_packages
+entry_points = setuptools.dist:check_entry_points
+zip_safe = setuptools.dist:assert_bool
+tests_require = setuptools.dist:check_requirements
+convert_2to3_doctests = setuptools.dist:assert_string_list
+test_loader = setuptools.dist:check_importable
+
+[setuptools.file_finders]
+svn_cvs = setuptools.command.sdist:_default_revctrl
+
+[egg_info.writers]
+top_level.txt = setuptools.command.egg_info:write_toplevel_names
+PKG-INFO = setuptools.command.egg_info:write_pkg_info
+eager_resources.txt = setuptools.command.egg_info:overwrite_arg
+namespace_packages.txt = setuptools.command.egg_info:overwrite_arg
+depends.txt = setuptools.command.egg_info:warn_depends_obsolete
+dependency_links.txt = setuptools.command.egg_info:overwrite_arg
+entry_points.txt = setuptools.command.egg_info:write_entries
+requires.txt = setuptools.command.egg_info:write_requirements
+
+[distutils.commands]
+test = setuptools.command.test:test
+bdist_wininst = setuptools.command.bdist_wininst:bdist_wininst
+alias = setuptools.command.alias:alias
+sdist = setuptools.command.sdist:sdist
+develop = setuptools.command.develop:develop
+bdist_egg = setuptools.command.bdist_egg:bdist_egg
+setopt = setuptools.command.setopt:setopt
+egg_info = setuptools.command.egg_info:egg_info
+build_ext = setuptools.command.build_ext:build_ext
+upload_docs = setuptools.command.upload_docs:upload_docs
+easy_install = setuptools.command.easy_install:easy_install
+install = setuptools.command.install:install
+install_egg_info = setuptools.command.install_egg_info:install_egg_info
+bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm
+install_lib = setuptools.command.install_lib:install_lib
+rotate = setuptools.command.rotate:rotate
+saveopts = setuptools.command.saveopts:saveopts
+install_scripts = setuptools.command.install_scripts:install_scripts
+build_py = setuptools.command.build_py:build_py
+register = setuptools.command.register:register
+

setuptools.egg-info/requires.txt

 
 
-[ssl:sys_platform=='win32']
-wincertstore==0.1
+[ssl:python_version in '2.4, 2.5']
+ssl==1.16
 
 [ssl:sys_platform=='win32' and python_version=='2.4']
 ctypes==1.0.2
 [certs]
 certifi==0.0.8
 
-[ssl:python_version in '2.4, 2.5']
-ssl==1.16
+[ssl:sys_platform=='win32']
+wincertstore==0.1

setuptools/svn_utils.py

 import shlex
 import locale
 import unicodedata
+import warnings
 from setuptools.compat import unicode, bytes
+from xml.sax.saxutils import unescape
 
 try:
     import urlparse
 #       http://bugs.python.org/issue8557
 #       http://stackoverflow.com/questions/5658622/
 #              python-subprocess-popen-environment-path
+
+
 def _run_command(args, stdout=_PIPE, stderr=_PIPE):
     #regarding the shell argument, see: http://bugs.python.org/issue8557
     try:
     return data
 
 
-def joinpath(prefix, suffix):
+def joinpath(prefix, *suffix):
     if not prefix or prefix == '.':
-        return suffix
-    return os.path.join(prefix, suffix)
+        return os.path.join(*suffix)
+    return os.path.join(prefix, *suffix)
 
 
 def fsencode(path):
 
     return path
 
+
 def fsdecode(path):
     "Path must be unicode or in file system encoding already"
     encoding = sys.getfilesystemencoding()
 
     return unicodedata.normalize('NFC', path)
 
+
 def consoledecode(text):
     encoding = locale.getpreferredencoding()
     return text.decode(encoding)
         if event == 'START_ELEMENT' and node.nodeName == 'target':
             doc.expandNode(node)
             path = os.path.normpath(node.getAttribute('path'))
-            log.warn('')
-            log.warn('PRE: %s' % prefix)
-            log.warn('PTH: %s' % path)
+
             if os.path.normcase(path).startswith(prefix):
                 path = path[len(prefix)+1:]
 
     """
     externals = []
     for line in lines.splitlines():
-        line = line.lstrip() #there might be a "\ "
+        line = line.lstrip()  # there might be a "\ "
         if not line:
             continue
 
     return externals
 
 
+def parse_prop_file(filename, key):
+    found = False
+    f = open(filename, 'rt')
+    data = ''
+    try:
+        for line in iter(f.readline, ''):    # can't use direct iter!
+            parts = line.split()
+            if len(parts) == 2:
+                kind, length = parts
+                data = f.read(int(length))
+                if kind == 'K' and data == key:
+                    found = True
+                elif kind == 'V' and found:
+                    break
+    finally:
+        f.close()
+
+    return data
+
+
 class SvnInfo(object):
     '''
     Generic svn_info object.  No has little knowledge of how to extract
 
     @classmethod
     def load(cls, dirname=''):
-        code, data = _run_command(['svn', 'info', os.path.normpath(dirname)])
+        normdir = os.path.normpath(dirname)
+        code, data = _run_command(['svn', 'info', normdir])
+        has_svn = os.path.isdir(os.path.join(normdir, '.svn'))
         svn_version = tuple(cls.get_svn_version().split('.'))
-        base_svn_version = tuple(int(x) for x in svn_version[:2])
-        if code and base_svn_version:
-            #Not an SVN repository or compatible one
-            return SvnInfo(dirname)
-        elif base_svn_version < (1, 3):
-            log.warn('Insufficent version of SVN found')
+
+        try:
+            base_svn_version = tuple(int(x) for x in svn_version[:2])
+        except ValueError:
+            base_svn_version = tuple()
+
+        if has_svn and (code or not base_svn_version 
+                             or base_svn_version < (1, 3)):
+            log.warn('Fallback onto .svn parsing')
+            warnings.warn(("No SVN 1.3+ command found: falling back "
+                           "on pre 1.7 .svn parsing"), DeprecationWarning)
+            return SvnFileInfo(dirname)
+        elif not has_svn:
+            log.warn('Not SVN Repository')
  1. pombredanne

    philip_thiem "Not SVN Repository" has a printed message seems to have been creeping in many places in buildout since that code of yours made it upstream and in regular usage of setuptools too .... I wonder if logging the fact this is not an SVN repo makes sense? ie there are tons of things which are not SVN repos (git, hg, etc)... what do you think?

             return SvnInfo(dirname)
         elif base_svn_version < (1, 5):
             return Svn13Info(dirname)
         Iterate over the non-deleted file entries in the repository path
         '''
         for item, kind in self.entries:
-            if kind.lower()=='file':
+            if kind.lower() == 'file':
                 yield item
 
     def iter_dirs(self, include_root=True):
         if include_root:
             yield self.path
         for item, kind in self.entries:
-            if kind.lower()=='dir':
+            if kind.lower() == 'dir':
                 yield item
 
     def get_entries(self):
     def get_externals(self):
         return []
 
+
 class Svn13Info(SvnInfo):
     def get_entries(self):
         code, data = _run_command(['svn', 'info', '-R', '--xml', self.path])
         return parse_externals_xml(lines, prefix=os.path.abspath(self.path))
 
 
+class SvnFileInfo(SvnInfo):
+
+    def __init__(self, path=''):
+        super(SvnFileInfo, self).__init__(path)
+        self._directories = None
+        self._revision = None
+
+    def _walk_svn(self, base):
+        entry_file = joinpath(base, '.svn', 'entries')
+        if os.path.isfile(entry_file):
+            entries = SVNEntriesFile.load(base)
+            yield (base, False, entries.parse_revision())
+            for path in entries.get_undeleted_records():
+                path = joinpath(base, path)
+                if os.path.isfile(path):
+                    yield (path, True, None)
+                elif os.path.isdir(path):
+                    for item in self._walk_svn(path):
+                        yield item
+
+    def _build_entries(self):
+        dirs = list()
+        files = list()
+        rev = 0
+        for path, isfile, dir_rev in self._walk_svn(self.path):
+            if isfile:
+                files.append(path)
+            else:
+                dirs.append(path)
+                rev = max(rev, dir_rev)
+
+        self._directories = dirs
+        self._entries = files
+        self._revision = rev
+
+    def get_entries(self):
+        if self._entries is None:
+            self._build_entries()
+        return self._entries
+
+    def get_revision(self):
+        if self._revision is None:
+            self._build_entries()
+        return self._revision
+
+    def get_externals(self):
+        if self._directories is None:
+            self._build_entries()
+
+        prop_files = [['.svn', 'dir-prop-base'],
+                      ['.svn', 'dir-props']]
+        externals = []
+
+        for dirname in self._directories:
+            prop_file = None
+            for rel_parts in prop_files:
+                filename = joinpath(dirname, *rel_parts)
+                if os.path.isfile(filename):
+                    prop_file = filename
+
+            if prop_file is not None:
+                ext_prop = parse_prop_file(prop_file, 'svn:externals')
+                externals.extend(parse_external_prop(ext_prop))
+
+        return externals
+
+
 def svn_finder(dirname=''):
     #combined externals due to common interface
     #combined externals and entries due to lack of dir_props in 1.7
         for sub_path in sub_info.iter_files():
             yield fsencode(sub_path)
 
+
+class SVNEntriesFile(object):
+    def __init__(self, data):
+        self.data = data
+
+    @classmethod
+    def load(class_, base):
+        filename = os.path.join(base, '.svn', 'entries')
+        f = open(filename)
+        try:
+            result = SVNEntriesFile.read(f)
+        finally:
+            f.close()
+        return result
+
+    @classmethod
+    def read(class_, fileobj):
+        data = fileobj.read()
+        is_xml = data.startswith('<?xml')
+        class_ = [SVNEntriesFileText, SVNEntriesFileXML][is_xml]
+        return class_(data)
+
+    def parse_revision(self):
+        all_revs = self.parse_revision_numbers() + [0]
+        return max(all_revs)
+
+
+class SVNEntriesFileText(SVNEntriesFile):
+    known_svn_versions = {
+        '1.4.x': 8,
+        '1.5.x': 9,
+        '1.6.x': 10,
+    }
+
+    def __get_cached_sections(self):
+        return self.sections
+
+    def get_sections(self):
+        SECTION_DIVIDER = '\f\n'
+        sections = self.data.split(SECTION_DIVIDER)
+        sections = [x for x in map(str.splitlines, sections)]
+        try:
+            # remove the SVN version number from the first line
+            svn_version = int(sections[0].pop(0))
+            if not svn_version in self.known_svn_versions.values():
+                log.warn("Unknown subversion verson %d", svn_version)
+        except ValueError:
+            return
+        self.sections = sections
+        self.get_sections = self.__get_cached_sections
+        return self.sections
+
+    def is_valid(self):
+        return bool(self.get_sections())
+
+    def get_url(self):
+        return self.get_sections()[0][4]
+
+    def parse_revision_numbers(self):
+        revision_line_number = 9
+        rev_numbers = [
+            int(section[revision_line_number])
+            for section in self.get_sections()
+            if (len(section) > revision_line_number
+                and section[revision_line_number])
+        ]
+        return rev_numbers
+
+    def get_undeleted_records(self):
+        undeleted = lambda s: s and s[0] and (len(s) < 6 or s[5] != 'delete')
+        result = [
+            section[0]
+            for section in self.get_sections()
+            if undeleted(section)
+        ]
+        return result
+
+
+class SVNEntriesFileXML(SVNEntriesFile):
+    def is_valid(self):
+        return True
+
+    def get_url(self):
+        "Get repository URL"
+        urlre = re.compile('url="([^"]+)"')
+        return urlre.search(self.data).group(1)
+
+    def parse_revision_numbers(self):
+        revre = re.compile(r'committed-rev="(\d+)"')
+        return [
+            int(m.group(1))
+            for m in revre.finditer(self.data)
+        ]
+
+    def get_undeleted_records(self):
+        entries_pattern = \
+            re.compile(r'name="([^"]+)"(?![^>]+deleted="true")', re.I)
+        results = [
+            unescape(match.group(1))
+            for match in entries_pattern.finditer(self.data)
+        ]
+        return results
+
+
 if __name__ == '__main__':
     for name in svn_finder(sys.argv[1]):
-        print(name)
+        print(name)

setuptools/tests/test_egg_info.py

         rev = egg_info.egg_info.get_svn_revision()
         self.assertEqual(rev, '89000')
 
+    def test_version_10_format_legacy_parser(self):
+        """
+        """
+        path_variable = None
+        for env in os.environ:
+            if env.lower() == 'path':
+                path_variable = env
+
+        if path_variable is None:
+            self.skipTest('Cannot figure out how to modify path')
+
+        old_path = os.environ[path_variable]
+        os.environ[path_variable] = ''
+        try:
+            self._write_entries(ENTRIES_V10)
+            rev = egg_info.egg_info.get_svn_revision()
+        finally:
+            os.environ[path_variable] = old_path
+
+        self.assertEqual(rev, '89000')
+
 
 def test_suite():
     return unittest.defaultTestLoader.loadTestsFromName(__name__)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.