Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/pip/_internal/commands/install.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
1 import errno | |
2 import logging | |
3 import operator | |
4 import os | |
5 import shutil | |
6 import site | |
7 from optparse import SUPPRESS_HELP | |
8 | |
9 from pip._vendor import pkg_resources | |
10 from pip._vendor.packaging.utils import canonicalize_name | |
11 | |
12 from pip._internal.cache import WheelCache | |
13 from pip._internal.cli import cmdoptions | |
14 from pip._internal.cli.cmdoptions import make_target_python | |
15 from pip._internal.cli.req_command import RequirementCommand, with_cleanup | |
16 from pip._internal.cli.status_codes import ERROR, SUCCESS | |
17 from pip._internal.exceptions import CommandError, InstallationError | |
18 from pip._internal.locations import distutils_scheme | |
19 from pip._internal.operations.check import check_install_conflicts | |
20 from pip._internal.req import install_given_reqs | |
21 from pip._internal.req.req_tracker import get_requirement_tracker | |
22 from pip._internal.utils.distutils_args import parse_distutils_args | |
23 from pip._internal.utils.filesystem import test_writable_dir | |
24 from pip._internal.utils.misc import ( | |
25 ensure_dir, | |
26 get_installed_version, | |
27 get_pip_version, | |
28 protect_pip_from_modification_on_windows, | |
29 write_output, | |
30 ) | |
31 from pip._internal.utils.temp_dir import TempDirectory | |
32 from pip._internal.utils.typing import MYPY_CHECK_RUNNING | |
33 from pip._internal.utils.virtualenv import virtualenv_no_global | |
34 from pip._internal.wheel_builder import build, should_build_for_install_command | |
35 | |
36 if MYPY_CHECK_RUNNING: | |
37 from optparse import Values | |
38 from typing import Iterable, List, Optional | |
39 | |
40 from pip._internal.models.format_control import FormatControl | |
41 from pip._internal.operations.check import ConflictDetails | |
42 from pip._internal.req.req_install import InstallRequirement | |
43 from pip._internal.wheel_builder import BinaryAllowedPredicate | |
44 | |
45 | |
46 logger = logging.getLogger(__name__) | |
47 | |
48 | |
49 def get_check_binary_allowed(format_control): | |
50 # type: (FormatControl) -> BinaryAllowedPredicate | |
51 def check_binary_allowed(req): | |
52 # type: (InstallRequirement) -> bool | |
53 canonical_name = canonicalize_name(req.name) | |
54 allowed_formats = format_control.get_allowed_formats(canonical_name) | |
55 return "binary" in allowed_formats | |
56 | |
57 return check_binary_allowed | |
58 | |
59 | |
60 class InstallCommand(RequirementCommand): | |
61 """ | |
62 Install packages from: | |
63 | |
64 - PyPI (and other indexes) using requirement specifiers. | |
65 - VCS project urls. | |
66 - Local project directories. | |
67 - Local or remote source archives. | |
68 | |
69 pip also supports installing from "requirements files", which provide | |
70 an easy way to specify a whole environment to be installed. | |
71 """ | |
72 | |
73 usage = """ | |
74 %prog [options] <requirement specifier> [package-index-options] ... | |
75 %prog [options] -r <requirements file> [package-index-options] ... | |
76 %prog [options] [-e] <vcs project url> ... | |
77 %prog [options] [-e] <local project path> ... | |
78 %prog [options] <archive url/path> ...""" | |
79 | |
80 def add_options(self): | |
81 # type: () -> None | |
82 self.cmd_opts.add_option(cmdoptions.requirements()) | |
83 self.cmd_opts.add_option(cmdoptions.constraints()) | |
84 self.cmd_opts.add_option(cmdoptions.no_deps()) | |
85 self.cmd_opts.add_option(cmdoptions.pre()) | |
86 | |
87 self.cmd_opts.add_option(cmdoptions.editable()) | |
88 self.cmd_opts.add_option( | |
89 '-t', '--target', | |
90 dest='target_dir', | |
91 metavar='dir', | |
92 default=None, | |
93 help='Install packages into <dir>. ' | |
94 'By default this will not replace existing files/folders in ' | |
95 '<dir>. Use --upgrade to replace existing packages in <dir> ' | |
96 'with new versions.' | |
97 ) | |
98 cmdoptions.add_target_python_options(self.cmd_opts) | |
99 | |
100 self.cmd_opts.add_option( | |
101 '--user', | |
102 dest='use_user_site', | |
103 action='store_true', | |
104 help="Install to the Python user install directory for your " | |
105 "platform. Typically ~/.local/, or %APPDATA%\\Python on " | |
106 "Windows. (See the Python documentation for site.USER_BASE " | |
107 "for full details.)") | |
108 self.cmd_opts.add_option( | |
109 '--no-user', | |
110 dest='use_user_site', | |
111 action='store_false', | |
112 help=SUPPRESS_HELP) | |
113 self.cmd_opts.add_option( | |
114 '--root', | |
115 dest='root_path', | |
116 metavar='dir', | |
117 default=None, | |
118 help="Install everything relative to this alternate root " | |
119 "directory.") | |
120 self.cmd_opts.add_option( | |
121 '--prefix', | |
122 dest='prefix_path', | |
123 metavar='dir', | |
124 default=None, | |
125 help="Installation prefix where lib, bin and other top-level " | |
126 "folders are placed") | |
127 | |
128 self.cmd_opts.add_option(cmdoptions.build_dir()) | |
129 | |
130 self.cmd_opts.add_option(cmdoptions.src()) | |
131 | |
132 self.cmd_opts.add_option( | |
133 '-U', '--upgrade', | |
134 dest='upgrade', | |
135 action='store_true', | |
136 help='Upgrade all specified packages to the newest available ' | |
137 'version. The handling of dependencies depends on the ' | |
138 'upgrade-strategy used.' | |
139 ) | |
140 | |
141 self.cmd_opts.add_option( | |
142 '--upgrade-strategy', | |
143 dest='upgrade_strategy', | |
144 default='only-if-needed', | |
145 choices=['only-if-needed', 'eager'], | |
146 help='Determines how dependency upgrading should be handled ' | |
147 '[default: %default]. ' | |
148 '"eager" - dependencies are upgraded regardless of ' | |
149 'whether the currently installed version satisfies the ' | |
150 'requirements of the upgraded package(s). ' | |
151 '"only-if-needed" - are upgraded only when they do not ' | |
152 'satisfy the requirements of the upgraded package(s).' | |
153 ) | |
154 | |
155 self.cmd_opts.add_option( | |
156 '--force-reinstall', | |
157 dest='force_reinstall', | |
158 action='store_true', | |
159 help='Reinstall all packages even if they are already ' | |
160 'up-to-date.') | |
161 | |
162 self.cmd_opts.add_option( | |
163 '-I', '--ignore-installed', | |
164 dest='ignore_installed', | |
165 action='store_true', | |
166 help='Ignore the installed packages, overwriting them. ' | |
167 'This can break your system if the existing package ' | |
168 'is of a different version or was installed ' | |
169 'with a different package manager!' | |
170 ) | |
171 | |
172 self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) | |
173 self.cmd_opts.add_option(cmdoptions.no_build_isolation()) | |
174 self.cmd_opts.add_option(cmdoptions.use_pep517()) | |
175 self.cmd_opts.add_option(cmdoptions.no_use_pep517()) | |
176 | |
177 self.cmd_opts.add_option(cmdoptions.install_options()) | |
178 self.cmd_opts.add_option(cmdoptions.global_options()) | |
179 | |
180 self.cmd_opts.add_option( | |
181 "--compile", | |
182 action="store_true", | |
183 dest="compile", | |
184 default=True, | |
185 help="Compile Python source files to bytecode", | |
186 ) | |
187 | |
188 self.cmd_opts.add_option( | |
189 "--no-compile", | |
190 action="store_false", | |
191 dest="compile", | |
192 help="Do not compile Python source files to bytecode", | |
193 ) | |
194 | |
195 self.cmd_opts.add_option( | |
196 "--no-warn-script-location", | |
197 action="store_false", | |
198 dest="warn_script_location", | |
199 default=True, | |
200 help="Do not warn when installing scripts outside PATH", | |
201 ) | |
202 self.cmd_opts.add_option( | |
203 "--no-warn-conflicts", | |
204 action="store_false", | |
205 dest="warn_about_conflicts", | |
206 default=True, | |
207 help="Do not warn about broken dependencies", | |
208 ) | |
209 | |
210 self.cmd_opts.add_option(cmdoptions.no_binary()) | |
211 self.cmd_opts.add_option(cmdoptions.only_binary()) | |
212 self.cmd_opts.add_option(cmdoptions.prefer_binary()) | |
213 self.cmd_opts.add_option(cmdoptions.require_hashes()) | |
214 self.cmd_opts.add_option(cmdoptions.progress_bar()) | |
215 | |
216 index_opts = cmdoptions.make_option_group( | |
217 cmdoptions.index_group, | |
218 self.parser, | |
219 ) | |
220 | |
221 self.parser.insert_option_group(0, index_opts) | |
222 self.parser.insert_option_group(0, self.cmd_opts) | |
223 | |
224 @with_cleanup | |
225 def run(self, options, args): | |
226 # type: (Values, List[str]) -> int | |
227 if options.use_user_site and options.target_dir is not None: | |
228 raise CommandError("Can not combine '--user' and '--target'") | |
229 | |
230 cmdoptions.check_install_build_global(options) | |
231 upgrade_strategy = "to-satisfy-only" | |
232 if options.upgrade: | |
233 upgrade_strategy = options.upgrade_strategy | |
234 | |
235 cmdoptions.check_dist_restriction(options, check_target=True) | |
236 | |
237 install_options = options.install_options or [] | |
238 | |
239 logger.debug("Using %s", get_pip_version()) | |
240 options.use_user_site = decide_user_install( | |
241 options.use_user_site, | |
242 prefix_path=options.prefix_path, | |
243 target_dir=options.target_dir, | |
244 root_path=options.root_path, | |
245 isolated_mode=options.isolated_mode, | |
246 ) | |
247 | |
248 target_temp_dir = None # type: Optional[TempDirectory] | |
249 target_temp_dir_path = None # type: Optional[str] | |
250 if options.target_dir: | |
251 options.ignore_installed = True | |
252 options.target_dir = os.path.abspath(options.target_dir) | |
253 if (os.path.exists(options.target_dir) and not | |
254 os.path.isdir(options.target_dir)): | |
255 raise CommandError( | |
256 "Target path exists but is not a directory, will not " | |
257 "continue." | |
258 ) | |
259 | |
260 # Create a target directory for using with the target option | |
261 target_temp_dir = TempDirectory(kind="target") | |
262 target_temp_dir_path = target_temp_dir.path | |
263 self.enter_context(target_temp_dir) | |
264 | |
265 global_options = options.global_options or [] | |
266 | |
267 session = self.get_default_session(options) | |
268 | |
269 target_python = make_target_python(options) | |
270 finder = self._build_package_finder( | |
271 options=options, | |
272 session=session, | |
273 target_python=target_python, | |
274 ignore_requires_python=options.ignore_requires_python, | |
275 ) | |
276 wheel_cache = WheelCache(options.cache_dir, options.format_control) | |
277 | |
278 req_tracker = self.enter_context(get_requirement_tracker()) | |
279 | |
280 directory = TempDirectory( | |
281 delete=not options.no_clean, | |
282 kind="install", | |
283 globally_managed=True, | |
284 ) | |
285 | |
286 try: | |
287 reqs = self.get_requirements(args, options, finder, session) | |
288 | |
289 reject_location_related_install_options( | |
290 reqs, options.install_options | |
291 ) | |
292 | |
293 preparer = self.make_requirement_preparer( | |
294 temp_build_dir=directory, | |
295 options=options, | |
296 req_tracker=req_tracker, | |
297 session=session, | |
298 finder=finder, | |
299 use_user_site=options.use_user_site, | |
300 ) | |
301 resolver = self.make_resolver( | |
302 preparer=preparer, | |
303 finder=finder, | |
304 options=options, | |
305 wheel_cache=wheel_cache, | |
306 use_user_site=options.use_user_site, | |
307 ignore_installed=options.ignore_installed, | |
308 ignore_requires_python=options.ignore_requires_python, | |
309 force_reinstall=options.force_reinstall, | |
310 upgrade_strategy=upgrade_strategy, | |
311 use_pep517=options.use_pep517, | |
312 ) | |
313 | |
314 self.trace_basic_info(finder) | |
315 | |
316 requirement_set = resolver.resolve( | |
317 reqs, check_supported_wheels=not options.target_dir | |
318 ) | |
319 | |
320 try: | |
321 pip_req = requirement_set.get_requirement("pip") | |
322 except KeyError: | |
323 modifying_pip = False | |
324 else: | |
325 # If we're not replacing an already installed pip, | |
326 # we're not modifying it. | |
327 modifying_pip = pip_req.satisfied_by is None | |
328 protect_pip_from_modification_on_windows( | |
329 modifying_pip=modifying_pip | |
330 ) | |
331 | |
332 check_binary_allowed = get_check_binary_allowed( | |
333 finder.format_control | |
334 ) | |
335 | |
336 reqs_to_build = [ | |
337 r for r in requirement_set.requirements.values() | |
338 if should_build_for_install_command( | |
339 r, check_binary_allowed | |
340 ) | |
341 ] | |
342 | |
343 _, build_failures = build( | |
344 reqs_to_build, | |
345 wheel_cache=wheel_cache, | |
346 verify=True, | |
347 build_options=[], | |
348 global_options=[], | |
349 ) | |
350 | |
351 # If we're using PEP 517, we cannot do a direct install | |
352 # so we fail here. | |
353 pep517_build_failure_names = [ | |
354 r.name # type: ignore | |
355 for r in build_failures if r.use_pep517 | |
356 ] # type: List[str] | |
357 if pep517_build_failure_names: | |
358 raise InstallationError( | |
359 "Could not build wheels for {} which use" | |
360 " PEP 517 and cannot be installed directly".format( | |
361 ", ".join(pep517_build_failure_names) | |
362 ) | |
363 ) | |
364 | |
365 # For now, we just warn about failures building legacy | |
366 # requirements, as we'll fall through to a direct | |
367 # install for those. | |
368 for r in build_failures: | |
369 if not r.use_pep517: | |
370 r.legacy_install_reason = 8368 | |
371 | |
372 to_install = resolver.get_installation_order( | |
373 requirement_set | |
374 ) | |
375 | |
376 # Check for conflicts in the package set we're installing. | |
377 conflicts = None # type: Optional[ConflictDetails] | |
378 should_warn_about_conflicts = ( | |
379 not options.ignore_dependencies and | |
380 options.warn_about_conflicts | |
381 ) | |
382 if should_warn_about_conflicts: | |
383 conflicts = self._determine_conflicts(to_install) | |
384 | |
385 # Don't warn about script install locations if | |
386 # --target has been specified | |
387 warn_script_location = options.warn_script_location | |
388 if options.target_dir: | |
389 warn_script_location = False | |
390 | |
391 installed = install_given_reqs( | |
392 to_install, | |
393 install_options, | |
394 global_options, | |
395 root=options.root_path, | |
396 home=target_temp_dir_path, | |
397 prefix=options.prefix_path, | |
398 warn_script_location=warn_script_location, | |
399 use_user_site=options.use_user_site, | |
400 pycompile=options.compile, | |
401 ) | |
402 | |
403 lib_locations = get_lib_location_guesses( | |
404 user=options.use_user_site, | |
405 home=target_temp_dir_path, | |
406 root=options.root_path, | |
407 prefix=options.prefix_path, | |
408 isolated=options.isolated_mode, | |
409 ) | |
410 working_set = pkg_resources.WorkingSet(lib_locations) | |
411 | |
412 installed.sort(key=operator.attrgetter('name')) | |
413 items = [] | |
414 for result in installed: | |
415 item = result.name | |
416 try: | |
417 installed_version = get_installed_version( | |
418 result.name, working_set=working_set | |
419 ) | |
420 if installed_version: | |
421 item += '-' + installed_version | |
422 except Exception: | |
423 pass | |
424 items.append(item) | |
425 | |
426 if conflicts is not None: | |
427 self._warn_about_conflicts( | |
428 conflicts, | |
429 resolver_variant=self.determine_resolver_variant(options), | |
430 ) | |
431 | |
432 installed_desc = ' '.join(items) | |
433 if installed_desc: | |
434 write_output( | |
435 'Successfully installed %s', installed_desc, | |
436 ) | |
437 except OSError as error: | |
438 show_traceback = (self.verbosity >= 1) | |
439 | |
440 message = create_os_error_message( | |
441 error, show_traceback, options.use_user_site, | |
442 ) | |
443 logger.error(message, exc_info=show_traceback) # noqa | |
444 | |
445 return ERROR | |
446 | |
447 if options.target_dir: | |
448 assert target_temp_dir | |
449 self._handle_target_dir( | |
450 options.target_dir, target_temp_dir, options.upgrade | |
451 ) | |
452 | |
453 return SUCCESS | |
454 | |
455 def _handle_target_dir(self, target_dir, target_temp_dir, upgrade): | |
456 # type: (str, TempDirectory, bool) -> None | |
457 ensure_dir(target_dir) | |
458 | |
459 # Checking both purelib and platlib directories for installed | |
460 # packages to be moved to target directory | |
461 lib_dir_list = [] | |
462 | |
463 # Checking both purelib and platlib directories for installed | |
464 # packages to be moved to target directory | |
465 scheme = distutils_scheme('', home=target_temp_dir.path) | |
466 purelib_dir = scheme['purelib'] | |
467 platlib_dir = scheme['platlib'] | |
468 data_dir = scheme['data'] | |
469 | |
470 if os.path.exists(purelib_dir): | |
471 lib_dir_list.append(purelib_dir) | |
472 if os.path.exists(platlib_dir) and platlib_dir != purelib_dir: | |
473 lib_dir_list.append(platlib_dir) | |
474 if os.path.exists(data_dir): | |
475 lib_dir_list.append(data_dir) | |
476 | |
477 for lib_dir in lib_dir_list: | |
478 for item in os.listdir(lib_dir): | |
479 if lib_dir == data_dir: | |
480 ddir = os.path.join(data_dir, item) | |
481 if any(s.startswith(ddir) for s in lib_dir_list[:-1]): | |
482 continue | |
483 target_item_dir = os.path.join(target_dir, item) | |
484 if os.path.exists(target_item_dir): | |
485 if not upgrade: | |
486 logger.warning( | |
487 'Target directory %s already exists. Specify ' | |
488 '--upgrade to force replacement.', | |
489 target_item_dir | |
490 ) | |
491 continue | |
492 if os.path.islink(target_item_dir): | |
493 logger.warning( | |
494 'Target directory %s already exists and is ' | |
495 'a link. pip will not automatically replace ' | |
496 'links, please remove if replacement is ' | |
497 'desired.', | |
498 target_item_dir | |
499 ) | |
500 continue | |
501 if os.path.isdir(target_item_dir): | |
502 shutil.rmtree(target_item_dir) | |
503 else: | |
504 os.remove(target_item_dir) | |
505 | |
506 shutil.move( | |
507 os.path.join(lib_dir, item), | |
508 target_item_dir | |
509 ) | |
510 | |
511 def _determine_conflicts(self, to_install): | |
512 # type: (List[InstallRequirement]) -> Optional[ConflictDetails] | |
513 try: | |
514 return check_install_conflicts(to_install) | |
515 except Exception: | |
516 logger.exception( | |
517 "Error while checking for conflicts. Please file an issue on " | |
518 "pip's issue tracker: https://github.com/pypa/pip/issues/new" | |
519 ) | |
520 return None | |
521 | |
522 def _warn_about_conflicts(self, conflict_details, resolver_variant): | |
523 # type: (ConflictDetails, str) -> None | |
524 package_set, (missing, conflicting) = conflict_details | |
525 if not missing and not conflicting: | |
526 return | |
527 | |
528 parts = [] # type: List[str] | |
529 if resolver_variant == "legacy": | |
530 parts.append( | |
531 "pip's legacy dependency resolver does not consider dependency " | |
532 "conflicts when selecting packages. This behaviour is the " | |
533 "source of the following dependency conflicts." | |
534 ) | |
535 else: | |
536 assert resolver_variant == "2020-resolver" | |
537 parts.append( | |
538 "pip's dependency resolver does not currently take into account " | |
539 "all the packages that are installed. This behaviour is the " | |
540 "source of the following dependency conflicts." | |
541 ) | |
542 | |
543 # NOTE: There is some duplication here, with commands/check.py | |
544 for project_name in missing: | |
545 version = package_set[project_name][0] | |
546 for dependency in missing[project_name]: | |
547 message = ( | |
548 "{name} {version} requires {requirement}, " | |
549 "which is not installed." | |
550 ).format( | |
551 name=project_name, | |
552 version=version, | |
553 requirement=dependency[1], | |
554 ) | |
555 parts.append(message) | |
556 | |
557 for project_name in conflicting: | |
558 version = package_set[project_name][0] | |
559 for dep_name, dep_version, req in conflicting[project_name]: | |
560 message = ( | |
561 "{name} {version} requires {requirement}, but {you} have " | |
562 "{dep_name} {dep_version} which is incompatible." | |
563 ).format( | |
564 name=project_name, | |
565 version=version, | |
566 requirement=req, | |
567 dep_name=dep_name, | |
568 dep_version=dep_version, | |
569 you=("you" if resolver_variant == "2020-resolver" else "you'll") | |
570 ) | |
571 parts.append(message) | |
572 | |
573 logger.critical("\n".join(parts)) | |
574 | |
575 | |
576 def get_lib_location_guesses( | |
577 user=False, # type: bool | |
578 home=None, # type: Optional[str] | |
579 root=None, # type: Optional[str] | |
580 isolated=False, # type: bool | |
581 prefix=None # type: Optional[str] | |
582 ): | |
583 # type:(...) -> List[str] | |
584 scheme = distutils_scheme('', user=user, home=home, root=root, | |
585 isolated=isolated, prefix=prefix) | |
586 return [scheme['purelib'], scheme['platlib']] | |
587 | |
588 | |
589 def site_packages_writable(root, isolated): | |
590 # type: (Optional[str], bool) -> bool | |
591 return all( | |
592 test_writable_dir(d) for d in set( | |
593 get_lib_location_guesses(root=root, isolated=isolated)) | |
594 ) | |
595 | |
596 | |
597 def decide_user_install( | |
598 use_user_site, # type: Optional[bool] | |
599 prefix_path=None, # type: Optional[str] | |
600 target_dir=None, # type: Optional[str] | |
601 root_path=None, # type: Optional[str] | |
602 isolated_mode=False, # type: bool | |
603 ): | |
604 # type: (...) -> bool | |
605 """Determine whether to do a user install based on the input options. | |
606 | |
607 If use_user_site is False, no additional checks are done. | |
608 If use_user_site is True, it is checked for compatibility with other | |
609 options. | |
610 If use_user_site is None, the default behaviour depends on the environment, | |
611 which is provided by the other arguments. | |
612 """ | |
613 # In some cases (config from tox), use_user_site can be set to an integer | |
614 # rather than a bool, which 'use_user_site is False' wouldn't catch. | |
615 if (use_user_site is not None) and (not use_user_site): | |
616 logger.debug("Non-user install by explicit request") | |
617 return False | |
618 | |
619 if use_user_site: | |
620 if prefix_path: | |
621 raise CommandError( | |
622 "Can not combine '--user' and '--prefix' as they imply " | |
623 "different installation locations" | |
624 ) | |
625 if virtualenv_no_global(): | |
626 raise InstallationError( | |
627 "Can not perform a '--user' install. User site-packages " | |
628 "are not visible in this virtualenv." | |
629 ) | |
630 logger.debug("User install by explicit request") | |
631 return True | |
632 | |
633 # If we are here, user installs have not been explicitly requested/avoided | |
634 assert use_user_site is None | |
635 | |
636 # user install incompatible with --prefix/--target | |
637 if prefix_path or target_dir: | |
638 logger.debug("Non-user install due to --prefix or --target option") | |
639 return False | |
640 | |
641 # If user installs are not enabled, choose a non-user install | |
642 if not site.ENABLE_USER_SITE: | |
643 logger.debug("Non-user install because user site-packages disabled") | |
644 return False | |
645 | |
646 # If we have permission for a non-user install, do that, | |
647 # otherwise do a user install. | |
648 if site_packages_writable(root=root_path, isolated=isolated_mode): | |
649 logger.debug("Non-user install because site-packages writeable") | |
650 return False | |
651 | |
652 logger.info("Defaulting to user installation because normal site-packages " | |
653 "is not writeable") | |
654 return True | |
655 | |
656 | |
657 def reject_location_related_install_options(requirements, options): | |
658 # type: (List[InstallRequirement], Optional[List[str]]) -> None | |
659 """If any location-changing --install-option arguments were passed for | |
660 requirements or on the command-line, then show a deprecation warning. | |
661 """ | |
662 def format_options(option_names): | |
663 # type: (Iterable[str]) -> List[str] | |
664 return ["--{}".format(name.replace("_", "-")) for name in option_names] | |
665 | |
666 offenders = [] | |
667 | |
668 for requirement in requirements: | |
669 install_options = requirement.install_options | |
670 location_options = parse_distutils_args(install_options) | |
671 if location_options: | |
672 offenders.append( | |
673 "{!r} from {}".format( | |
674 format_options(location_options.keys()), requirement | |
675 ) | |
676 ) | |
677 | |
678 if options: | |
679 location_options = parse_distutils_args(options) | |
680 if location_options: | |
681 offenders.append( | |
682 "{!r} from command line".format( | |
683 format_options(location_options.keys()) | |
684 ) | |
685 ) | |
686 | |
687 if not offenders: | |
688 return | |
689 | |
690 raise CommandError( | |
691 "Location-changing options found in --install-option: {}." | |
692 " This is unsupported, use pip-level options like --user," | |
693 " --prefix, --root, and --target instead.".format( | |
694 "; ".join(offenders) | |
695 ) | |
696 ) | |
697 | |
698 | |
699 def create_os_error_message(error, show_traceback, using_user_site): | |
700 # type: (OSError, bool, bool) -> str | |
701 """Format an error message for an OSError | |
702 | |
703 It may occur anytime during the execution of the install command. | |
704 """ | |
705 parts = [] | |
706 | |
707 # Mention the error if we are not going to show a traceback | |
708 parts.append("Could not install packages due to an OSError") | |
709 if not show_traceback: | |
710 parts.append(": ") | |
711 parts.append(str(error)) | |
712 else: | |
713 parts.append(".") | |
714 | |
715 # Spilt the error indication from a helper message (if any) | |
716 parts[-1] += "\n" | |
717 | |
718 # Suggest useful actions to the user: | |
719 # (1) using user site-packages or (2) verifying the permissions | |
720 if error.errno == errno.EACCES: | |
721 user_option_part = "Consider using the `--user` option" | |
722 permissions_part = "Check the permissions" | |
723 | |
724 if not using_user_site: | |
725 parts.extend([ | |
726 user_option_part, " or ", | |
727 permissions_part.lower(), | |
728 ]) | |
729 else: | |
730 parts.append(permissions_part) | |
731 parts.append(".\n") | |
732 | |
733 return "".join(parts).strip() + "\n" |