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