comparison venv/lib/python2.7/site-packages/pip/vcs/subversion.py @ 0:d67268158946 draft

planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
author bcclaywell
date Mon, 12 Oct 2015 17:43:33 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:d67268158946
1 from __future__ import absolute_import
2
3 import logging
4 import os
5 import re
6
7 from pip._vendor.six.moves.urllib import parse as urllib_parse
8
9 from pip.index import Link
10 from pip.utils import rmtree, display_path
11 from pip.utils.logging import indent_log
12 from pip.vcs import vcs, VersionControl
13
14 _svn_xml_url_re = re.compile('url="([^"]+)"')
15 _svn_rev_re = re.compile('committed-rev="(\d+)"')
16 _svn_url_re = re.compile(r'URL: (.+)')
17 _svn_revision_re = re.compile(r'Revision: (.+)')
18 _svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"')
19 _svn_info_xml_url_re = re.compile(r'<url>(.*)</url>')
20
21
22 logger = logging.getLogger(__name__)
23
24
25 class Subversion(VersionControl):
26 name = 'svn'
27 dirname = '.svn'
28 repo_name = 'checkout'
29 schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https', 'svn+svn')
30
31 def get_info(self, location):
32 """Returns (url, revision), where both are strings"""
33 assert not location.rstrip('/').endswith(self.dirname), \
34 'Bad directory: %s' % location
35 output = self.run_command(
36 ['info', location],
37 show_stdout=False,
38 extra_environ={'LANG': 'C'},
39 )
40 match = _svn_url_re.search(output)
41 if not match:
42 logger.warning(
43 'Cannot determine URL of svn checkout %s',
44 display_path(location),
45 )
46 logger.debug('Output that cannot be parsed: \n%s', output)
47 return None, None
48 url = match.group(1).strip()
49 match = _svn_revision_re.search(output)
50 if not match:
51 logger.warning(
52 'Cannot determine revision of svn checkout %s',
53 display_path(location),
54 )
55 logger.debug('Output that cannot be parsed: \n%s', output)
56 return url, None
57 return url, match.group(1)
58
59 def export(self, location):
60 """Export the svn repository at the url to the destination location"""
61 url, rev = self.get_url_rev()
62 rev_options = get_rev_options(url, rev)
63 logger.info('Exporting svn repository %s to %s', url, location)
64 with indent_log():
65 if os.path.exists(location):
66 # Subversion doesn't like to check out over an existing
67 # directory --force fixes this, but was only added in svn 1.5
68 rmtree(location)
69 self.run_command(
70 ['export'] + rev_options + [url, location],
71 filter_stdout=self._filter, show_stdout=False)
72
73 def switch(self, dest, url, rev_options):
74 self.run_command(['switch'] + rev_options + [url, dest])
75
76 def update(self, dest, rev_options):
77 self.run_command(['update'] + rev_options + [dest])
78
79 def obtain(self, dest):
80 url, rev = self.get_url_rev()
81 rev_options = get_rev_options(url, rev)
82 if rev:
83 rev_display = ' (to revision %s)' % rev
84 else:
85 rev_display = ''
86 if self.check_destination(dest, url, rev_options, rev_display):
87 logger.info(
88 'Checking out %s%s to %s',
89 url,
90 rev_display,
91 display_path(dest),
92 )
93 self.run_command(['checkout', '-q'] + rev_options + [url, dest])
94
95 def get_location(self, dist, dependency_links):
96 for url in dependency_links:
97 egg_fragment = Link(url).egg_fragment
98 if not egg_fragment:
99 continue
100 if '-' in egg_fragment:
101 # FIXME: will this work when a package has - in the name?
102 key = '-'.join(egg_fragment.split('-')[:-1]).lower()
103 else:
104 key = egg_fragment
105 if key == dist.key:
106 return url.split('#', 1)[0]
107 return None
108
109 def get_revision(self, location):
110 """
111 Return the maximum revision for all files under a given location
112 """
113 # Note: taken from setuptools.command.egg_info
114 revision = 0
115
116 for base, dirs, files in os.walk(location):
117 if self.dirname not in dirs:
118 dirs[:] = []
119 continue # no sense walking uncontrolled subdirs
120 dirs.remove(self.dirname)
121 entries_fn = os.path.join(base, self.dirname, 'entries')
122 if not os.path.exists(entries_fn):
123 # FIXME: should we warn?
124 continue
125
126 dirurl, localrev = self._get_svn_url_rev(base)
127
128 if base == location:
129 base_url = dirurl + '/' # save the root url
130 elif not dirurl or not dirurl.startswith(base_url):
131 dirs[:] = []
132 continue # not part of the same svn tree, skip it
133 revision = max(revision, localrev)
134 return revision
135
136 def get_url_rev(self):
137 # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it
138 url, rev = super(Subversion, self).get_url_rev()
139 if url.startswith('ssh://'):
140 url = 'svn+' + url
141 return url, rev
142
143 def get_url(self, location):
144 # In cases where the source is in a subdirectory, not alongside
145 # setup.py we have to look up in the location until we find a real
146 # setup.py
147 orig_location = location
148 while not os.path.exists(os.path.join(location, 'setup.py')):
149 last_location = location
150 location = os.path.dirname(location)
151 if location == last_location:
152 # We've traversed up to the root of the filesystem without
153 # finding setup.py
154 logger.warning(
155 "Could not find setup.py for directory %s (tried all "
156 "parent directories)",
157 orig_location,
158 )
159 return None
160
161 return self._get_svn_url_rev(location)[0]
162
163 def _get_svn_url_rev(self, location):
164 from pip.exceptions import InstallationError
165
166 with open(os.path.join(location, self.dirname, 'entries')) as f:
167 data = f.read()
168 if (data.startswith('8') or
169 data.startswith('9') or
170 data.startswith('10')):
171 data = list(map(str.splitlines, data.split('\n\x0c\n')))
172 del data[0][0] # get rid of the '8'
173 url = data[0][3]
174 revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0]
175 elif data.startswith('<?xml'):
176 match = _svn_xml_url_re.search(data)
177 if not match:
178 raise ValueError('Badly formatted data: %r' % data)
179 url = match.group(1) # get repository URL
180 revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)] + [0]
181 else:
182 try:
183 # subversion >= 1.7
184 xml = self.run_command(
185 ['info', '--xml', location],
186 show_stdout=False,
187 )
188 url = _svn_info_xml_url_re.search(xml).group(1)
189 revs = [
190 int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml)
191 ]
192 except InstallationError:
193 url, revs = None, []
194
195 if revs:
196 rev = max(revs)
197 else:
198 rev = 0
199
200 return url, rev
201
202 def get_tag_revs(self, svn_tag_url):
203 stdout = self.run_command(['ls', '-v', svn_tag_url], show_stdout=False)
204 results = []
205 for line in stdout.splitlines():
206 parts = line.split()
207 rev = int(parts[0])
208 tag = parts[-1].strip('/')
209 results.append((tag, rev))
210 return results
211
212 def find_tag_match(self, rev, tag_revs):
213 best_match_rev = None
214 best_tag = None
215 for tag, tag_rev in tag_revs:
216 if (tag_rev > rev and
217 (best_match_rev is None or best_match_rev > tag_rev)):
218 # FIXME: Is best_match > tag_rev really possible?
219 # or is it a sign something is wacky?
220 best_match_rev = tag_rev
221 best_tag = tag
222 return best_tag
223
224 def get_src_requirement(self, dist, location, find_tags=False):
225 repo = self.get_url(location)
226 if repo is None:
227 return None
228 parts = repo.split('/')
229 # FIXME: why not project name?
230 egg_project_name = dist.egg_name().split('-', 1)[0]
231 rev = self.get_revision(location)
232 if parts[-2] in ('tags', 'tag'):
233 # It's a tag, perfect!
234 full_egg_name = '%s-%s' % (egg_project_name, parts[-1])
235 elif parts[-2] in ('branches', 'branch'):
236 # It's a branch :(
237 full_egg_name = '%s-%s-r%s' % (dist.egg_name(), parts[-1], rev)
238 elif parts[-1] == 'trunk':
239 # Trunk :-/
240 full_egg_name = '%s-dev_r%s' % (dist.egg_name(), rev)
241 if find_tags:
242 tag_url = '/'.join(parts[:-1]) + '/tags'
243 tag_revs = self.get_tag_revs(tag_url)
244 match = self.find_tag_match(rev, tag_revs)
245 if match:
246 logger.info(
247 'trunk checkout %s seems to be equivalent to tag %s',
248 match,
249 )
250 repo = '%s/%s' % (tag_url, match)
251 full_egg_name = '%s-%s' % (egg_project_name, match)
252 else:
253 # Don't know what it is
254 logger.warning(
255 'svn URL does not fit normal structure (tags/branches/trunk): '
256 '%s',
257 repo,
258 )
259 full_egg_name = '%s-dev_r%s' % (egg_project_name, rev)
260 return 'svn+%s@%s#egg=%s' % (repo, rev, full_egg_name)
261
262
263 def get_rev_options(url, rev):
264 if rev:
265 rev_options = ['-r', rev]
266 else:
267 rev_options = []
268
269 r = urllib_parse.urlsplit(url)
270 if hasattr(r, 'username'):
271 # >= Python-2.5
272 username, password = r.username, r.password
273 else:
274 netloc = r[1]
275 if '@' in netloc:
276 auth = netloc.split('@')[0]
277 if ':' in auth:
278 username, password = auth.split(':', 1)
279 else:
280 username, password = auth, None
281 else:
282 username, password = None, None
283
284 if username:
285 rev_options += ['--username', username]
286 if password:
287 rev_options += ['--password', password]
288 return rev_options
289
290
291 vcs.register(Subversion)