diff options
| author | Shubham Saini <shubham6405@gmail.com> | 2018-12-11 10:01:23 +0000 |
|---|---|---|
| committer | Shubham Saini <shubham6405@gmail.com> | 2018-12-11 10:01:23 +0000 |
| commit | 68df54d6629ec019142eb149dd037774f2d11e7c (patch) | |
| tree | 345bc22d46b4e01a4ba8303b94278952a4ed2b9e /venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs | |
First commit
Diffstat (limited to 'venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs')
5 files changed, 1271 insertions, 0 deletions
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/__init__.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/__init__.py new file mode 100644 index 0000000..bff94fa --- /dev/null +++ b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/__init__.py | |||
| @@ -0,0 +1,471 @@ | |||
| 1 | """Handles all VCS (version control) support""" | ||
| 2 | from __future__ import absolute_import | ||
| 3 | |||
| 4 | import copy | ||
| 5 | import errno | ||
| 6 | import logging | ||
| 7 | import os | ||
| 8 | import shutil | ||
| 9 | import sys | ||
| 10 | |||
| 11 | from pip._vendor.six.moves.urllib import parse as urllib_parse | ||
| 12 | |||
| 13 | from pip._internal.exceptions import BadCommand | ||
| 14 | from pip._internal.utils.misc import ( | ||
| 15 | display_path, backup_dir, call_subprocess, rmtree, ask_path_exists, | ||
| 16 | ) | ||
| 17 | from pip._internal.utils.typing import MYPY_CHECK_RUNNING | ||
| 18 | |||
| 19 | if MYPY_CHECK_RUNNING: | ||
| 20 | from typing import Dict, Optional, Tuple | ||
| 21 | from pip._internal.basecommand import Command | ||
| 22 | |||
| 23 | __all__ = ['vcs', 'get_src_requirement'] | ||
| 24 | |||
| 25 | |||
| 26 | logger = logging.getLogger(__name__) | ||
| 27 | |||
| 28 | |||
| 29 | class RevOptions(object): | ||
| 30 | |||
| 31 | """ | ||
| 32 | Encapsulates a VCS-specific revision to install, along with any VCS | ||
| 33 | install options. | ||
| 34 | |||
| 35 | Instances of this class should be treated as if immutable. | ||
| 36 | """ | ||
| 37 | |||
| 38 | def __init__(self, vcs, rev=None, extra_args=None): | ||
| 39 | """ | ||
| 40 | Args: | ||
| 41 | vcs: a VersionControl object. | ||
| 42 | rev: the name of the revision to install. | ||
| 43 | extra_args: a list of extra options. | ||
| 44 | """ | ||
| 45 | if extra_args is None: | ||
| 46 | extra_args = [] | ||
| 47 | |||
| 48 | self.extra_args = extra_args | ||
| 49 | self.rev = rev | ||
| 50 | self.vcs = vcs | ||
| 51 | |||
| 52 | def __repr__(self): | ||
| 53 | return '<RevOptions {}: rev={!r}>'.format(self.vcs.name, self.rev) | ||
| 54 | |||
| 55 | @property | ||
| 56 | def arg_rev(self): | ||
| 57 | if self.rev is None: | ||
| 58 | return self.vcs.default_arg_rev | ||
| 59 | |||
| 60 | return self.rev | ||
| 61 | |||
| 62 | def to_args(self): | ||
| 63 | """ | ||
| 64 | Return the VCS-specific command arguments. | ||
| 65 | """ | ||
| 66 | args = [] | ||
| 67 | rev = self.arg_rev | ||
| 68 | if rev is not None: | ||
| 69 | args += self.vcs.get_base_rev_args(rev) | ||
| 70 | args += self.extra_args | ||
| 71 | |||
| 72 | return args | ||
| 73 | |||
| 74 | def to_display(self): | ||
| 75 | if not self.rev: | ||
| 76 | return '' | ||
| 77 | |||
| 78 | return ' (to revision {})'.format(self.rev) | ||
| 79 | |||
| 80 | def make_new(self, rev): | ||
| 81 | """ | ||
| 82 | Make a copy of the current instance, but with a new rev. | ||
| 83 | |||
| 84 | Args: | ||
| 85 | rev: the name of the revision for the new object. | ||
| 86 | """ | ||
| 87 | return self.vcs.make_rev_options(rev, extra_args=self.extra_args) | ||
| 88 | |||
| 89 | |||
| 90 | class VcsSupport(object): | ||
| 91 | _registry = {} # type: Dict[str, Command] | ||
| 92 | schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp', 'svn'] | ||
| 93 | |||
| 94 | def __init__(self): | ||
| 95 | # Register more schemes with urlparse for various version control | ||
| 96 | # systems | ||
| 97 | urllib_parse.uses_netloc.extend(self.schemes) | ||
| 98 | # Python >= 2.7.4, 3.3 doesn't have uses_fragment | ||
| 99 | if getattr(urllib_parse, 'uses_fragment', None): | ||
| 100 | urllib_parse.uses_fragment.extend(self.schemes) | ||
| 101 | super(VcsSupport, self).__init__() | ||
| 102 | |||
| 103 | def __iter__(self): | ||
| 104 | return self._registry.__iter__() | ||
| 105 | |||
| 106 | @property | ||
| 107 | def backends(self): | ||
| 108 | return list(self._registry.values()) | ||
| 109 | |||
| 110 | @property | ||
| 111 | def dirnames(self): | ||
| 112 | return [backend.dirname for backend in self.backends] | ||
| 113 | |||
| 114 | @property | ||
| 115 | def all_schemes(self): | ||
| 116 | schemes = [] | ||
| 117 | for backend in self.backends: | ||
| 118 | schemes.extend(backend.schemes) | ||
| 119 | return schemes | ||
| 120 | |||
| 121 | def register(self, cls): | ||
| 122 | if not hasattr(cls, 'name'): | ||
| 123 | logger.warning('Cannot register VCS %s', cls.__name__) | ||
| 124 | return | ||
| 125 | if cls.name not in self._registry: | ||
| 126 | self._registry[cls.name] = cls | ||
| 127 | logger.debug('Registered VCS backend: %s', cls.name) | ||
| 128 | |||
| 129 | def unregister(self, cls=None, name=None): | ||
| 130 | if name in self._registry: | ||
| 131 | del self._registry[name] | ||
| 132 | elif cls in self._registry.values(): | ||
| 133 | del self._registry[cls.name] | ||
| 134 | else: | ||
| 135 | logger.warning('Cannot unregister because no class or name given') | ||
| 136 | |||
| 137 | def get_backend_name(self, location): | ||
| 138 | """ | ||
| 139 | Return the name of the version control backend if found at given | ||
| 140 | location, e.g. vcs.get_backend_name('/path/to/vcs/checkout') | ||
| 141 | """ | ||
| 142 | for vc_type in self._registry.values(): | ||
| 143 | if vc_type.controls_location(location): | ||
| 144 | logger.debug('Determine that %s uses VCS: %s', | ||
| 145 | location, vc_type.name) | ||
| 146 | return vc_type.name | ||
| 147 | return None | ||
| 148 | |||
| 149 | def get_backend(self, name): | ||
| 150 | name = name.lower() | ||
| 151 | if name in self._registry: | ||
| 152 | return self._registry[name] | ||
| 153 | |||
| 154 | def get_backend_from_location(self, location): | ||
| 155 | vc_type = self.get_backend_name(location) | ||
| 156 | if vc_type: | ||
| 157 | return self.get_backend(vc_type) | ||
| 158 | return None | ||
| 159 | |||
| 160 | |||
| 161 | vcs = VcsSupport() | ||
| 162 | |||
| 163 | |||
| 164 | class VersionControl(object): | ||
| 165 | name = '' | ||
| 166 | dirname = '' | ||
| 167 | # List of supported schemes for this Version Control | ||
| 168 | schemes = () # type: Tuple[str, ...] | ||
| 169 | # Iterable of environment variable names to pass to call_subprocess(). | ||
| 170 | unset_environ = () # type: Tuple[str, ...] | ||
| 171 | default_arg_rev = None # type: Optional[str] | ||
| 172 | |||
| 173 | def __init__(self, url=None, *args, **kwargs): | ||
| 174 | self.url = url | ||
| 175 | super(VersionControl, self).__init__(*args, **kwargs) | ||
| 176 | |||
| 177 | def get_base_rev_args(self, rev): | ||
| 178 | """ | ||
| 179 | Return the base revision arguments for a vcs command. | ||
| 180 | |||
| 181 | Args: | ||
| 182 | rev: the name of a revision to install. Cannot be None. | ||
| 183 | """ | ||
| 184 | raise NotImplementedError | ||
| 185 | |||
| 186 | def make_rev_options(self, rev=None, extra_args=None): | ||
| 187 | """ | ||
| 188 | Return a RevOptions object. | ||
| 189 | |||
| 190 | Args: | ||
| 191 | rev: the name of a revision to install. | ||
| 192 | extra_args: a list of extra options. | ||
| 193 | """ | ||
| 194 | return RevOptions(self, rev, extra_args=extra_args) | ||
| 195 | |||
| 196 | def _is_local_repository(self, repo): | ||
| 197 | """ | ||
| 198 | posix absolute paths start with os.path.sep, | ||
| 199 | win32 ones start with drive (like c:\\folder) | ||
| 200 | """ | ||
| 201 | drive, tail = os.path.splitdrive(repo) | ||
| 202 | return repo.startswith(os.path.sep) or drive | ||
| 203 | |||
| 204 | # See issue #1083 for why this method was introduced: | ||
| 205 | # https://github.com/pypa/pip/issues/1083 | ||
| 206 | def translate_egg_surname(self, surname): | ||
| 207 | # For example, Django has branches of the form "stable/1.7.x". | ||
| 208 | return surname.replace('/', '_') | ||
| 209 | |||
| 210 | def export(self, location): | ||
| 211 | """ | ||
| 212 | Export the repository at the url to the destination location | ||
| 213 | i.e. only download the files, without vcs informations | ||
| 214 | """ | ||
| 215 | raise NotImplementedError | ||
| 216 | |||
| 217 | def get_url_rev(self): | ||
| 218 | """ | ||
| 219 | Returns the correct repository URL and revision by parsing the given | ||
| 220 | repository URL | ||
| 221 | """ | ||
| 222 | error_message = ( | ||
| 223 | "Sorry, '%s' is a malformed VCS url. " | ||
| 224 | "The format is <vcs>+<protocol>://<url>, " | ||
| 225 | "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp" | ||
| 226 | ) | ||
| 227 | assert '+' in self.url, error_message % self.url | ||
| 228 | url = self.url.split('+', 1)[1] | ||
| 229 | scheme, netloc, path, query, frag = urllib_parse.urlsplit(url) | ||
| 230 | rev = None | ||
| 231 | if '@' in path: | ||
| 232 | path, rev = path.rsplit('@', 1) | ||
| 233 | url = urllib_parse.urlunsplit((scheme, netloc, path, query, '')) | ||
| 234 | return url, rev | ||
| 235 | |||
| 236 | def get_info(self, location): | ||
| 237 | """ | ||
| 238 | Returns (url, revision), where both are strings | ||
| 239 | """ | ||
| 240 | assert not location.rstrip('/').endswith(self.dirname), \ | ||
| 241 | 'Bad directory: %s' % location | ||
| 242 | return self.get_url(location), self.get_revision(location) | ||
| 243 | |||
| 244 | def normalize_url(self, url): | ||
| 245 | """ | ||
| 246 | Normalize a URL for comparison by unquoting it and removing any | ||
| 247 | trailing slash. | ||
| 248 | """ | ||
| 249 | return urllib_parse.unquote(url).rstrip('/') | ||
| 250 | |||
| 251 | def compare_urls(self, url1, url2): | ||
| 252 | """ | ||
| 253 | Compare two repo URLs for identity, ignoring incidental differences. | ||
| 254 | """ | ||
| 255 | return (self.normalize_url(url1) == self.normalize_url(url2)) | ||
| 256 | |||
| 257 | def obtain(self, dest): | ||
| 258 | """ | ||
| 259 | Called when installing or updating an editable package, takes the | ||
| 260 | source path of the checkout. | ||
| 261 | """ | ||
| 262 | raise NotImplementedError | ||
| 263 | |||
| 264 | def switch(self, dest, url, rev_options): | ||
| 265 | """ | ||
| 266 | Switch the repo at ``dest`` to point to ``URL``. | ||
| 267 | |||
| 268 | Args: | ||
| 269 | rev_options: a RevOptions object. | ||
| 270 | """ | ||
| 271 | raise NotImplementedError | ||
| 272 | |||
| 273 | def update(self, dest, rev_options): | ||
| 274 | """ | ||
| 275 | Update an already-existing repo to the given ``rev_options``. | ||
| 276 | |||
| 277 | Args: | ||
| 278 | rev_options: a RevOptions object. | ||
| 279 | """ | ||
| 280 | raise NotImplementedError | ||
| 281 | |||
| 282 | def is_commit_id_equal(self, dest, name): | ||
| 283 | """ | ||
| 284 | Return whether the id of the current commit equals the given name. | ||
| 285 | |||
| 286 | Args: | ||
| 287 | dest: the repository directory. | ||
| 288 | name: a string name. | ||
| 289 | """ | ||
| 290 | raise NotImplementedError | ||
| 291 | |||
| 292 | def check_destination(self, dest, url, rev_options): | ||
| 293 | """ | ||
| 294 | Prepare a location to receive a checkout/clone. | ||
| 295 | |||
| 296 | Return True if the location is ready for (and requires) a | ||
| 297 | checkout/clone, False otherwise. | ||
| 298 | |||
| 299 | Args: | ||
| 300 | rev_options: a RevOptions object. | ||
| 301 | """ | ||
| 302 | checkout = True | ||
| 303 | prompt = False | ||
| 304 | rev_display = rev_options.to_display() | ||
| 305 | if os.path.exists(dest): | ||
| 306 | checkout = False | ||
| 307 | if os.path.exists(os.path.join(dest, self.dirname)): | ||
| 308 | existing_url = self.get_url(dest) | ||
| 309 | if self.compare_urls(existing_url, url): | ||
| 310 | logger.debug( | ||
| 311 | '%s in %s exists, and has correct URL (%s)', | ||
| 312 | self.repo_name.title(), | ||
| 313 | display_path(dest), | ||
| 314 | url, | ||
| 315 | ) | ||
| 316 | if not self.is_commit_id_equal(dest, rev_options.rev): | ||
| 317 | logger.info( | ||
| 318 | 'Updating %s %s%s', | ||
| 319 | display_path(dest), | ||
| 320 | self.repo_name, | ||
| 321 | rev_display, | ||
| 322 | ) | ||
| 323 | self.update(dest, rev_options) | ||
| 324 | else: | ||
| 325 | logger.info( | ||
| 326 | 'Skipping because already up-to-date.') | ||
| 327 | else: | ||
| 328 | logger.warning( | ||
| 329 | '%s %s in %s exists with URL %s', | ||
| 330 | self.name, | ||
| 331 | self.repo_name, | ||
| 332 | display_path(dest), | ||
| 333 | existing_url, | ||
| 334 | ) | ||
| 335 | prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', | ||
| 336 | ('s', 'i', 'w', 'b')) | ||
| 337 | else: | ||
| 338 | logger.warning( | ||
| 339 | 'Directory %s already exists, and is not a %s %s.', | ||
| 340 | dest, | ||
| 341 | self.name, | ||
| 342 | self.repo_name, | ||
| 343 | ) | ||
| 344 | prompt = ('(i)gnore, (w)ipe, (b)ackup ', ('i', 'w', 'b')) | ||
| 345 | if prompt: | ||
| 346 | logger.warning( | ||
| 347 | 'The plan is to install the %s repository %s', | ||
| 348 | self.name, | ||
| 349 | url, | ||
| 350 | ) | ||
| 351 | response = ask_path_exists('What to do? %s' % prompt[0], | ||
| 352 | prompt[1]) | ||
| 353 | |||
| 354 | if response == 's': | ||
| 355 | logger.info( | ||
| 356 | 'Switching %s %s to %s%s', | ||
| 357 | self.repo_name, | ||
| 358 | display_path(dest), | ||
| 359 | url, | ||
| 360 | rev_display, | ||
| 361 | ) | ||
| 362 | self.switch(dest, url, rev_options) | ||
| 363 | elif response == 'i': | ||
| 364 | # do nothing | ||
| 365 | pass | ||
| 366 | elif response == 'w': | ||
| 367 | logger.warning('Deleting %s', display_path(dest)) | ||
| 368 | rmtree(dest) | ||
| 369 | checkout = True | ||
| 370 | elif response == 'b': | ||
| 371 | dest_dir = backup_dir(dest) | ||
| 372 | logger.warning( | ||
| 373 | 'Backing up %s to %s', display_path(dest), dest_dir, | ||
| 374 | ) | ||
| 375 | shutil.move(dest, dest_dir) | ||
| 376 | checkout = True | ||
| 377 | elif response == 'a': | ||
| 378 | sys.exit(-1) | ||
| 379 | return checkout | ||
| 380 | |||
| 381 | def unpack(self, location): | ||
| 382 | """ | ||
| 383 | Clean up current location and download the url repository | ||
| 384 | (and vcs infos) into location | ||
| 385 | """ | ||
| 386 | if os.path.exists(location): | ||
| 387 | rmtree(location) | ||
| 388 | self.obtain(location) | ||
| 389 | |||
| 390 | def get_src_requirement(self, dist, location): | ||
| 391 | """ | ||
| 392 | Return a string representing the requirement needed to | ||
| 393 | redownload the files currently present in location, something | ||
| 394 | like: | ||
| 395 | {repository_url}@{revision}#egg={project_name}-{version_identifier} | ||
| 396 | """ | ||
| 397 | raise NotImplementedError | ||
| 398 | |||
| 399 | def get_url(self, location): | ||
| 400 | """ | ||
| 401 | Return the url used at location | ||
| 402 | Used in get_info or check_destination | ||
| 403 | """ | ||
| 404 | raise NotImplementedError | ||
| 405 | |||
| 406 | def get_revision(self, location): | ||
| 407 | """ | ||
| 408 | Return the current commit id of the files at the given location. | ||
| 409 | """ | ||
| 410 | raise NotImplementedError | ||
| 411 | |||
| 412 | def run_command(self, cmd, show_stdout=True, cwd=None, | ||
| 413 | on_returncode='raise', | ||
| 414 | command_desc=None, | ||
| 415 | extra_environ=None, spinner=None): | ||
| 416 | """ | ||
| 417 | Run a VCS subcommand | ||
| 418 | This is simply a wrapper around call_subprocess that adds the VCS | ||
| 419 | command name, and checks that the VCS is available | ||
| 420 | """ | ||
| 421 | cmd = [self.name] + cmd | ||
| 422 | try: | ||
| 423 | return call_subprocess(cmd, show_stdout, cwd, | ||
| 424 | on_returncode, | ||
| 425 | command_desc, extra_environ, | ||
| 426 | unset_environ=self.unset_environ, | ||
| 427 | spinner=spinner) | ||
| 428 | except OSError as e: | ||
| 429 | # errno.ENOENT = no such file or directory | ||
| 430 | # In other words, the VCS executable isn't available | ||
| 431 | if e.errno == errno.ENOENT: | ||
| 432 | raise BadCommand( | ||
| 433 | 'Cannot find command %r - do you have ' | ||
| 434 | '%r installed and in your ' | ||
| 435 | 'PATH?' % (self.name, self.name)) | ||
| 436 | else: | ||
| 437 | raise # re-raise exception if a different error occurred | ||
| 438 | |||
| 439 | @classmethod | ||
| 440 | def controls_location(cls, location): | ||
| 441 | """ | ||
| 442 | Check if a location is controlled by the vcs. | ||
| 443 | It is meant to be overridden to implement smarter detection | ||
| 444 | mechanisms for specific vcs. | ||
| 445 | """ | ||
| 446 | logger.debug('Checking in %s for %s (%s)...', | ||
| 447 | location, cls.dirname, cls.name) | ||
| 448 | path = os.path.join(location, cls.dirname) | ||
| 449 | return os.path.exists(path) | ||
| 450 | |||
| 451 | |||
| 452 | def get_src_requirement(dist, location): | ||
| 453 | version_control = vcs.get_backend_from_location(location) | ||
| 454 | if version_control: | ||
| 455 | try: | ||
| 456 | return version_control().get_src_requirement(dist, | ||
| 457 | location) | ||
| 458 | except BadCommand: | ||
| 459 | logger.warning( | ||
| 460 | 'cannot determine version of editable source in %s ' | ||
| 461 | '(%s command not found in path)', | ||
| 462 | location, | ||
| 463 | version_control.name, | ||
| 464 | ) | ||
| 465 | return dist.as_requirement() | ||
| 466 | logger.warning( | ||
| 467 | 'cannot determine version of editable source in %s (is not SVN ' | ||
| 468 | 'checkout, Git clone, Mercurial clone or Bazaar branch)', | ||
| 469 | location, | ||
| 470 | ) | ||
| 471 | return dist.as_requirement() | ||
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/bazaar.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/bazaar.py new file mode 100644 index 0000000..6ed629a --- /dev/null +++ b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/bazaar.py | |||
| @@ -0,0 +1,113 @@ | |||
| 1 | from __future__ import absolute_import | ||
| 2 | |||
| 3 | import logging | ||
| 4 | import os | ||
| 5 | |||
| 6 | from pip._vendor.six.moves.urllib import parse as urllib_parse | ||
| 7 | |||
| 8 | from pip._internal.download import path_to_url | ||
| 9 | from pip._internal.utils.misc import display_path, rmtree | ||
| 10 | from pip._internal.utils.temp_dir import TempDirectory | ||
| 11 | from pip._internal.vcs import VersionControl, vcs | ||
| 12 | |||
| 13 | logger = logging.getLogger(__name__) | ||
| 14 | |||
| 15 | |||
| 16 | class Bazaar(VersionControl): | ||
| 17 | name = 'bzr' | ||
| 18 | dirname = '.bzr' | ||
| 19 | repo_name = 'branch' | ||
| 20 | schemes = ( | ||
| 21 | 'bzr', 'bzr+http', 'bzr+https', 'bzr+ssh', 'bzr+sftp', 'bzr+ftp', | ||
| 22 | 'bzr+lp', | ||
| 23 | ) | ||
| 24 | |||
| 25 | def __init__(self, url=None, *args, **kwargs): | ||
| 26 | super(Bazaar, self).__init__(url, *args, **kwargs) | ||
| 27 | # This is only needed for python <2.7.5 | ||
| 28 | # Register lp but do not expose as a scheme to support bzr+lp. | ||
| 29 | if getattr(urllib_parse, 'uses_fragment', None): | ||
| 30 | urllib_parse.uses_fragment.extend(['lp']) | ||
| 31 | |||
| 32 | def get_base_rev_args(self, rev): | ||
| 33 | return ['-r', rev] | ||
| 34 | |||
| 35 | def export(self, location): | ||
| 36 | """ | ||
| 37 | Export the Bazaar repository at the url to the destination location | ||
| 38 | """ | ||
| 39 | # Remove the location to make sure Bazaar can export it correctly | ||
| 40 | if os.path.exists(location): | ||
| 41 | rmtree(location) | ||
| 42 | |||
| 43 | with TempDirectory(kind="export") as temp_dir: | ||
| 44 | self.unpack(temp_dir.path) | ||
| 45 | |||
| 46 | self.run_command( | ||
| 47 | ['export', location], | ||
| 48 | cwd=temp_dir.path, show_stdout=False, | ||
| 49 | ) | ||
| 50 | |||
| 51 | def switch(self, dest, url, rev_options): | ||
| 52 | self.run_command(['switch', url], cwd=dest) | ||
| 53 | |||
| 54 | def update(self, dest, rev_options): | ||
| 55 | cmd_args = ['pull', '-q'] + rev_options.to_args() | ||
| 56 | self.run_command(cmd_args, cwd=dest) | ||
| 57 | |||
| 58 | def obtain(self, dest): | ||
| 59 | url, rev = self.get_url_rev() | ||
| 60 | rev_options = self.make_rev_options(rev) | ||
| 61 | if self.check_destination(dest, url, rev_options): | ||
| 62 | rev_display = rev_options.to_display() | ||
| 63 | logger.info( | ||
| 64 | 'Checking out %s%s to %s', | ||
| 65 | url, | ||
| 66 | rev_display, | ||
| 67 | display_path(dest), | ||
| 68 | ) | ||
| 69 | cmd_args = ['branch', '-q'] + rev_options.to_args() + [url, dest] | ||
| 70 | self.run_command(cmd_args) | ||
| 71 | |||
| 72 | def get_url_rev(self): | ||
| 73 | # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it | ||
| 74 | url, rev = super(Bazaar, self).get_url_rev() | ||
| 75 | if url.startswith('ssh://'): | ||
| 76 | url = 'bzr+' + url | ||
| 77 | return url, rev | ||
| 78 | |||
| 79 | def get_url(self, location): | ||
| 80 | urls = self.run_command(['info'], show_stdout=False, cwd=location) | ||
| 81 | for line in urls.splitlines(): | ||
| 82 | line = line.strip() | ||
| 83 | for x in ('checkout of branch: ', | ||
| 84 | 'parent branch: '): | ||
| 85 | if line.startswith(x): | ||
| 86 | repo = line.split(x)[1] | ||
| 87 | if self._is_local_repository(repo): | ||
| 88 | return path_to_url(repo) | ||
| 89 | return repo | ||
| 90 | return None | ||
| 91 | |||
| 92 | def get_revision(self, location): | ||
| 93 | revision = self.run_command( | ||
| 94 | ['revno'], show_stdout=False, cwd=location, | ||
| 95 | ) | ||
| 96 | return revision.splitlines()[-1] | ||
| 97 | |||
| 98 | def get_src_requirement(self, dist, location): | ||
| 99 | repo = self.get_url(location) | ||
| 100 | if not repo: | ||
| 101 | return None | ||
| 102 | if not repo.lower().startswith('bzr:'): | ||
| 103 | repo = 'bzr+' + repo | ||
| 104 | egg_project_name = dist.egg_name().split('-', 1)[0] | ||
| 105 | current_rev = self.get_revision(location) | ||
| 106 | return '%s@%s#egg=%s' % (repo, current_rev, egg_project_name) | ||
| 107 | |||
| 108 | def is_commit_id_equal(self, dest, name): | ||
| 109 | """Always assume the versions don't match""" | ||
| 110 | return False | ||
| 111 | |||
| 112 | |||
| 113 | vcs.register(Bazaar) | ||
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/git.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/git.py new file mode 100644 index 0000000..7a63dfa --- /dev/null +++ b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/git.py | |||
| @@ -0,0 +1,311 @@ | |||
| 1 | from __future__ import absolute_import | ||
| 2 | |||
| 3 | import logging | ||
| 4 | import os.path | ||
| 5 | import re | ||
| 6 | |||
| 7 | from pip._vendor.packaging.version import parse as parse_version | ||
| 8 | from pip._vendor.six.moves.urllib import parse as urllib_parse | ||
| 9 | from pip._vendor.six.moves.urllib import request as urllib_request | ||
| 10 | |||
| 11 | from pip._internal.compat import samefile | ||
| 12 | from pip._internal.exceptions import BadCommand | ||
| 13 | from pip._internal.utils.misc import display_path | ||
| 14 | from pip._internal.utils.temp_dir import TempDirectory | ||
| 15 | from pip._internal.vcs import VersionControl, vcs | ||
| 16 | |||
| 17 | urlsplit = urllib_parse.urlsplit | ||
| 18 | urlunsplit = urllib_parse.urlunsplit | ||
| 19 | |||
| 20 | |||
| 21 | logger = logging.getLogger(__name__) | ||
| 22 | |||
| 23 | |||
| 24 | HASH_REGEX = re.compile('[a-fA-F0-9]{40}') | ||
| 25 | |||
| 26 | |||
| 27 | def looks_like_hash(sha): | ||
| 28 | return bool(HASH_REGEX.match(sha)) | ||
| 29 | |||
| 30 | |||
| 31 | class Git(VersionControl): | ||
| 32 | name = 'git' | ||
| 33 | dirname = '.git' | ||
| 34 | repo_name = 'clone' | ||
| 35 | schemes = ( | ||
| 36 | 'git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file', | ||
| 37 | ) | ||
| 38 | # Prevent the user's environment variables from interfering with pip: | ||
| 39 | # https://github.com/pypa/pip/issues/1130 | ||
| 40 | unset_environ = ('GIT_DIR', 'GIT_WORK_TREE') | ||
| 41 | default_arg_rev = 'HEAD' | ||
| 42 | |||
| 43 | def __init__(self, url=None, *args, **kwargs): | ||
| 44 | |||
| 45 | # Works around an apparent Git bug | ||
| 46 | # (see http://article.gmane.org/gmane.comp.version-control.git/146500) | ||
| 47 | if url: | ||
| 48 | scheme, netloc, path, query, fragment = urlsplit(url) | ||
| 49 | if scheme.endswith('file'): | ||
| 50 | initial_slashes = path[:-len(path.lstrip('/'))] | ||
| 51 | newpath = ( | ||
| 52 | initial_slashes + | ||
| 53 | urllib_request.url2pathname(path) | ||
| 54 | .replace('\\', '/').lstrip('/') | ||
| 55 | ) | ||
| 56 | url = urlunsplit((scheme, netloc, newpath, query, fragment)) | ||
| 57 | after_plus = scheme.find('+') + 1 | ||
| 58 | url = scheme[:after_plus] + urlunsplit( | ||
| 59 | (scheme[after_plus:], netloc, newpath, query, fragment), | ||
| 60 | ) | ||
| 61 | |||
| 62 | super(Git, self).__init__(url, *args, **kwargs) | ||
| 63 | |||
| 64 | def get_base_rev_args(self, rev): | ||
| 65 | return [rev] | ||
| 66 | |||
| 67 | def get_git_version(self): | ||
| 68 | VERSION_PFX = 'git version ' | ||
| 69 | version = self.run_command(['version'], show_stdout=False) | ||
| 70 | if version.startswith(VERSION_PFX): | ||
| 71 | version = version[len(VERSION_PFX):].split()[0] | ||
| 72 | else: | ||
| 73 | version = '' | ||
| 74 | # get first 3 positions of the git version becasue | ||
| 75 | # on windows it is x.y.z.windows.t, and this parses as | ||
| 76 | # LegacyVersion which always smaller than a Version. | ||
| 77 | version = '.'.join(version.split('.')[:3]) | ||
| 78 | return parse_version(version) | ||
| 79 | |||
| 80 | def export(self, location): | ||
| 81 | """Export the Git repository at the url to the destination location""" | ||
| 82 | if not location.endswith('/'): | ||
| 83 | location = location + '/' | ||
| 84 | |||
| 85 | with TempDirectory(kind="export") as temp_dir: | ||
| 86 | self.unpack(temp_dir.path) | ||
| 87 | self.run_command( | ||
| 88 | ['checkout-index', '-a', '-f', '--prefix', location], | ||
| 89 | show_stdout=False, cwd=temp_dir.path | ||
| 90 | ) | ||
| 91 | |||
| 92 | def get_revision_sha(self, dest, rev): | ||
| 93 | """ | ||
| 94 | Return a commit hash for the given revision if it names a remote | ||
| 95 | branch or tag. Otherwise, return None. | ||
| 96 | |||
| 97 | Args: | ||
| 98 | dest: the repository directory. | ||
| 99 | rev: the revision name. | ||
| 100 | """ | ||
| 101 | # Pass rev to pre-filter the list. | ||
| 102 | output = self.run_command(['show-ref', rev], cwd=dest, | ||
| 103 | show_stdout=False, on_returncode='ignore') | ||
| 104 | refs = {} | ||
| 105 | for line in output.strip().splitlines(): | ||
| 106 | try: | ||
| 107 | sha, ref = line.split() | ||
| 108 | except ValueError: | ||
| 109 | # Include the offending line to simplify troubleshooting if | ||
| 110 | # this error ever occurs. | ||
| 111 | raise ValueError('unexpected show-ref line: {!r}'.format(line)) | ||
| 112 | |||
| 113 | refs[ref] = sha | ||
| 114 | |||
| 115 | branch_ref = 'refs/remotes/origin/{}'.format(rev) | ||
| 116 | tag_ref = 'refs/tags/{}'.format(rev) | ||
| 117 | |||
| 118 | return refs.get(branch_ref) or refs.get(tag_ref) | ||
| 119 | |||
| 120 | def check_rev_options(self, dest, rev_options): | ||
| 121 | """Check the revision options before checkout. | ||
| 122 | |||
| 123 | Returns a new RevOptions object for the SHA1 of the branch or tag | ||
| 124 | if found. | ||
| 125 | |||
| 126 | Args: | ||
| 127 | rev_options: a RevOptions object. | ||
| 128 | """ | ||
| 129 | rev = rev_options.arg_rev | ||
| 130 | sha = self.get_revision_sha(dest, rev) | ||
| 131 | |||
| 132 | if sha is not None: | ||
| 133 | return rev_options.make_new(sha) | ||
| 134 | |||
| 135 | # Do not show a warning for the common case of something that has | ||
| 136 | # the form of a Git commit hash. | ||
| 137 | if not looks_like_hash(rev): | ||
| 138 | logger.warning( | ||
| 139 | "Did not find branch or tag '%s', assuming revision or ref.", | ||
| 140 | rev, | ||
| 141 | ) | ||
| 142 | return rev_options | ||
| 143 | |||
| 144 | def is_commit_id_equal(self, dest, name): | ||
| 145 | """ | ||
| 146 | Return whether the current commit hash equals the given name. | ||
| 147 | |||
| 148 | Args: | ||
| 149 | dest: the repository directory. | ||
| 150 | name: a string name. | ||
| 151 | """ | ||
| 152 | if not name: | ||
| 153 | # Then avoid an unnecessary subprocess call. | ||
| 154 | return False | ||
| 155 | |||
| 156 | return self.get_revision(dest) == name | ||
| 157 | |||
| 158 | def switch(self, dest, url, rev_options): | ||
| 159 | self.run_command(['config', 'remote.origin.url', url], cwd=dest) | ||
| 160 | cmd_args = ['checkout', '-q'] + rev_options.to_args() | ||
| 161 | self.run_command(cmd_args, cwd=dest) | ||
| 162 | |||
| 163 | self.update_submodules(dest) | ||
| 164 | |||
| 165 | def update(self, dest, rev_options): | ||
| 166 | # First fetch changes from the default remote | ||
| 167 | if self.get_git_version() >= parse_version('1.9.0'): | ||
| 168 | # fetch tags in addition to everything else | ||
| 169 | self.run_command(['fetch', '-q', '--tags'], cwd=dest) | ||
| 170 | else: | ||
| 171 | self.run_command(['fetch', '-q'], cwd=dest) | ||
| 172 | # Then reset to wanted revision (maybe even origin/master) | ||
| 173 | rev_options = self.check_rev_options(dest, rev_options) | ||
| 174 | cmd_args = ['reset', '--hard', '-q'] + rev_options.to_args() | ||
| 175 | self.run_command(cmd_args, cwd=dest) | ||
| 176 | #: update submodules | ||
| 177 | self.update_submodules(dest) | ||
| 178 | |||
| 179 | def obtain(self, dest): | ||
| 180 | url, rev = self.get_url_rev() | ||
| 181 | rev_options = self.make_rev_options(rev) | ||
| 182 | if self.check_destination(dest, url, rev_options): | ||
| 183 | rev_display = rev_options.to_display() | ||
| 184 | logger.info( | ||
| 185 | 'Cloning %s%s to %s', url, rev_display, display_path(dest), | ||
| 186 | ) | ||
| 187 | self.run_command(['clone', '-q', url, dest]) | ||
| 188 | |||
| 189 | if rev: | ||
| 190 | rev_options = self.check_rev_options(dest, rev_options) | ||
| 191 | # Only do a checkout if the current commit id doesn't match | ||
| 192 | # the requested revision. | ||
| 193 | if not self.is_commit_id_equal(dest, rev_options.rev): | ||
| 194 | rev = rev_options.rev | ||
| 195 | # Only fetch the revision if it's a ref | ||
| 196 | if rev.startswith('refs/'): | ||
| 197 | self.run_command( | ||
| 198 | ['fetch', '-q', url] + rev_options.to_args(), | ||
| 199 | cwd=dest, | ||
| 200 | ) | ||
| 201 | # Change the revision to the SHA of the ref we fetched | ||
| 202 | rev = 'FETCH_HEAD' | ||
| 203 | self.run_command(['checkout', '-q', rev], cwd=dest) | ||
| 204 | |||
| 205 | #: repo may contain submodules | ||
| 206 | self.update_submodules(dest) | ||
| 207 | |||
| 208 | def get_url(self, location): | ||
| 209 | """Return URL of the first remote encountered.""" | ||
| 210 | remotes = self.run_command( | ||
| 211 | ['config', '--get-regexp', r'remote\..*\.url'], | ||
| 212 | show_stdout=False, cwd=location, | ||
| 213 | ) | ||
| 214 | remotes = remotes.splitlines() | ||
| 215 | found_remote = remotes[0] | ||
| 216 | for remote in remotes: | ||
| 217 | if remote.startswith('remote.origin.url '): | ||
| 218 | found_remote = remote | ||
| 219 | break | ||
| 220 | url = found_remote.split(' ')[1] | ||
| 221 | return url.strip() | ||
| 222 | |||
| 223 | def get_revision(self, location): | ||
| 224 | current_rev = self.run_command( | ||
| 225 | ['rev-parse', 'HEAD'], show_stdout=False, cwd=location, | ||
| 226 | ) | ||
| 227 | return current_rev.strip() | ||
| 228 | |||
| 229 | def _get_subdirectory(self, location): | ||
| 230 | """Return the relative path of setup.py to the git repo root.""" | ||
| 231 | # find the repo root | ||
| 232 | git_dir = self.run_command(['rev-parse', '--git-dir'], | ||
| 233 | show_stdout=False, cwd=location).strip() | ||
| 234 | if not os.path.isabs(git_dir): | ||
| 235 | git_dir = os.path.join(location, git_dir) | ||
| 236 | root_dir = os.path.join(git_dir, '..') | ||
| 237 | # find setup.py | ||
| 238 | orig_location = location | ||
| 239 | while not os.path.exists(os.path.join(location, 'setup.py')): | ||
| 240 | last_location = location | ||
| 241 | location = os.path.dirname(location) | ||
| 242 | if location == last_location: | ||
| 243 | # We've traversed up to the root of the filesystem without | ||
| 244 | # finding setup.py | ||
| 245 | logger.warning( | ||
| 246 | "Could not find setup.py for directory %s (tried all " | ||
| 247 | "parent directories)", | ||
| 248 | orig_location, | ||
| 249 | ) | ||
| 250 | return None | ||
| 251 | # relative path of setup.py to repo root | ||
| 252 | if samefile(root_dir, location): | ||
| 253 | return None | ||
| 254 | return os.path.relpath(location, root_dir) | ||
| 255 | |||
| 256 | def get_src_requirement(self, dist, location): | ||
| 257 | repo = self.get_url(location) | ||
| 258 | if not repo.lower().startswith('git:'): | ||
| 259 | repo = 'git+' + repo | ||
| 260 | egg_project_name = dist.egg_name().split('-', 1)[0] | ||
| 261 | if not repo: | ||
| 262 | return None | ||
| 263 | current_rev = self.get_revision(location) | ||
| 264 | req = '%s@%s#egg=%s' % (repo, current_rev, egg_project_name) | ||
| 265 | subdirectory = self._get_subdirectory(location) | ||
| 266 | if subdirectory: | ||
| 267 | req += '&subdirectory=' + subdirectory | ||
| 268 | return req | ||
| 269 | |||
| 270 | def get_url_rev(self): | ||
| 271 | """ | ||
| 272 | Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. | ||
| 273 | That's required because although they use SSH they sometimes doesn't | ||
| 274 | work with a ssh:// scheme (e.g. Github). But we need a scheme for | ||
| 275 | parsing. Hence we remove it again afterwards and return it as a stub. | ||
| 276 | """ | ||
| 277 | if '://' not in self.url: | ||
| 278 | assert 'file:' not in self.url | ||
| 279 | self.url = self.url.replace('git+', 'git+ssh://') | ||
| 280 | url, rev = super(Git, self).get_url_rev() | ||
| 281 | url = url.replace('ssh://', '') | ||
| 282 | else: | ||
| 283 | url, rev = super(Git, self).get_url_rev() | ||
| 284 | |||
| 285 | return url, rev | ||
| 286 | |||
| 287 | def update_submodules(self, location): | ||
| 288 | if not os.path.exists(os.path.join(location, '.gitmodules')): | ||
| 289 | return | ||
| 290 | self.run_command( | ||
| 291 | ['submodule', 'update', '--init', '--recursive', '-q'], | ||
| 292 | cwd=location, | ||
| 293 | ) | ||
| 294 | |||
| 295 | @classmethod | ||
| 296 | def controls_location(cls, location): | ||
| 297 | if super(Git, cls).controls_location(location): | ||
| 298 | return True | ||
| 299 | try: | ||
| 300 | r = cls().run_command(['rev-parse'], | ||
| 301 | cwd=location, | ||
| 302 | show_stdout=False, | ||
| 303 | on_returncode='ignore') | ||
| 304 | return not r | ||
| 305 | except BadCommand: | ||
| 306 | logger.debug("could not determine if %s is under git control " | ||
| 307 | "because git is not available", location) | ||
| 308 | return False | ||
| 309 | |||
| 310 | |||
| 311 | vcs.register(Git) | ||
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/mercurial.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/mercurial.py new file mode 100644 index 0000000..3936473 --- /dev/null +++ b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/mercurial.py | |||
| @@ -0,0 +1,105 @@ | |||
| 1 | from __future__ import absolute_import | ||
| 2 | |||
| 3 | import logging | ||
| 4 | import os | ||
| 5 | |||
| 6 | from pip._vendor.six.moves import configparser | ||
| 7 | |||
| 8 | from pip._internal.download import path_to_url | ||
| 9 | from pip._internal.utils.misc import display_path | ||
| 10 | from pip._internal.utils.temp_dir import TempDirectory | ||
| 11 | from pip._internal.vcs import VersionControl, vcs | ||
| 12 | |||
| 13 | logger = logging.getLogger(__name__) | ||
| 14 | |||
| 15 | |||
| 16 | class Mercurial(VersionControl): | ||
| 17 | name = 'hg' | ||
| 18 | dirname = '.hg' | ||
| 19 | repo_name = 'clone' | ||
| 20 | schemes = ('hg', 'hg+http', 'hg+https', 'hg+ssh', 'hg+static-http') | ||
| 21 | |||
| 22 | def get_base_rev_args(self, rev): | ||
| 23 | return [rev] | ||
| 24 | |||
| 25 | def export(self, location): | ||
| 26 | """Export the Hg repository at the url to the destination location""" | ||
| 27 | with TempDirectory(kind="export") as temp_dir: | ||
| 28 | self.unpack(temp_dir.path) | ||
| 29 | |||
| 30 | self.run_command( | ||
| 31 | ['archive', location], show_stdout=False, cwd=temp_dir.path | ||
| 32 | ) | ||
| 33 | |||
| 34 | def switch(self, dest, url, rev_options): | ||
| 35 | repo_config = os.path.join(dest, self.dirname, 'hgrc') | ||
| 36 | config = configparser.SafeConfigParser() | ||
| 37 | try: | ||
| 38 | config.read(repo_config) | ||
| 39 | config.set('paths', 'default', url) | ||
| 40 | with open(repo_config, 'w') as config_file: | ||
| 41 | config.write(config_file) | ||
| 42 | except (OSError, configparser.NoSectionError) as exc: | ||
| 43 | logger.warning( | ||
| 44 | 'Could not switch Mercurial repository to %s: %s', url, exc, | ||
| 45 | ) | ||
| 46 | else: | ||
| 47 | cmd_args = ['update', '-q'] + rev_options.to_args() | ||
| 48 | self.run_command(cmd_args, cwd=dest) | ||
| 49 | |||
| 50 | def update(self, dest, rev_options): | ||
| 51 | self.run_command(['pull', '-q'], cwd=dest) | ||
| 52 | cmd_args = ['update', '-q'] + rev_options.to_args() | ||
| 53 | self.run_command(cmd_args, cwd=dest) | ||
| 54 | |||
| 55 | def obtain(self, dest): | ||
| 56 | url, rev = self.get_url_rev() | ||
| 57 | rev_options = self.make_rev_options(rev) | ||
| 58 | if self.check_destination(dest, url, rev_options): | ||
| 59 | rev_display = rev_options.to_display() | ||
| 60 | logger.info( | ||
| 61 | 'Cloning hg %s%s to %s', | ||
| 62 | url, | ||
| 63 | rev_display, | ||
| 64 | display_path(dest), | ||
| 65 | ) | ||
| 66 | self.run_command(['clone', '--noupdate', '-q', url, dest]) | ||
| 67 | cmd_args = ['update', '-q'] + rev_options.to_args() | ||
| 68 | self.run_command(cmd_args, cwd=dest) | ||
| 69 | |||
| 70 | def get_url(self, location): | ||
| 71 | url = self.run_command( | ||
| 72 | ['showconfig', 'paths.default'], | ||
| 73 | show_stdout=False, cwd=location).strip() | ||
| 74 | if self._is_local_repository(url): | ||
| 75 | url = path_to_url(url) | ||
| 76 | return url.strip() | ||
| 77 | |||
| 78 | def get_revision(self, location): | ||
| 79 | current_revision = self.run_command( | ||
| 80 | ['parents', '--template={rev}'], | ||
| 81 | show_stdout=False, cwd=location).strip() | ||
| 82 | return current_revision | ||
| 83 | |||
| 84 | def get_revision_hash(self, location): | ||
| 85 | current_rev_hash = self.run_command( | ||
| 86 | ['parents', '--template={node}'], | ||
| 87 | show_stdout=False, cwd=location).strip() | ||
| 88 | return current_rev_hash | ||
| 89 | |||
| 90 | def get_src_requirement(self, dist, location): | ||
| 91 | repo = self.get_url(location) | ||
| 92 | if not repo.lower().startswith('hg:'): | ||
| 93 | repo = 'hg+' + repo | ||
| 94 | egg_project_name = dist.egg_name().split('-', 1)[0] | ||
| 95 | if not repo: | ||
| 96 | return None | ||
| 97 | current_rev_hash = self.get_revision_hash(location) | ||
| 98 | return '%s@%s#egg=%s' % (repo, current_rev_hash, egg_project_name) | ||
| 99 | |||
| 100 | def is_commit_id_equal(self, dest, name): | ||
| 101 | """Always assume the versions don't match""" | ||
| 102 | return False | ||
| 103 | |||
| 104 | |||
| 105 | vcs.register(Mercurial) | ||
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/subversion.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/subversion.py new file mode 100644 index 0000000..95e5440 --- /dev/null +++ b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/subversion.py | |||
| @@ -0,0 +1,271 @@ | |||
| 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._internal.index import Link | ||
| 10 | from pip._internal.utils.logging import indent_log | ||
| 11 | from pip._internal.utils.misc import display_path, rmtree | ||
| 12 | from pip._internal.vcs import VersionControl, vcs | ||
| 13 | |||
| 14 | _svn_xml_url_re = re.compile('url="([^"]+)"') | ||
| 15 | _svn_rev_re = re.compile(r'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_base_rev_args(self, rev): | ||
| 32 | return ['-r', rev] | ||
| 33 | |||
| 34 | def get_info(self, location): | ||
| 35 | """Returns (url, revision), where both are strings""" | ||
| 36 | assert not location.rstrip('/').endswith(self.dirname), \ | ||
| 37 | 'Bad directory: %s' % location | ||
| 38 | output = self.run_command( | ||
| 39 | ['info', location], | ||
| 40 | show_stdout=False, | ||
| 41 | extra_environ={'LANG': 'C'}, | ||
| 42 | ) | ||
| 43 | match = _svn_url_re.search(output) | ||
| 44 | if not match: | ||
| 45 | logger.warning( | ||
| 46 | 'Cannot determine URL of svn checkout %s', | ||
| 47 | display_path(location), | ||
| 48 | ) | ||
| 49 | logger.debug('Output that cannot be parsed: \n%s', output) | ||
| 50 | return None, None | ||
| 51 | url = match.group(1).strip() | ||
| 52 | match = _svn_revision_re.search(output) | ||
| 53 | if not match: | ||
| 54 | logger.warning( | ||
| 55 | 'Cannot determine revision of svn checkout %s', | ||
| 56 | display_path(location), | ||
| 57 | ) | ||
| 58 | logger.debug('Output that cannot be parsed: \n%s', output) | ||
| 59 | return url, None | ||
| 60 | return url, match.group(1) | ||
| 61 | |||
| 62 | def export(self, location): | ||
| 63 | """Export the svn repository at the url to the destination location""" | ||
| 64 | url, rev = self.get_url_rev() | ||
| 65 | rev_options = get_rev_options(self, url, rev) | ||
| 66 | url = self.remove_auth_from_url(url) | ||
| 67 | logger.info('Exporting svn repository %s to %s', url, location) | ||
| 68 | with indent_log(): | ||
| 69 | if os.path.exists(location): | ||
| 70 | # Subversion doesn't like to check out over an existing | ||
| 71 | # directory --force fixes this, but was only added in svn 1.5 | ||
| 72 | rmtree(location) | ||
| 73 | cmd_args = ['export'] + rev_options.to_args() + [url, location] | ||
| 74 | self.run_command(cmd_args, show_stdout=False) | ||
| 75 | |||
| 76 | def switch(self, dest, url, rev_options): | ||
| 77 | cmd_args = ['switch'] + rev_options.to_args() + [url, dest] | ||
| 78 | self.run_command(cmd_args) | ||
| 79 | |||
| 80 | def update(self, dest, rev_options): | ||
| 81 | cmd_args = ['update'] + rev_options.to_args() + [dest] | ||
| 82 | self.run_command(cmd_args) | ||
| 83 | |||
| 84 | def obtain(self, dest): | ||
| 85 | url, rev = self.get_url_rev() | ||
| 86 | rev_options = get_rev_options(self, url, rev) | ||
| 87 | url = self.remove_auth_from_url(url) | ||
| 88 | if self.check_destination(dest, url, rev_options): | ||
| 89 | rev_display = rev_options.to_display() | ||
| 90 | logger.info( | ||
| 91 | 'Checking out %s%s to %s', | ||
| 92 | url, | ||
| 93 | rev_display, | ||
| 94 | display_path(dest), | ||
| 95 | ) | ||
| 96 | cmd_args = ['checkout', '-q'] + rev_options.to_args() + [url, dest] | ||
| 97 | self.run_command(cmd_args) | ||
| 98 | |||
| 99 | def get_location(self, dist, dependency_links): | ||
| 100 | for url in dependency_links: | ||
| 101 | egg_fragment = Link(url).egg_fragment | ||
| 102 | if not egg_fragment: | ||
| 103 | continue | ||
| 104 | if '-' in egg_fragment: | ||
| 105 | # FIXME: will this work when a package has - in the name? | ||
| 106 | key = '-'.join(egg_fragment.split('-')[:-1]).lower() | ||
| 107 | else: | ||
| 108 | key = egg_fragment | ||
| 109 | if key == dist.key: | ||
| 110 | return url.split('#', 1)[0] | ||
| 111 | return None | ||
| 112 | |||
| 113 | def get_revision(self, location): | ||
| 114 | """ | ||
| 115 | Return the maximum revision for all files under a given location | ||
| 116 | """ | ||
| 117 | # Note: taken from setuptools.command.egg_info | ||
| 118 | revision = 0 | ||
| 119 | |||
| 120 | for base, dirs, files in os.walk(location): | ||
| 121 | if self.dirname not in dirs: | ||
| 122 | dirs[:] = [] | ||
| 123 | continue # no sense walking uncontrolled subdirs | ||
| 124 | dirs.remove(self.dirname) | ||
| 125 | entries_fn = os.path.join(base, self.dirname, 'entries') | ||
| 126 | if not os.path.exists(entries_fn): | ||
| 127 | # FIXME: should we warn? | ||
| 128 | continue | ||
| 129 | |||
| 130 | dirurl, localrev = self._get_svn_url_rev(base) | ||
| 131 | |||
| 132 | if base == location: | ||
| 133 | base = dirurl + '/' # save the root url | ||
| 134 | elif not dirurl or not dirurl.startswith(base): | ||
| 135 | dirs[:] = [] | ||
| 136 | continue # not part of the same svn tree, skip it | ||
| 137 | revision = max(revision, localrev) | ||
| 138 | return revision | ||
| 139 | |||
| 140 | def get_url_rev(self): | ||
| 141 | # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it | ||
| 142 | url, rev = super(Subversion, self).get_url_rev() | ||
| 143 | if url.startswith('ssh://'): | ||
| 144 | url = 'svn+' + url | ||
| 145 | return url, rev | ||
| 146 | |||
| 147 | def get_url(self, location): | ||
| 148 | # In cases where the source is in a subdirectory, not alongside | ||
| 149 | # setup.py we have to look up in the location until we find a real | ||
| 150 | # setup.py | ||
| 151 | orig_location = location | ||
| 152 | while not os.path.exists(os.path.join(location, 'setup.py')): | ||
| 153 | last_location = location | ||
| 154 | location = os.path.dirname(location) | ||
| 155 | if location == last_location: | ||
| 156 | # We've traversed up to the root of the filesystem without | ||
| 157 | # finding setup.py | ||
| 158 | logger.warning( | ||
| 159 | "Could not find setup.py for directory %s (tried all " | ||
| 160 | "parent directories)", | ||
| 161 | orig_location, | ||
| 162 | ) | ||
| 163 | return None | ||
| 164 | |||
| 165 | return self._get_svn_url_rev(location)[0] | ||
| 166 | |||
| 167 | def _get_svn_url_rev(self, location): | ||
| 168 | from pip._internal.exceptions import InstallationError | ||
| 169 | |||
| 170 | entries_path = os.path.join(location, self.dirname, 'entries') | ||
| 171 | if os.path.exists(entries_path): | ||
| 172 | with open(entries_path) as f: | ||
| 173 | data = f.read() | ||
| 174 | else: # subversion >= 1.7 does not have the 'entries' file | ||
| 175 | data = '' | ||
| 176 | |||
| 177 | if (data.startswith('8') or | ||
| 178 | data.startswith('9') or | ||
| 179 | data.startswith('10')): | ||
| 180 | data = list(map(str.splitlines, data.split('\n\x0c\n'))) | ||
| 181 | del data[0][0] # get rid of the '8' | ||
| 182 | url = data[0][3] | ||
| 183 | revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0] | ||
| 184 | elif data.startswith('<?xml'): | ||
| 185 | match = _svn_xml_url_re.search(data) | ||
| 186 | if not match: | ||
| 187 | raise ValueError('Badly formatted data: %r' % data) | ||
| 188 | url = match.group(1) # get repository URL | ||
| 189 | revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)] + [0] | ||
| 190 | else: | ||
| 191 | try: | ||
| 192 | # subversion >= 1.7 | ||
| 193 | xml = self.run_command( | ||
| 194 | ['info', '--xml', location], | ||
| 195 | show_stdout=False, | ||
| 196 | ) | ||
| 197 | url = _svn_info_xml_url_re.search(xml).group(1) | ||
| 198 | revs = [ | ||
| 199 | int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml) | ||
| 200 | ] | ||
| 201 | except InstallationError: | ||
| 202 | url, revs = None, [] | ||
| 203 | |||
| 204 | if revs: | ||
| 205 | rev = max(revs) | ||
| 206 | else: | ||
| 207 | rev = 0 | ||
| 208 | |||
| 209 | return url, rev | ||
| 210 | |||
| 211 | def get_src_requirement(self, dist, location): | ||
| 212 | repo = self.get_url(location) | ||
| 213 | if repo is None: | ||
| 214 | return None | ||
| 215 | # FIXME: why not project name? | ||
| 216 | egg_project_name = dist.egg_name().split('-', 1)[0] | ||
| 217 | rev = self.get_revision(location) | ||
| 218 | return 'svn+%s@%s#egg=%s' % (repo, rev, egg_project_name) | ||
| 219 | |||
| 220 | def is_commit_id_equal(self, dest, name): | ||
| 221 | """Always assume the versions don't match""" | ||
| 222 | return False | ||
| 223 | |||
| 224 | @staticmethod | ||
| 225 | def remove_auth_from_url(url): | ||
| 226 | # Return a copy of url with 'username:password@' removed. | ||
| 227 | # username/pass params are passed to subversion through flags | ||
| 228 | # and are not recognized in the url. | ||
| 229 | |||
| 230 | # parsed url | ||
| 231 | purl = urllib_parse.urlsplit(url) | ||
| 232 | stripped_netloc = \ | ||
| 233 | purl.netloc.split('@')[-1] | ||
| 234 | |||
| 235 | # stripped url | ||
| 236 | url_pieces = ( | ||
| 237 | purl.scheme, stripped_netloc, purl.path, purl.query, purl.fragment | ||
| 238 | ) | ||
| 239 | surl = urllib_parse.urlunsplit(url_pieces) | ||
| 240 | return surl | ||
| 241 | |||
| 242 | |||
| 243 | def get_rev_options(vcs, url, rev): | ||
| 244 | """ | ||
| 245 | Return a RevOptions object. | ||
| 246 | """ | ||
| 247 | r = urllib_parse.urlsplit(url) | ||
| 248 | if hasattr(r, 'username'): | ||
| 249 | # >= Python-2.5 | ||
| 250 | username, password = r.username, r.password | ||
| 251 | else: | ||
| 252 | netloc = r[1] | ||
| 253 | if '@' in netloc: | ||
| 254 | auth = netloc.split('@')[0] | ||
| 255 | if ':' in auth: | ||
| 256 | username, password = auth.split(':', 1) | ||
| 257 | else: | ||
| 258 | username, password = auth, None | ||
| 259 | else: | ||
| 260 | username, password = None, None | ||
| 261 | |||
| 262 | extra_args = [] | ||
| 263 | if username: | ||
| 264 | extra_args += ['--username', username] | ||
| 265 | if password: | ||
| 266 | extra_args += ['--password', password] | ||
| 267 | |||
| 268 | return vcs.make_rev_options(rev, extra_args=extra_args) | ||
| 269 | |||
| 270 | |||
| 271 | vcs.register(Subversion) | ||
