comparison planemo/lib/python3.7/site-packages/pip/_internal/vcs/subversion.py @ 1:56ad4e20f292 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:32:28 -0400
parents
children
comparison
equal deleted inserted replaced
0:d30785e31577 1:56ad4e20f292
1 from __future__ import absolute_import
2
3 import logging
4 import os
5 import re
6 import sys
7
8 from pip._internal.utils.logging import indent_log
9 from pip._internal.utils.misc import (
10 display_path, rmtree, split_auth_from_netloc,
11 )
12 from pip._internal.utils.typing import MYPY_CHECK_RUNNING
13 from pip._internal.vcs.versioncontrol import VersionControl, vcs
14
15 _svn_xml_url_re = re.compile('url="([^"]+)"')
16 _svn_rev_re = re.compile(r'committed-rev="(\d+)"')
17 _svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"')
18 _svn_info_xml_url_re = re.compile(r'<url>(.*)</url>')
19
20
21 if MYPY_CHECK_RUNNING:
22 from typing import List, Optional, Tuple
23 from pip._internal.vcs.versioncontrol import RevOptions
24
25 logger = logging.getLogger(__name__)
26
27
28 class Subversion(VersionControl):
29 name = 'svn'
30 dirname = '.svn'
31 repo_name = 'checkout'
32 schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https', 'svn+svn')
33
34 @classmethod
35 def should_add_vcs_url_prefix(cls, remote_url):
36 return True
37
38 @staticmethod
39 def get_base_rev_args(rev):
40 return ['-r', rev]
41
42 @classmethod
43 def get_revision(cls, location):
44 """
45 Return the maximum revision for all files under a given location
46 """
47 # Note: taken from setuptools.command.egg_info
48 revision = 0
49
50 for base, dirs, files in os.walk(location):
51 if cls.dirname not in dirs:
52 dirs[:] = []
53 continue # no sense walking uncontrolled subdirs
54 dirs.remove(cls.dirname)
55 entries_fn = os.path.join(base, cls.dirname, 'entries')
56 if not os.path.exists(entries_fn):
57 # FIXME: should we warn?
58 continue
59
60 dirurl, localrev = cls._get_svn_url_rev(base)
61
62 if base == location:
63 base = dirurl + '/' # save the root url
64 elif not dirurl or not dirurl.startswith(base):
65 dirs[:] = []
66 continue # not part of the same svn tree, skip it
67 revision = max(revision, localrev)
68 return revision
69
70 @classmethod
71 def get_netloc_and_auth(cls, netloc, scheme):
72 """
73 This override allows the auth information to be passed to svn via the
74 --username and --password options instead of via the URL.
75 """
76 if scheme == 'ssh':
77 # The --username and --password options can't be used for
78 # svn+ssh URLs, so keep the auth information in the URL.
79 return super(Subversion, cls).get_netloc_and_auth(netloc, scheme)
80
81 return split_auth_from_netloc(netloc)
82
83 @classmethod
84 def get_url_rev_and_auth(cls, url):
85 # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it
86 url, rev, user_pass = super(Subversion, cls).get_url_rev_and_auth(url)
87 if url.startswith('ssh://'):
88 url = 'svn+' + url
89 return url, rev, user_pass
90
91 @staticmethod
92 def make_rev_args(username, password):
93 extra_args = []
94 if username:
95 extra_args += ['--username', username]
96 if password:
97 extra_args += ['--password', password]
98
99 return extra_args
100
101 @classmethod
102 def get_remote_url(cls, location):
103 # In cases where the source is in a subdirectory, not alongside
104 # setup.py we have to look up in the location until we find a real
105 # setup.py
106 orig_location = location
107 while not os.path.exists(os.path.join(location, 'setup.py')):
108 last_location = location
109 location = os.path.dirname(location)
110 if location == last_location:
111 # We've traversed up to the root of the filesystem without
112 # finding setup.py
113 logger.warning(
114 "Could not find setup.py for directory %s (tried all "
115 "parent directories)",
116 orig_location,
117 )
118 return None
119
120 return cls._get_svn_url_rev(location)[0]
121
122 @classmethod
123 def _get_svn_url_rev(cls, location):
124 from pip._internal.exceptions import InstallationError
125
126 entries_path = os.path.join(location, cls.dirname, 'entries')
127 if os.path.exists(entries_path):
128 with open(entries_path) as f:
129 data = f.read()
130 else: # subversion >= 1.7 does not have the 'entries' file
131 data = ''
132
133 if (data.startswith('8') or
134 data.startswith('9') or
135 data.startswith('10')):
136 data = list(map(str.splitlines, data.split('\n\x0c\n')))
137 del data[0][0] # get rid of the '8'
138 url = data[0][3]
139 revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0]
140 elif data.startswith('<?xml'):
141 match = _svn_xml_url_re.search(data)
142 if not match:
143 raise ValueError('Badly formatted data: %r' % data)
144 url = match.group(1) # get repository URL
145 revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)] + [0]
146 else:
147 try:
148 # subversion >= 1.7
149 # Note that using get_remote_call_options is not necessary here
150 # because `svn info` is being run against a local directory.
151 # We don't need to worry about making sure interactive mode
152 # is being used to prompt for passwords, because passwords
153 # are only potentially needed for remote server requests.
154 xml = cls.run_command(
155 ['info', '--xml', location],
156 show_stdout=False,
157 )
158 url = _svn_info_xml_url_re.search(xml).group(1)
159 revs = [
160 int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml)
161 ]
162 except InstallationError:
163 url, revs = None, []
164
165 if revs:
166 rev = max(revs)
167 else:
168 rev = 0
169
170 return url, rev
171
172 @classmethod
173 def is_commit_id_equal(cls, dest, name):
174 """Always assume the versions don't match"""
175 return False
176
177 def __init__(self, use_interactive=None):
178 # type: (bool) -> None
179 if use_interactive is None:
180 use_interactive = sys.stdin.isatty()
181 self.use_interactive = use_interactive
182
183 # This member is used to cache the fetched version of the current
184 # ``svn`` client.
185 # Special value definitions:
186 # None: Not evaluated yet.
187 # Empty tuple: Could not parse version.
188 self._vcs_version = None # type: Optional[Tuple[int, ...]]
189
190 super(Subversion, self).__init__()
191
192 def call_vcs_version(self):
193 # type: () -> Tuple[int, ...]
194 """Query the version of the currently installed Subversion client.
195
196 :return: A tuple containing the parts of the version information or
197 ``()`` if the version returned from ``svn`` could not be parsed.
198 :raises: BadCommand: If ``svn`` is not installed.
199 """
200 # Example versions:
201 # svn, version 1.10.3 (r1842928)
202 # compiled Feb 25 2019, 14:20:39 on x86_64-apple-darwin17.0.0
203 # svn, version 1.7.14 (r1542130)
204 # compiled Mar 28 2018, 08:49:13 on x86_64-pc-linux-gnu
205 version_prefix = 'svn, version '
206 version = self.run_command(['--version'], show_stdout=False)
207 if not version.startswith(version_prefix):
208 return ()
209
210 version = version[len(version_prefix):].split()[0]
211 version_list = version.split('.')
212 try:
213 parsed_version = tuple(map(int, version_list))
214 except ValueError:
215 return ()
216
217 return parsed_version
218
219 def get_vcs_version(self):
220 # type: () -> Tuple[int, ...]
221 """Return the version of the currently installed Subversion client.
222
223 If the version of the Subversion client has already been queried,
224 a cached value will be used.
225
226 :return: A tuple containing the parts of the version information or
227 ``()`` if the version returned from ``svn`` could not be parsed.
228 :raises: BadCommand: If ``svn`` is not installed.
229 """
230 if self._vcs_version is not None:
231 # Use cached version, if available.
232 # If parsing the version failed previously (empty tuple),
233 # do not attempt to parse it again.
234 return self._vcs_version
235
236 vcs_version = self.call_vcs_version()
237 self._vcs_version = vcs_version
238 return vcs_version
239
240 def get_remote_call_options(self):
241 # type: () -> List[str]
242 """Return options to be used on calls to Subversion that contact the server.
243
244 These options are applicable for the following ``svn`` subcommands used
245 in this class.
246
247 - checkout
248 - export
249 - switch
250 - update
251
252 :return: A list of command line arguments to pass to ``svn``.
253 """
254 if not self.use_interactive:
255 # --non-interactive switch is available since Subversion 0.14.4.
256 # Subversion < 1.8 runs in interactive mode by default.
257 return ['--non-interactive']
258
259 svn_version = self.get_vcs_version()
260 # By default, Subversion >= 1.8 runs in non-interactive mode if
261 # stdin is not a TTY. Since that is how pip invokes SVN, in
262 # call_subprocess(), pip must pass --force-interactive to ensure
263 # the user can be prompted for a password, if required.
264 # SVN added the --force-interactive option in SVN 1.8. Since
265 # e.g. RHEL/CentOS 7, which is supported until 2024, ships with
266 # SVN 1.7, pip should continue to support SVN 1.7. Therefore, pip
267 # can't safely add the option if the SVN version is < 1.8 (or unknown).
268 if svn_version >= (1, 8):
269 return ['--force-interactive']
270
271 return []
272
273 def export(self, location, url):
274 """Export the svn repository at the url to the destination location"""
275 url, rev_options = self.get_url_rev_options(url)
276
277 logger.info('Exporting svn repository %s to %s', url, location)
278 with indent_log():
279 if os.path.exists(location):
280 # Subversion doesn't like to check out over an existing
281 # directory --force fixes this, but was only added in svn 1.5
282 rmtree(location)
283 cmd_args = (['export'] + self.get_remote_call_options() +
284 rev_options.to_args() + [url, location])
285 self.run_command(cmd_args, show_stdout=False)
286
287 def fetch_new(self, dest, url, rev_options):
288 # type: (str, str, RevOptions) -> None
289 rev_display = rev_options.to_display()
290 logger.info(
291 'Checking out %s%s to %s',
292 url,
293 rev_display,
294 display_path(dest),
295 )
296 cmd_args = (['checkout', '-q'] +
297 self.get_remote_call_options() +
298 rev_options.to_args() + [url, dest])
299 self.run_command(cmd_args)
300
301 def switch(self, dest, url, rev_options):
302 # type: (str, str, RevOptions) -> None
303 cmd_args = (['switch'] + self.get_remote_call_options() +
304 rev_options.to_args() + [url, dest])
305 self.run_command(cmd_args)
306
307 def update(self, dest, url, rev_options):
308 # type: (str, str, RevOptions) -> None
309 cmd_args = (['update'] + self.get_remote_call_options() +
310 rev_options.to_args() + [dest])
311 self.run_command(cmd_args)
312
313
314 vcs.register(Subversion)