install.py 25 KB

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