Mercurial > repos > guerler > springsuite
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) |
