From 30bb58f069cf1624f35cfbdb725e8e443ff64330 Mon Sep 17 00:00:00 2001 From: Philip Thiem Date: Sat, 28 Sep 2013 12:14:12 -0500 Subject: Added a legacy fallback test Added in code to after a deprecation warning parse the .svn files Should also parse externals. --HG-- extra : rebase_source : 9dd3bcf22cb56eb0826051f9e477f155e47cdbf6 --- setuptools.egg-info/entry_points.txt | 124 +++++++++--------- setuptools.egg-info/requires.txt | 8 +- setuptools/svn_utils.py | 240 ++++++++++++++++++++++++++++++++--- setuptools/tests/test_egg_info.py | 21 +++ 4 files changed, 310 insertions(+), 83 deletions(-) diff --git a/setuptools.egg-info/entry_points.txt b/setuptools.egg-info/entry_points.txt index d64b3c28..478fac7d 100644 --- a/setuptools.egg-info/entry_points.txt +++ b/setuptools.egg-info/entry_points.txt @@ -1,62 +1,62 @@ -[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 + diff --git a/setuptools.egg-info/requires.txt b/setuptools.egg-info/requires.txt index 91d84d9c..221040f8 100644 --- a/setuptools.egg-info/requires.txt +++ b/setuptools.egg-info/requires.txt @@ -1,7 +1,7 @@ -[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 @@ -9,5 +9,5 @@ ctypes==1.0.2 [certs] certifi==0.0.8 -[ssl:python_version in '2.4, 2.5'] -ssl==1.16 \ No newline at end of file +[ssl:sys_platform=='win32'] +wincertstore==0.1 \ No newline at end of file diff --git a/setuptools/svn_utils.py b/setuptools/svn_utils.py index 17b211d7..22b45cd7 100644 --- a/setuptools/svn_utils.py +++ b/setuptools/svn_utils.py @@ -6,7 +6,9 @@ import xml.dom.pulldom import shlex import locale import unicodedata +import warnings from setuptools.compat import unicode, bytes +from xml.sax.saxutils import unescape try: import urlparse @@ -23,6 +25,8 @@ from subprocess import Popen as _Popen, PIPE as _PIPE # 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: @@ -63,10 +67,10 @@ def _get_xml_data(decoded_str): 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): @@ -87,6 +91,7 @@ def fsencode(path): return path + def fsdecode(path): "Path must be unicode or in file system encoding already" encoding = sys.getfilesystemencoding() @@ -98,6 +103,7 @@ def fsdecode(path): return unicodedata.normalize('NFC', path) + def consoledecode(text): encoding = locale.getpreferredencoding() return text.decode(encoding) @@ -130,9 +136,7 @@ def parse_externals_xml(decoded_str, prefix=''): 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:] @@ -154,7 +158,7 @@ def parse_external_prop(lines): """ externals = [] for line in lines.splitlines(): - line = line.lstrip() #there might be a "\ " + line = line.lstrip() # there might be a "\ " if not line: continue @@ -178,6 +182,26 @@ def parse_external_prop(lines): 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 @@ -203,14 +227,24 @@ class SvnInfo(object): @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') return SvnInfo(dirname) elif base_svn_version < (1, 5): return Svn13Info(dirname) @@ -259,7 +293,7 @@ class SvnInfo(object): 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): @@ -269,7 +303,7 @@ class SvnInfo(object): 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): @@ -278,6 +312,7 @@ class SvnInfo(object): def get_externals(self): return [] + class Svn13Info(SvnInfo): def get_entries(self): code, data = _run_command(['svn', 'info', '-R', '--xml', self.path]) @@ -316,6 +351,73 @@ class Svn15Info(Svn13Info): 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 @@ -328,6 +430,110 @@ def svn_finder(dirname=''): 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(' 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) \ No newline at end of file + print(name) diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 95667b69..7abafd71 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -48,6 +48,27 @@ class TestEggInfo(unittest.TestCase): 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__) -- cgit v1.2.3