diff options
-rwxr-xr-x | setuptools/command/egg_info.py | 36 | ||||
-rwxr-xr-x | setuptools/command/sdist.py | 45 | ||||
-rw-r--r-- | setuptools/svn_utils.py | 135 |
3 files changed, 135 insertions, 81 deletions
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 7daaee0f..7ed50d73 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -9,6 +9,7 @@ from distutils.errors import * from distutils import log from setuptools.command.sdist import sdist from setuptools.compat import basestring +from setuptools import svn_util from distutils.util import convert_path from distutils.filelist import FileList as _FileList from pkg_resources import parse_requirements, safe_name, parse_version, \ @@ -217,40 +218,21 @@ class egg_info(Command): @staticmethod def get_svn_revision(): revision = 0 - urlre = re.compile('url="([^"]+)"') - revre = re.compile('committed-rev="(\d+)"') - urlre11 = re.compile('<url>([^<>]+)</url>') - revre11 = re.compile('<commit\s+[^>]*revision="(\d+)"') for base,dirs,files in os.walk(os.curdir): if '.svn' not in dirs: dirs[:] = [] continue # no sense walking uncontrolled subdirs dirs.remove('.svn') - f = open(os.path.join(base,'.svn','entries')) - data = f.read() - f.close() - if data.startswith('<?xml'): - dirurl = urlre.search(data).group(1) # get repository URL - localrev = max([int(m.group(1)) for m in revre.finditer(data)]+[0]) - else: - try: svnver = int(data.splitlines()[0]) - except: svnver=-1 - if svnver<8: - log.warn("unrecognized .svn/entries format; skipping %s", base) - dirs[:] = [] - continue - elif svnver > 10: - p = _Popen(['svn', 'info', '--xml'], stdout=_PIPE, shell=(sys.platform=='win32')) - data = unicode(p.communicate()[0], encoding='utf-8') - dirurl = urlre11.search(data).group(1) - localrev = max([int(m.group(1)) for m in revre11.finditer(data)]+[0]) - else: - data = list(map(str.splitlines,data.split('\n\x0c\n'))) - del data[0][0] # get rid of the '8' or '9' or '10' - dirurl = data[0][3] - localrev = max([int(d[9]) for d in data if len(d)>9 and d[9]]+[0]) + enteries = svn_util.SVNEntries.load(base) + if not entries.is_valid: + log.warn(" get_svn_revision: Cannot determine how to read enteries.") + dirs[:] = [] + continue + + localrev = entries.parse_revsion() + dirurl = entries.get_url() if base==os.curdir: base_url = dirurl+'/' # save the root url diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index 39cd6043..e0c4b7e5 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -4,6 +4,7 @@ from distutils import log from glob import glob import os, re, sys, pkg_resources from glob import glob +from setuptools.svn_util import SVNEntries READMES = ('README', 'README.rst', 'README.txt') @@ -61,49 +62,13 @@ def _default_revctrl(dirname=''): def externals_finder(dirname, filename): """Find any 'svn:externals' directories""" - found = False - f = open(filename,'rt') - 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=='svn:externals': - found = True - elif kind=='V' and found: - f.close() - break - else: - f.close() - return - - for line in data.splitlines(): - parts = line.split() - if parts: - yield joinpath(dirname, parts[0]) - + for name in SVNEntries.load(dirname).get_external_dirs(filename): + yield joinpath(dirname, name) -entries_pattern = re.compile(r'name="([^"]+)"(?![^>]+deleted="true")', re.I) def entries_finder(dirname, filename): - f = open(filename,'rU') - data = f.read() - f.close() - if data.startswith('<?xml'): - for match in entries_pattern.finditer(data): - yield joinpath(dirname,unescape(match.group(1))) - else: - svnver=-1 - try: svnver = int(data.splitlines()[0]) - except: pass - if svnver<8: - log.warn("unrecognized .svn/entries format in %s", os.path.abspath(dirname)) - return - for record in map(str.splitlines, data.split('\n\x0c\n')[1:]): - # subversion 1.6/1.5/1.4 - if not record or len(record)>=6 and record[5]=="delete": - continue # skip deleted - yield joinpath(dirname, record[0]) + for record in SVNEntries.load(dirname).get_undeleted_records(): + yield joinpath(dirname, record) finders = [ diff --git a/setuptools/svn_utils.py b/setuptools/svn_utils.py index d36f64ea..dff1c4b0 100644 --- a/setuptools/svn_utils.py +++ b/setuptools/svn_utils.py @@ -19,14 +19,29 @@ def get_entries_files(base, recurse=True): #the special casing of the entry files seem to start at 1.4.x, so if we check
#for xml in entries and then fall back to the command line, this should catch everything.
+#TODO add the text entry back, and make its use dependent on the non existence of svn?
+
class SVNEntries(object):
+ svn_tool_version = ''
def __init__(self, path, data):
self.path = path
self.data = data
+ if not self.svn_tool_version:
+ self.svn_tool_version = self.get_svn_tool_version()
+
+ @staticmethod
+ def get_svn_tool_version():
+ proc = _Popen(['svn', 'propget', self.path],
+ stdout=_PIPE, shell=(sys.platform=='win32'))
+ data = unicode(proc.communicate()[0], encoding='utf-8')
+ if data is not not:
+ return data.strip()
+ else:
+ return ''
@classmethod
- def load(class_, base):
+ def load_dir(class_, base):
filename = os.path.join(base, '.svn', 'entries')
f = open(filename)
result = SVNEntries.read(f, None)
@@ -41,9 +56,14 @@ class SVNEntries(object): #entries were originally xml so pre-1.4.x
return SVNEntriesXML(data, path)
else if path is None:
- raise ValueError('Must have path to call svn')
+ result = SVNEntriesText(data, path)
else:
- return SVNEntriesCMD(data, path)
+ class_.svn_tool_version = class_.get_svn_tool_version()
+ result = SVNEntriesText(data, path)
+ if result.is_valid():
+ return svnentriescmd(data, path)
+ else:
+ return result
def parse_revision(self):
all_revs = self.parse_revision_numbers() + [0]
@@ -52,12 +72,29 @@ class SVNEntries(object): def __get_cached_external_dirs(self):
return self.external_dirs
- def get_external_dirs(self):
- #regard the shell argument, see: http://bugs.python.org/issue8557
- # and http://stackoverflow.com/questions/5658622/python-subprocess-popen-environment-path
- proc = _Popen(['svn', 'propget', self.path],
- stdout=_PIPE, shell=(sys.platform=='win32'))
- data = unicode(proc.communicate()[0], encoding='utf-8').splitlines()
+ def __get_externals_data(self, filename):
+ found = False
+ f = open(filename,'rt')
+ 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=='svn:externals':
+ found = True
+ elif kind=='V' and found:
+ f.close()
+ break
+ else:
+ f.close()
+ return ''
+
+ def get_external_dirs(self, filename):
+ data = self.__get_externals_data(filename)
+
+ if not data:
+ return
+
data = [line.split() for line in data]
# http://svnbook.red-bean.com/en/1.6/svn.advanced.externals.html
@@ -65,11 +102,63 @@ class SVNEntries(object): #but looks like we only need the local relative path names so it's just
#2 either the first column or the last (of 2 or 3)
index = -1
- if all("://" in line[-1] for line in data):
+ if all("://" in line[-1] or not line[-1] for line in data):
index = 0
-
- self.external_dirs = [line[index] for line in data]
- return self.dir_data
+
+ self.external_dirs = [line[index] for line in data if line[index]]
+ self.get_external_dirs = self.__get_cached_external_dirs
+ return self.external_dirs
+
+class SVNEntriesText(SVNEntries):
+ 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' # or '\n\x0c\n'?
+ sections = self.data.split(SECTION_DIVIDER)
+ sections = list(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 SVNEntriesXML(SVNEntries):
def is_valid(self):
@@ -85,6 +174,7 @@ class SVNEntriesXML(SVNEntries): return [
int(m.group(1))
for m in revre.finditer(self.data)
+ if m.group(1)
]
def get_undeleted_records(self):
@@ -92,6 +182,7 @@ class SVNEntriesXML(SVNEntries): results = [
unescape(match.group(1))
for match in entries_pattern.finditer(self.data)
+ if m.group(1)
]
return results
@@ -145,6 +236,7 @@ class SVNEntriesCMD(SVNEntries): int(m.group(1))
for entry in self.get_enteries()
for m in self.revre.finditer(entry)
+ if m.group(1)
]
def get_undeleted_records(self):
@@ -155,6 +247,21 @@ class SVNEntriesCMD(SVNEntries): return [
m.group(1))
for entry in self.get_enteries()
- for m in self.namere.finditer(entry)
+ for m in self.namere.finditer(entry)
+ if m.group(1)
]
+ def __get_externals_data(self, filename):
+
+ #othewise will be called twice.
+ if filename.lower() != 'dir-props':
+ return ''
+
+ #regard the shell argument, see: http://bugs.python.org/issue8557
+ # and http://stackoverflow.com/questions/5658622/python-subprocess-popen-environment-path
+ proc = _Popen(['svn', 'propget', self.path],
+ stdout=_PIPE, shell=(sys.platform=='win32'))
+ try:
+ return unicode(proc.communicate()[0], encoding='utf-8').splitlines()
+ except ValueError:
+ return ''
|