install.py 28 KB

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