summaryrefslogtreecommitdiff
path: root/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/__init__.py')
-rw-r--r--venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/__init__.py471
1 files changed, 0 insertions, 471 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
deleted file mode 100644
index bff94fa..0000000
--- a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/vcs/__init__.py
+++ /dev/null
@@ -1,471 +0,0 @@
1"""Handles all VCS (version control) support"""
2from __future__ import absolute_import
3
4import copy
5import errno
6import logging
7import os
8import shutil
9import sys
10
11from pip._vendor.six.moves.urllib import parse as urllib_parse
12
13from pip._internal.exceptions import BadCommand
14from pip._internal.utils.misc import (
15 display_path, backup_dir, call_subprocess, rmtree, ask_path_exists,
16)
17from pip._internal.utils.typing import MYPY_CHECK_RUNNING
18
19if 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
26logger = logging.getLogger(__name__)
27
28
29class 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
90class 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
161vcs = VcsSupport()
162
163
164class 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
452def 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()