You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

install.py 23KB

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