diff options
Diffstat (limited to 'venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/utils/misc.py')
-rw-r--r-- | venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/utils/misc.py | 851 |
1 files changed, 0 insertions, 851 deletions
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/utils/misc.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/utils/misc.py deleted file mode 100644 index db84a7c..0000000 --- a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_internal/utils/misc.py +++ /dev/null | |||
@@ -1,851 +0,0 @@ | |||
1 | from __future__ import absolute_import | ||
2 | |||
3 | import contextlib | ||
4 | import errno | ||
5 | import io | ||
6 | import locale | ||
7 | # we have a submodule named 'logging' which would shadow this if we used the | ||
8 | # regular name: | ||
9 | import logging as std_logging | ||
10 | import os | ||
11 | import posixpath | ||
12 | import re | ||
13 | import shutil | ||
14 | import stat | ||
15 | import subprocess | ||
16 | import sys | ||
17 | import tarfile | ||
18 | import zipfile | ||
19 | from collections import deque | ||
20 | |||
21 | from pip._vendor import pkg_resources | ||
22 | # NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is | ||
23 | # why we ignore the type on this import. | ||
24 | from pip._vendor.retrying import retry # type: ignore | ||
25 | from pip._vendor.six import PY2 | ||
26 | from pip._vendor.six.moves import input | ||
27 | |||
28 | from pip._internal.compat import console_to_str, expanduser, stdlib_pkgs | ||
29 | from pip._internal.exceptions import InstallationError | ||
30 | from pip._internal.locations import ( | ||
31 | running_under_virtualenv, site_packages, user_site, virtualenv_no_global, | ||
32 | write_delete_marker_file, | ||
33 | ) | ||
34 | |||
35 | if PY2: | ||
36 | from io import BytesIO as StringIO | ||
37 | else: | ||
38 | from io import StringIO | ||
39 | |||
40 | __all__ = ['rmtree', 'display_path', 'backup_dir', | ||
41 | 'ask', 'splitext', | ||
42 | 'format_size', 'is_installable_dir', | ||
43 | 'is_svn_page', 'file_contents', | ||
44 | 'split_leading_dir', 'has_leading_dir', | ||
45 | 'normalize_path', | ||
46 | 'renames', 'get_prog', | ||
47 | 'unzip_file', 'untar_file', 'unpack_file', 'call_subprocess', | ||
48 | 'captured_stdout', 'ensure_dir', | ||
49 | 'ARCHIVE_EXTENSIONS', 'SUPPORTED_EXTENSIONS', | ||
50 | 'get_installed_version'] | ||
51 | |||
52 | |||
53 | logger = std_logging.getLogger(__name__) | ||
54 | |||
55 | BZ2_EXTENSIONS = ('.tar.bz2', '.tbz') | ||
56 | XZ_EXTENSIONS = ('.tar.xz', '.txz', '.tlz', '.tar.lz', '.tar.lzma') | ||
57 | ZIP_EXTENSIONS = ('.zip', '.whl') | ||
58 | TAR_EXTENSIONS = ('.tar.gz', '.tgz', '.tar') | ||
59 | ARCHIVE_EXTENSIONS = ( | ||
60 | ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS) | ||
61 | SUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS | ||
62 | try: | ||
63 | import bz2 # noqa | ||
64 | SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS | ||
65 | except ImportError: | ||
66 | logger.debug('bz2 module is not available') | ||
67 | |||
68 | try: | ||
69 | # Only for Python 3.3+ | ||
70 | import lzma # noqa | ||
71 | SUPPORTED_EXTENSIONS += XZ_EXTENSIONS | ||
72 | except ImportError: | ||
73 | logger.debug('lzma module is not available') | ||
74 | |||
75 | |||
76 | def import_or_raise(pkg_or_module_string, ExceptionType, *args, **kwargs): | ||
77 | try: | ||
78 | return __import__(pkg_or_module_string) | ||
79 | except ImportError: | ||
80 | raise ExceptionType(*args, **kwargs) | ||
81 | |||
82 | |||
83 | def ensure_dir(path): | ||
84 | """os.path.makedirs without EEXIST.""" | ||
85 | try: | ||
86 | os.makedirs(path) | ||
87 | except OSError as e: | ||
88 | if e.errno != errno.EEXIST: | ||
89 | raise | ||
90 | |||
91 | |||
92 | def get_prog(): | ||
93 | try: | ||
94 | prog = os.path.basename(sys.argv[0]) | ||
95 | if prog in ('__main__.py', '-c'): | ||
96 | return "%s -m pip" % sys.executable | ||
97 | else: | ||
98 | return prog | ||
99 | except (AttributeError, TypeError, IndexError): | ||
100 | pass | ||
101 | return 'pip' | ||
102 | |||
103 | |||
104 | # Retry every half second for up to 3 seconds | ||
105 | @retry(stop_max_delay=3000, wait_fixed=500) | ||
106 | def rmtree(dir, ignore_errors=False): | ||
107 | shutil.rmtree(dir, ignore_errors=ignore_errors, | ||
108 | onerror=rmtree_errorhandler) | ||
109 | |||
110 | |||
111 | def rmtree_errorhandler(func, path, exc_info): | ||
112 | """On Windows, the files in .svn are read-only, so when rmtree() tries to | ||
113 | remove them, an exception is thrown. We catch that here, remove the | ||
114 | read-only attribute, and hopefully continue without problems.""" | ||
115 | # if file type currently read only | ||
116 | if os.stat(path).st_mode & stat.S_IREAD: | ||
117 | # convert to read/write | ||
118 | os.chmod(path, stat.S_IWRITE) | ||
119 | # use the original function to repeat the operation | ||
120 | func(path) | ||
121 | return | ||
122 | else: | ||
123 | raise | ||
124 | |||
125 | |||
126 | def display_path(path): | ||
127 | """Gives the display value for a given path, making it relative to cwd | ||
128 | if possible.""" | ||
129 | path = os.path.normcase(os.path.abspath(path)) | ||
130 | if sys.version_info[0] == 2: | ||
131 | path = path.decode(sys.getfilesystemencoding(), 'replace') | ||
132 | path = path.encode(sys.getdefaultencoding(), 'replace') | ||
133 | if path.startswith(os.getcwd() + os.path.sep): | ||
134 | path = '.' + path[len(os.getcwd()):] | ||
135 | return path | ||
136 | |||
137 | |||
138 | def backup_dir(dir, ext='.bak'): | ||
139 | """Figure out the name of a directory to back up the given dir to | ||
140 | (adding .bak, .bak2, etc)""" | ||
141 | n = 1 | ||
142 | extension = ext | ||
143 | while os.path.exists(dir + extension): | ||
144 | n += 1 | ||
145 | extension = ext + str(n) | ||
146 | return dir + extension | ||
147 | |||
148 | |||
149 | def ask_path_exists(message, options): | ||
150 | for action in os.environ.get('PIP_EXISTS_ACTION', '').split(): | ||
151 | if action in options: | ||
152 | return action | ||
153 | return ask(message, options) | ||
154 | |||
155 | |||
156 | def ask(message, options): | ||
157 | """Ask the message interactively, with the given possible responses""" | ||
158 | while 1: | ||
159 | if os.environ.get('PIP_NO_INPUT'): | ||
160 | raise Exception( | ||
161 | 'No input was expected ($PIP_NO_INPUT set); question: %s' % | ||
162 | message | ||
163 | ) | ||
164 | response = input(message) | ||
165 | response = response.strip().lower() | ||
166 | if response not in options: | ||
167 | print( | ||
168 | 'Your response (%r) was not one of the expected responses: ' | ||
169 | '%s' % (response, ', '.join(options)) | ||
170 | ) | ||
171 | else: | ||
172 | return response | ||
173 | |||
174 | |||
175 | def format_size(bytes): | ||
176 | if bytes > 1000 * 1000: | ||
177 | return '%.1fMB' % (bytes / 1000.0 / 1000) | ||
178 | elif bytes > 10 * 1000: | ||
179 | return '%ikB' % (bytes / 1000) | ||
180 | elif bytes > 1000: | ||
181 | return '%.1fkB' % (bytes / 1000.0) | ||
182 | else: | ||
183 | return '%ibytes' % bytes | ||
184 | |||
185 | |||
186 | def is_installable_dir(path): | ||
187 | """Return True if `path` is a directory containing a setup.py file.""" | ||
188 | if not os.path.isdir(path): | ||
189 | return False | ||
190 | setup_py = os.path.join(path, 'setup.py') | ||
191 | if os.path.isfile(setup_py): | ||
192 | return True | ||
193 | return False | ||
194 | |||
195 | |||
196 | def is_svn_page(html): | ||
197 | """ | ||
198 | Returns true if the page appears to be the index page of an svn repository | ||
199 | """ | ||
200 | return (re.search(r'<title>[^<]*Revision \d+:', html) and | ||
201 | re.search(r'Powered by (?:<a[^>]*?>)?Subversion', html, re.I)) | ||
202 | |||
203 | |||
204 | def file_contents(filename): | ||
205 | with open(filename, 'rb') as fp: | ||
206 | return fp.read().decode('utf-8') | ||
207 | |||
208 | |||
209 | def read_chunks(file, size=io.DEFAULT_BUFFER_SIZE): | ||
210 | """Yield pieces of data from a file-like object until EOF.""" | ||
211 | while True: | ||
212 | chunk = file.read(size) | ||
213 | if not chunk: | ||
214 | break | ||
215 | yield chunk | ||
216 | |||
217 | |||
218 | def split_leading_dir(path): | ||
219 | path = path.lstrip('/').lstrip('\\') | ||
220 | if '/' in path and (('\\' in path and path.find('/') < path.find('\\')) or | ||
221 | '\\' not in path): | ||
222 | return path.split('/', 1) | ||
223 | elif '\\' in path: | ||
224 | return path.split('\\', 1) | ||
225 | else: | ||
226 | return path, '' | ||
227 | |||
228 | |||
229 | def has_leading_dir(paths): | ||
230 | """Returns true if all the paths have the same leading path name | ||
231 | (i.e., everything is in one subdirectory in an archive)""" | ||
232 | common_prefix = None | ||
233 | for path in paths: | ||
234 | prefix, rest = split_leading_dir(path) | ||
235 | if not prefix: | ||
236 | return False | ||
237 | elif common_prefix is None: | ||
238 | common_prefix = prefix | ||
239 | elif prefix != common_prefix: | ||
240 | return False | ||
241 | return True | ||
242 | |||
243 | |||
244 | def normalize_path(path, resolve_symlinks=True): | ||
245 | """ | ||
246 | Convert a path to its canonical, case-normalized, absolute version. | ||
247 | |||
248 | """ | ||
249 | path = expanduser(path) | ||
250 | if resolve_symlinks: | ||
251 | path = os.path.realpath(path) | ||
252 | else: | ||
253 | path = os.path.abspath(path) | ||
254 | return os.path.normcase(path) | ||
255 | |||
256 | |||
257 | def splitext(path): | ||
258 | """Like os.path.splitext, but take off .tar too""" | ||
259 | base, ext = posixpath.splitext(path) | ||
260 | if base.lower().endswith('.tar'): | ||
261 | ext = base[-4:] + ext | ||
262 | base = base[:-4] | ||
263 | return base, ext | ||
264 | |||
265 | |||
266 | def renames(old, new): | ||
267 | """Like os.renames(), but handles renaming across devices.""" | ||
268 | # Implementation borrowed from os.renames(). | ||
269 | head, tail = os.path.split(new) | ||
270 | if head and tail and not os.path.exists(head): | ||
271 | os.makedirs(head) | ||
272 | |||
273 | shutil.move(old, new) | ||
274 | |||
275 | head, tail = os.path.split(old) | ||
276 | if head and tail: | ||
277 | try: | ||
278 | os.removedirs(head) | ||
279 | except OSError: | ||
280 | pass | ||
281 | |||
282 | |||
283 | def is_local(path): | ||
284 | """ | ||
285 | Return True if path is within sys.prefix, if we're running in a virtualenv. | ||
286 | |||
287 | If we're not in a virtualenv, all paths are considered "local." | ||
288 | |||
289 | """ | ||
290 | if not running_under_virtualenv(): | ||
291 | return True | ||
292 | return normalize_path(path).startswith(normalize_path(sys.prefix)) | ||
293 | |||
294 | |||
295 | def dist_is_local(dist): | ||
296 | """ | ||
297 | Return True if given Distribution object is installed locally | ||
298 | (i.e. within current virtualenv). | ||
299 | |||
300 | Always True if we're not in a virtualenv. | ||
301 | |||
302 | """ | ||
303 | return is_local(dist_location(dist)) | ||
304 | |||
305 | |||
306 | def dist_in_usersite(dist): | ||
307 | """ | ||
308 | Return True if given Distribution is installed in user site. | ||
309 | """ | ||
310 | norm_path = normalize_path(dist_location(dist)) | ||
311 | return norm_path.startswith(normalize_path(user_site)) | ||
312 | |||
313 | |||
314 | def dist_in_site_packages(dist): | ||
315 | """ | ||
316 | Return True if given Distribution is installed in | ||
317 | sysconfig.get_python_lib(). | ||
318 | """ | ||
319 | return normalize_path( | ||
320 | dist_location(dist) | ||
321 | ).startswith(normalize_path(site_packages)) | ||
322 | |||
323 | |||
324 | def dist_is_editable(dist): | ||
325 | """Is distribution an editable install?""" | ||
326 | for path_item in sys.path: | ||
327 | egg_link = os.path.join(path_item, dist.project_name + '.egg-link') | ||
328 | if os.path.isfile(egg_link): | ||
329 | return True | ||
330 | return False | ||
331 | |||
332 | |||
333 | def get_installed_distributions(local_only=True, | ||
334 | skip=stdlib_pkgs, | ||
335 | include_editables=True, | ||
336 | editables_only=False, | ||
337 | user_only=False): | ||
338 | """ | ||
339 | Return a list of installed Distribution objects. | ||
340 | |||
341 | If ``local_only`` is True (default), only return installations | ||
342 | local to the current virtualenv, if in a virtualenv. | ||
343 | |||
344 | ``skip`` argument is an iterable of lower-case project names to | ||
345 | ignore; defaults to stdlib_pkgs | ||
346 | |||
347 | If ``include_editables`` is False, don't report editables. | ||
348 | |||
349 | If ``editables_only`` is True , only report editables. | ||
350 | |||
351 | If ``user_only`` is True , only report installations in the user | ||
352 | site directory. | ||
353 | |||
354 | """ | ||
355 | if local_only: | ||
356 | local_test = dist_is_local | ||
357 | else: | ||
358 | def local_test(d): | ||
359 | return True | ||
360 | |||
361 | if include_editables: | ||
362 | def editable_test(d): | ||
363 | return True | ||
364 | else: | ||
365 | def editable_test(d): | ||
366 | return not dist_is_editable(d) | ||
367 | |||
368 | if editables_only: | ||
369 | def editables_only_test(d): | ||
370 | return dist_is_editable(d) | ||
371 | else: | ||
372 | def editables_only_test(d): | ||
373 | return True | ||
374 | |||
375 | if user_only: | ||
376 | user_test = dist_in_usersite | ||
377 | else: | ||
378 | def user_test(d): | ||
379 | return True | ||
380 | |||
381 | return [d for d in pkg_resources.working_set | ||
382 | if local_test(d) and | ||
383 | d.key not in skip and | ||
384 | editable_test(d) and | ||
385 | editables_only_test(d) and | ||
386 | user_test(d) | ||
387 | ] | ||
388 | |||
389 | |||
390 | def egg_link_path(dist): | ||
391 | """ | ||
392 | Return the path for the .egg-link file if it exists, otherwise, None. | ||
393 | |||
394 | There's 3 scenarios: | ||
395 | 1) not in a virtualenv | ||
396 | try to find in site.USER_SITE, then site_packages | ||
397 | 2) in a no-global virtualenv | ||
398 | try to find in site_packages | ||
399 | 3) in a yes-global virtualenv | ||
400 | try to find in site_packages, then site.USER_SITE | ||
401 | (don't look in global location) | ||
402 | |||
403 | For #1 and #3, there could be odd cases, where there's an egg-link in 2 | ||
404 | locations. | ||
405 | |||
406 | This method will just return the first one found. | ||
407 | """ | ||
408 | sites = [] | ||
409 | if running_under_virtualenv(): | ||
410 | if virtualenv_no_global(): | ||
411 | sites.append(site_packages) | ||
412 | else: | ||
413 | sites.append(site_packages) | ||
414 | if user_site: | ||
415 | sites.append(user_site) | ||
416 | else: | ||
417 | if user_site: | ||
418 | sites.append(user_site) | ||
419 | sites.append(site_packages) | ||
420 | |||
421 | for site in sites: | ||
422 | egglink = os.path.join(site, dist.project_name) + '.egg-link' | ||
423 | if os.path.isfile(egglink): | ||
424 | return egglink | ||
425 | |||
426 | |||
427 | def dist_location(dist): | ||
428 | """ | ||
429 | Get the site-packages location of this distribution. Generally | ||
430 | this is dist.location, except in the case of develop-installed | ||
431 | packages, where dist.location is the source code location, and we | ||
432 | want to know where the egg-link file is. | ||
433 | |||
434 | """ | ||
435 | egg_link = egg_link_path(dist) | ||
436 | if egg_link: | ||
437 | return egg_link | ||
438 | return dist.location | ||
439 | |||
440 | |||
441 | def current_umask(): | ||
442 | """Get the current umask which involves having to set it temporarily.""" | ||
443 | mask = os.umask(0) | ||
444 | os.umask(mask) | ||
445 | return mask | ||
446 | |||
447 | |||
448 | def unzip_file(filename, location, flatten=True): | ||
449 | """ | ||
450 | Unzip the file (with path `filename`) to the destination `location`. All | ||
451 | files are written based on system defaults and umask (i.e. permissions are | ||
452 | not preserved), except that regular file members with any execute | ||
453 | permissions (user, group, or world) have "chmod +x" applied after being | ||
454 | written. Note that for windows, any execute changes using os.chmod are | ||
455 | no-ops per the python docs. | ||
456 | """ | ||
457 | ensure_dir(location) | ||
458 | zipfp = open(filename, 'rb') | ||
459 | try: | ||
460 | zip = zipfile.ZipFile(zipfp, allowZip64=True) | ||
461 | leading = has_leading_dir(zip.namelist()) and flatten | ||
462 | for info in zip.infolist(): | ||
463 | name = info.filename | ||
464 | data = zip.read(name) | ||
465 | fn = name | ||
466 | if leading: | ||
467 | fn = split_leading_dir(name)[1] | ||
468 | fn = os.path.join(location, fn) | ||
469 | dir = os.path.dirname(fn) | ||
470 | if fn.endswith('/') or fn.endswith('\\'): | ||
471 | # A directory | ||
472 | ensure_dir(fn) | ||
473 | else: | ||
474 | ensure_dir(dir) | ||
475 | fp = open(fn, 'wb') | ||
476 | try: | ||
477 | fp.write(data) | ||
478 | finally: | ||
479 | fp.close() | ||
480 | mode = info.external_attr >> 16 | ||
481 | # if mode and regular file and any execute permissions for | ||
482 | # user/group/world? | ||
483 | if mode and stat.S_ISREG(mode) and mode & 0o111: | ||
484 | # make dest file have execute for user/group/world | ||
485 | # (chmod +x) no-op on windows per python docs | ||
486 | os.chmod(fn, (0o777 - current_umask() | 0o111)) | ||
487 | finally: | ||
488 | zipfp.close() | ||
489 | |||
490 | |||
491 | def untar_file(filename, location): | ||
492 | """ | ||
493 | Untar the file (with path `filename`) to the destination `location`. | ||
494 | All files are written based on system defaults and umask (i.e. permissions | ||
495 | are not preserved), except that regular file members with any execute | ||
496 | permissions (user, group, or world) have "chmod +x" applied after being | ||
497 | written. Note that for windows, any execute changes using os.chmod are | ||
498 | no-ops per the python docs. | ||
499 | """ | ||
500 | ensure_dir(location) | ||
501 | if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): | ||
502 | mode = 'r:gz' | ||
503 | elif filename.lower().endswith(BZ2_EXTENSIONS): | ||
504 | mode = 'r:bz2' | ||
505 | elif filename.lower().endswith(XZ_EXTENSIONS): | ||
506 | mode = 'r:xz' | ||
507 | elif filename.lower().endswith('.tar'): | ||
508 | mode = 'r' | ||
509 | else: | ||
510 | logger.warning( | ||
511 | 'Cannot determine compression type for file %s', filename, | ||
512 | ) | ||
513 | mode = 'r:*' | ||
514 | tar = tarfile.open(filename, mode) | ||
515 | try: | ||
516 | # note: python<=2.5 doesn't seem to know about pax headers, filter them | ||
517 | leading = has_leading_dir([ | ||
518 | member.name for member in tar.getmembers() | ||
519 | if member.name != 'pax_global_header' | ||
520 | ]) | ||
521 | for member in tar.getmembers(): | ||
522 | fn = member.name | ||
523 | if fn == 'pax_global_header': | ||
524 | continue | ||
525 | if leading: | ||
526 | fn = split_leading_dir(fn)[1] | ||
527 | path = os.path.join(location, fn) | ||
528 | if member.isdir(): | ||
529 | ensure_dir(path) | ||
530 | elif member.issym(): | ||
531 | try: | ||
532 | tar._extract_member(member, path) | ||
533 | except Exception as exc: | ||
534 | # Some corrupt tar files seem to produce this | ||
535 | # (specifically bad symlinks) | ||
536 | logger.warning( | ||
537 | 'In the tar file %s the member %s is invalid: %s', | ||
538 | filename, member.name, exc, | ||
539 | ) | ||
540 | continue | ||
541 | else: | ||
542 | try: | ||
543 | fp = tar.extractfile(member) | ||
544 | except (KeyError, AttributeError) as exc: | ||
545 | # Some corrupt tar files seem to produce this | ||
546 | # (specifically bad symlinks) | ||
547 | logger.warning( | ||
548 | 'In the tar file %s the member %s is invalid: %s', | ||
549 | filename, member.name, exc, | ||
550 | ) | ||
551 | continue | ||
552 | ensure_dir(os.path.dirname(path)) | ||
553 | with open(path, 'wb') as destfp: | ||
554 | shutil.copyfileobj(fp, destfp) | ||
555 | fp.close() | ||
556 | # Update the timestamp (useful for cython compiled files) | ||
557 | tar.utime(member, path) | ||
558 | # member have any execute permissions for user/group/world? | ||
559 | if member.mode & 0o111: | ||
560 | # make dest file have execute for user/group/world | ||
561 | # no-op on windows per python docs | ||
562 | os.chmod(path, (0o777 - current_umask() | 0o111)) | ||
563 | finally: | ||
564 | tar.close() | ||
565 | |||
566 | |||
567 | def unpack_file(filename, location, content_type, link): | ||
568 | filename = os.path.realpath(filename) | ||
569 | if (content_type == 'application/zip' or | ||
570 | filename.lower().endswith(ZIP_EXTENSIONS) or | ||
571 | zipfile.is_zipfile(filename)): | ||
572 | unzip_file( | ||
573 | filename, | ||
574 | location, | ||
575 | flatten=not filename.endswith('.whl') | ||
576 | ) | ||
577 | elif (content_type == 'application/x-gzip' or | ||
578 | tarfile.is_tarfile(filename) or | ||
579 | filename.lower().endswith( | ||
580 | TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS)): | ||
581 | untar_file(filename, location) | ||
582 | elif (content_type and content_type.startswith('text/html') and | ||
583 | is_svn_page(file_contents(filename))): | ||
584 | # We don't really care about this | ||
585 | from pip._internal.vcs.subversion import Subversion | ||
586 | Subversion('svn+' + link.url).unpack(location) | ||
587 | else: | ||
588 | # FIXME: handle? | ||
589 | # FIXME: magic signatures? | ||
590 | logger.critical( | ||
591 | 'Cannot unpack file %s (downloaded from %s, content-type: %s); ' | ||
592 | 'cannot detect archive format', | ||
593 | filename, location, content_type, | ||
594 | ) | ||
595 | raise InstallationError( | ||
596 | 'Cannot determine archive format of %s' % location | ||
597 | ) | ||
598 | |||
599 | |||
600 | def call_subprocess(cmd, show_stdout=True, cwd=None, | ||
601 | on_returncode='raise', | ||
602 | command_desc=None, | ||
603 | extra_environ=None, unset_environ=None, spinner=None): | ||
604 | """ | ||
605 | Args: | ||
606 | unset_environ: an iterable of environment variable names to unset | ||
607 | prior to calling subprocess.Popen(). | ||
608 | """ | ||
609 | if unset_environ is None: | ||
610 | unset_environ = [] | ||
611 | # This function's handling of subprocess output is confusing and I | ||
612 | # previously broke it terribly, so as penance I will write a long comment | ||
613 | # explaining things. | ||
614 | # | ||
615 | # The obvious thing that affects output is the show_stdout= | ||
616 | # kwarg. show_stdout=True means, let the subprocess write directly to our | ||
617 | # stdout. Even though it is nominally the default, it is almost never used | ||
618 | # inside pip (and should not be used in new code without a very good | ||
619 | # reason); as of 2016-02-22 it is only used in a few places inside the VCS | ||
620 | # wrapper code. Ideally we should get rid of it entirely, because it | ||
621 | # creates a lot of complexity here for a rarely used feature. | ||
622 | # | ||
623 | # Most places in pip set show_stdout=False. What this means is: | ||
624 | # - We connect the child stdout to a pipe, which we read. | ||
625 | # - By default, we hide the output but show a spinner -- unless the | ||
626 | # subprocess exits with an error, in which case we show the output. | ||
627 | # - If the --verbose option was passed (= loglevel is DEBUG), then we show | ||
628 | # the output unconditionally. (But in this case we don't want to show | ||
629 | # the output a second time if it turns out that there was an error.) | ||
630 | # | ||
631 | # stderr is always merged with stdout (even if show_stdout=True). | ||
632 | if show_stdout: | ||
633 | stdout = None | ||
634 | else: | ||
635 | stdout = subprocess.PIPE | ||
636 | if command_desc is None: | ||
637 | cmd_parts = [] | ||
638 | for part in cmd: | ||
639 | if ' ' in part or '\n' in part or '"' in part or "'" in part: | ||
640 | part = '"%s"' % part.replace('"', '\\"') | ||
641 | cmd_parts.append(part) | ||
642 | command_desc = ' '.join(cmd_parts) | ||
643 | logger.debug("Running command %s", command_desc) | ||
644 | env = os.environ.copy() | ||
645 | if extra_environ: | ||
646 | env.update(extra_environ) | ||
647 | for name in unset_environ: | ||
648 | env.pop(name, None) | ||
649 | try: | ||
650 | proc = subprocess.Popen( | ||
651 | cmd, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, | ||
652 | stdout=stdout, cwd=cwd, env=env, | ||
653 | ) | ||
654 | proc.stdin.close() | ||
655 | except Exception as exc: | ||
656 | logger.critical( | ||
657 | "Error %s while executing command %s", exc, command_desc, | ||
658 | ) | ||
659 | raise | ||
660 | all_output = [] | ||
661 | if stdout is not None: | ||
662 | while True: | ||
663 | line = console_to_str(proc.stdout.readline()) | ||
664 | if not line: | ||
665 | break | ||
666 | line = line.rstrip() | ||
667 | all_output.append(line + '\n') | ||
668 | if logger.getEffectiveLevel() <= std_logging.DEBUG: | ||
669 | # Show the line immediately | ||
670 | logger.debug(line) | ||
671 | else: | ||
672 | # Update the spinner | ||
673 | if spinner is not None: | ||
674 | spinner.spin() | ||
675 | try: | ||
676 | proc.wait() | ||
677 | finally: | ||
678 | if proc.stdout: | ||
679 | proc.stdout.close() | ||
680 | if spinner is not None: | ||
681 | if proc.returncode: | ||
682 | spinner.finish("error") | ||
683 | else: | ||
684 | spinner.finish("done") | ||
685 | if proc.returncode: | ||
686 | if on_returncode == 'raise': | ||
687 | if (logger.getEffectiveLevel() > std_logging.DEBUG and | ||
688 | not show_stdout): | ||
689 | logger.info( | ||
690 | 'Complete output from command %s:', command_desc, | ||
691 | ) | ||
692 | logger.info( | ||
693 | ''.join(all_output) + | ||
694 | '\n----------------------------------------' | ||
695 | ) | ||
696 | raise InstallationError( | ||
697 | 'Command "%s" failed with error code %s in %s' | ||
698 | % (command_desc, proc.returncode, cwd)) | ||
699 | elif on_returncode == 'warn': | ||
700 | logger.warning( | ||
701 | 'Command "%s" had error code %s in %s', | ||
702 | command_desc, proc.returncode, cwd, | ||
703 | ) | ||
704 | elif on_returncode == 'ignore': | ||
705 | pass | ||
706 | else: | ||
707 | raise ValueError('Invalid value: on_returncode=%s' % | ||
708 | repr(on_returncode)) | ||
709 | if not show_stdout: | ||
710 | return ''.join(all_output) | ||
711 | |||
712 | |||
713 | def read_text_file(filename): | ||
714 | """Return the contents of *filename*. | ||
715 | |||
716 | Try to decode the file contents with utf-8, the preferred system encoding | ||
717 | (e.g., cp1252 on some Windows machines), and latin1, in that order. | ||
718 | Decoding a byte string with latin1 will never raise an error. In the worst | ||
719 | case, the returned string will contain some garbage characters. | ||
720 | |||
721 | """ | ||
722 | with open(filename, 'rb') as fp: | ||
723 | data = fp.read() | ||
724 | |||
725 | encodings = ['utf-8', locale.getpreferredencoding(False), 'latin1'] | ||
726 | for enc in encodings: | ||
727 | try: | ||
728 | data = data.decode(enc) | ||
729 | except UnicodeDecodeError: | ||
730 | continue | ||
731 | break | ||
732 | |||
733 | assert type(data) != bytes # Latin1 should have worked. | ||
734 | return data | ||
735 | |||
736 | |||
737 | def _make_build_dir(build_dir): | ||
738 | os.makedirs(build_dir) | ||
739 | write_delete_marker_file(build_dir) | ||
740 | |||
741 | |||
742 | class FakeFile(object): | ||
743 | """Wrap a list of lines in an object with readline() to make | ||
744 | ConfigParser happy.""" | ||
745 | def __init__(self, lines): | ||
746 | self._gen = (l for l in lines) | ||
747 | |||
748 | def readline(self): | ||
749 | try: | ||
750 | try: | ||
751 | return next(self._gen) | ||
752 | except NameError: | ||
753 | return self._gen.next() | ||
754 | except StopIteration: | ||
755 | return '' | ||
756 | |||
757 | def __iter__(self): | ||
758 | return self._gen | ||
759 | |||
760 | |||
761 | class StreamWrapper(StringIO): | ||
762 | |||
763 | @classmethod | ||
764 | def from_stream(cls, orig_stream): | ||
765 | cls.orig_stream = orig_stream | ||
766 | return cls() | ||
767 | |||
768 | # compileall.compile_dir() needs stdout.encoding to print to stdout | ||
769 | @property | ||
770 | def encoding(self): | ||
771 | return self.orig_stream.encoding | ||
772 | |||
773 | |||
774 | @contextlib.contextmanager | ||
775 | def captured_output(stream_name): | ||
776 | """Return a context manager used by captured_stdout/stdin/stderr | ||
777 | that temporarily replaces the sys stream *stream_name* with a StringIO. | ||
778 | |||
779 | Taken from Lib/support/__init__.py in the CPython repo. | ||
780 | """ | ||
781 | orig_stdout = getattr(sys, stream_name) | ||
782 | setattr(sys, stream_name, StreamWrapper.from_stream(orig_stdout)) | ||
783 | try: | ||
784 | yield getattr(sys, stream_name) | ||
785 | finally: | ||
786 | setattr(sys, stream_name, orig_stdout) | ||
787 | |||
788 | |||
789 | def captured_stdout(): | ||
790 | """Capture the output of sys.stdout: | ||
791 | |||
792 | with captured_stdout() as stdout: | ||
793 | print('hello') | ||
794 | self.assertEqual(stdout.getvalue(), 'hello\n') | ||
795 | |||
796 | Taken from Lib/support/__init__.py in the CPython repo. | ||
797 | """ | ||
798 | return captured_output('stdout') | ||
799 | |||
800 | |||
801 | class cached_property(object): | ||
802 | """A property that is only computed once per instance and then replaces | ||
803 | itself with an ordinary attribute. Deleting the attribute resets the | ||
804 | property. | ||
805 | |||
806 | Source: https://github.com/bottlepy/bottle/blob/0.11.5/bottle.py#L175 | ||
807 | """ | ||
808 | |||
809 | def __init__(self, func): | ||
810 | self.__doc__ = getattr(func, '__doc__') | ||
811 | self.func = func | ||
812 | |||
813 | def __get__(self, obj, cls): | ||
814 | if obj is None: | ||
815 | # We're being accessed from the class itself, not from an object | ||
816 | return self | ||
817 | value = obj.__dict__[self.func.__name__] = self.func(obj) | ||
818 | return value | ||
819 | |||
820 | |||
821 | def get_installed_version(dist_name, lookup_dirs=None): | ||
822 | """Get the installed version of dist_name avoiding pkg_resources cache""" | ||
823 | # Create a requirement that we'll look for inside of setuptools. | ||
824 | req = pkg_resources.Requirement.parse(dist_name) | ||
825 | |||
826 | # We want to avoid having this cached, so we need to construct a new | ||
827 | # working set each time. | ||
828 | if lookup_dirs is None: | ||
829 | working_set = pkg_resources.WorkingSet() | ||
830 | else: | ||
831 | working_set = pkg_resources.WorkingSet(lookup_dirs) | ||
832 | |||
833 | # Get the installed distribution from our working set | ||
834 | dist = working_set.find(req) | ||
835 | |||
836 | # Check to see if we got an installed distribution or not, if we did | ||
837 | # we want to return it's version. | ||
838 | return dist.version if dist else None | ||
839 | |||
840 | |||
841 | def consume(iterator): | ||
842 | """Consume an iterable at C speed.""" | ||
843 | deque(iterator, maxlen=0) | ||
844 | |||
845 | |||
846 | # Simulates an enum | ||
847 | def enum(*sequential, **named): | ||
848 | enums = dict(zip(sequential, range(len(sequential))), **named) | ||
849 | reverse = {value: key for key, value in enums.items()} | ||
850 | enums['reverse_mapping'] = reverse | ||
851 | return type('Enum', (), enums) | ||