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 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. from __future__ import absolute_import
  2. import errno
  3. import logging
  4. import operator
  5. import os
  6. import shutil
  7. from optparse import SUPPRESS_HELP
  8. from pip._vendor import pkg_resources
  9. from pip._internal.cache import WheelCache
  10. from pip._internal.cli import cmdoptions
  11. from pip._internal.cli.base_command import RequirementCommand
  12. from pip._internal.cli.status_codes import ERROR
  13. from pip._internal.exceptions import (
  14. CommandError, InstallationError, PreviousBuildDirError,
  15. )
  16. from pip._internal.locations import distutils_scheme, virtualenv_no_global
  17. from pip._internal.operations.check import check_install_conflicts
  18. from pip._internal.operations.prepare import RequirementPreparer
  19. from pip._internal.req import RequirementSet, install_given_reqs
  20. from pip._internal.req.req_tracker import RequirementTracker
  21. from pip._internal.resolve import Resolver
  22. from pip._internal.utils.filesystem import check_path_owner
  23. from pip._internal.utils.misc import (
  24. ensure_dir, get_installed_version,
  25. protect_pip_from_modification_on_windows,
  26. )
  27. from pip._internal.utils.temp_dir import TempDirectory
  28. from pip._internal.wheel import WheelBuilder
  29. logger = logging.getLogger(__name__)
  30. class InstallCommand(RequirementCommand):
  31. """
  32. Install packages from:
  33. - PyPI (and other indexes) using requirement specifiers.
  34. - VCS project urls.
  35. - Local project directories.
  36. - Local or remote source archives.
  37. pip also supports installing from "requirements files", which provide
  38. an easy way to specify a whole environment to be installed.
  39. """
  40. name = 'install'
  41. usage = """
  42. %prog [options] <requirement specifier> [package-index-options] ...
  43. %prog [options] -r <requirements file> [package-index-options] ...
  44. %prog [options] [-e] <vcs project url> ...
  45. %prog [options] [-e] <local project path> ...
  46. %prog [options] <archive url/path> ..."""
  47. summary = 'Install packages.'
  48. def __init__(self, *args, **kw):
  49. super(InstallCommand, self).__init__(*args, **kw)
  50. cmd_opts = self.cmd_opts
  51. cmd_opts.add_option(cmdoptions.requirements())
  52. cmd_opts.add_option(cmdoptions.constraints())
  53. cmd_opts.add_option(cmdoptions.no_deps())
  54. cmd_opts.add_option(cmdoptions.pre())
  55. cmd_opts.add_option(cmdoptions.editable())
  56. cmd_opts.add_option(
  57. '-t', '--target',
  58. dest='target_dir',
  59. metavar='dir',
  60. default=None,
  61. help='Install packages into <dir>. '
  62. 'By default this will not replace existing files/folders in '
  63. '<dir>. Use --upgrade to replace existing packages in <dir> '
  64. 'with new versions.'
  65. )
  66. cmd_opts.add_option(cmdoptions.platform())
  67. cmd_opts.add_option(cmdoptions.python_version())
  68. cmd_opts.add_option(cmdoptions.implementation())
  69. cmd_opts.add_option(cmdoptions.abi())
  70. cmd_opts.add_option(
  71. '--user',
  72. dest='use_user_site',
  73. action='store_true',
  74. help="Install to the Python user install directory for your "
  75. "platform. Typically ~/.local/, or %APPDATA%\\Python on "
  76. "Windows. (See the Python documentation for site.USER_BASE "
  77. "for full details.)")
  78. cmd_opts.add_option(
  79. '--no-user',
  80. dest='use_user_site',
  81. action='store_false',
  82. help=SUPPRESS_HELP)
  83. cmd_opts.add_option(
  84. '--root',
  85. dest='root_path',
  86. metavar='dir',
  87. default=None,
  88. help="Install everything relative to this alternate root "
  89. "directory.")
  90. cmd_opts.add_option(
  91. '--prefix',
  92. dest='prefix_path',
  93. metavar='dir',
  94. default=None,
  95. help="Installation prefix where lib, bin and other top-level "
  96. "folders are placed")
  97. cmd_opts.add_option(cmdoptions.build_dir())
  98. cmd_opts.add_option(cmdoptions.src())
  99. cmd_opts.add_option(
  100. '-U', '--upgrade',
  101. dest='upgrade',
  102. action='store_true',
  103. help='Upgrade all specified packages to the newest available '
  104. 'version. The handling of dependencies depends on the '
  105. 'upgrade-strategy used.'
  106. )
  107. cmd_opts.add_option(
  108. '--upgrade-strategy',
  109. dest='upgrade_strategy',
  110. default='only-if-needed',
  111. choices=['only-if-needed', 'eager'],
  112. help='Determines how dependency upgrading should be handled '
  113. '[default: %default]. '
  114. '"eager" - dependencies are upgraded regardless of '
  115. 'whether the currently installed version satisfies the '
  116. 'requirements of the upgraded package(s). '
  117. '"only-if-needed" - are upgraded only when they do not '
  118. 'satisfy the requirements of the upgraded package(s).'
  119. )
  120. cmd_opts.add_option(
  121. '--force-reinstall',
  122. dest='force_reinstall',
  123. action='store_true',
  124. help='Reinstall all packages even if they are already '
  125. 'up-to-date.')
  126. cmd_opts.add_option(
  127. '-I', '--ignore-installed',
  128. dest='ignore_installed',
  129. action='store_true',
  130. help='Ignore the installed packages (reinstalling instead).')
  131. cmd_opts.add_option(cmdoptions.ignore_requires_python())
  132. cmd_opts.add_option(cmdoptions.no_build_isolation())
  133. cmd_opts.add_option(cmdoptions.use_pep517())
  134. cmd_opts.add_option(cmdoptions.no_use_pep517())
  135. cmd_opts.add_option(cmdoptions.install_options())
  136. cmd_opts.add_option(cmdoptions.global_options())
  137. cmd_opts.add_option(
  138. "--compile",
  139. action="store_true",
  140. dest="compile",
  141. default=True,
  142. help="Compile Python source files to bytecode",
  143. )
  144. cmd_opts.add_option(
  145. "--no-compile",
  146. action="store_false",
  147. dest="compile",
  148. help="Do not compile Python source files to bytecode",
  149. )
  150. cmd_opts.add_option(
  151. "--no-warn-script-location",
  152. action="store_false",
  153. dest="warn_script_location",
  154. default=True,
  155. help="Do not warn when installing scripts outside PATH",
  156. )
  157. cmd_opts.add_option(
  158. "--no-warn-conflicts",
  159. action="store_false",
  160. dest="warn_about_conflicts",
  161. default=True,
  162. help="Do not warn about broken dependencies",
  163. )
  164. cmd_opts.add_option(cmdoptions.no_binary())
  165. cmd_opts.add_option(cmdoptions.only_binary())
  166. cmd_opts.add_option(cmdoptions.prefer_binary())
  167. cmd_opts.add_option(cmdoptions.no_clean())
  168. cmd_opts.add_option(cmdoptions.require_hashes())
  169. cmd_opts.add_option(cmdoptions.progress_bar())
  170. index_opts = cmdoptions.make_option_group(
  171. cmdoptions.index_group,
  172. self.parser,
  173. )
  174. self.parser.insert_option_group(0, index_opts)
  175. self.parser.insert_option_group(0, cmd_opts)
  176. def run(self, options, args):
  177. cmdoptions.check_install_build_global(options)
  178. upgrade_strategy = "to-satisfy-only"
  179. if options.upgrade:
  180. upgrade_strategy = options.upgrade_strategy
  181. if options.build_dir:
  182. options.build_dir = os.path.abspath(options.build_dir)
  183. cmdoptions.check_dist_restriction(options, check_target=True)
  184. if options.python_version:
  185. python_versions = [options.python_version]
  186. else:
  187. python_versions = None
  188. options.src_dir = os.path.abspath(options.src_dir)
  189. install_options = options.install_options or []
  190. if options.use_user_site:
  191. if options.prefix_path:
  192. raise CommandError(
  193. "Can not combine '--user' and '--prefix' as they imply "
  194. "different installation locations"
  195. )
  196. if virtualenv_no_global():
  197. raise InstallationError(
  198. "Can not perform a '--user' install. User site-packages "
  199. "are not visible in this virtualenv."
  200. )
  201. install_options.append('--user')
  202. install_options.append('--prefix=')
  203. target_temp_dir = TempDirectory(kind="target")
  204. if options.target_dir:
  205. options.ignore_installed = True
  206. options.target_dir = os.path.abspath(options.target_dir)
  207. if (os.path.exists(options.target_dir) and not
  208. os.path.isdir(options.target_dir)):
  209. raise CommandError(
  210. "Target path exists but is not a directory, will not "
  211. "continue."
  212. )
  213. # Create a target directory for using with the target option
  214. target_temp_dir.create()
  215. install_options.append('--home=' + target_temp_dir.path)
  216. global_options = options.global_options or []
  217. with self._build_session(options) as session:
  218. finder = self._build_package_finder(
  219. options=options,
  220. session=session,
  221. platform=options.platform,
  222. python_versions=python_versions,
  223. abi=options.abi,
  224. implementation=options.implementation,
  225. )
  226. build_delete = (not (options.no_clean or options.build_dir))
  227. wheel_cache = WheelCache(options.cache_dir, options.format_control)
  228. if options.cache_dir and not check_path_owner(options.cache_dir):
  229. logger.warning(
  230. "The directory '%s' or its parent directory is not owned "
  231. "by the current user and caching wheels has been "
  232. "disabled. check the permissions and owner of that "
  233. "directory. If executing pip with sudo, you may want "
  234. "sudo's -H flag.",
  235. options.cache_dir,
  236. )
  237. options.cache_dir = None
  238. with RequirementTracker() as req_tracker, TempDirectory(
  239. options.build_dir, delete=build_delete, kind="install"
  240. ) as directory:
  241. requirement_set = RequirementSet(
  242. require_hashes=options.require_hashes,
  243. check_supported_wheels=not options.target_dir,
  244. )
  245. try:
  246. self.populate_requirement_set(
  247. requirement_set, args, options, finder, session,
  248. self.name, wheel_cache
  249. )
  250. preparer = RequirementPreparer(
  251. build_dir=directory.path,
  252. src_dir=options.src_dir,
  253. download_dir=None,
  254. wheel_download_dir=None,
  255. progress_bar=options.progress_bar,
  256. build_isolation=options.build_isolation,
  257. req_tracker=req_tracker,
  258. )
  259. resolver = Resolver(
  260. preparer=preparer,
  261. finder=finder,
  262. session=session,
  263. wheel_cache=wheel_cache,
  264. use_user_site=options.use_user_site,
  265. upgrade_strategy=upgrade_strategy,
  266. force_reinstall=options.force_reinstall,
  267. ignore_dependencies=options.ignore_dependencies,
  268. ignore_requires_python=options.ignore_requires_python,
  269. ignore_installed=options.ignore_installed,
  270. isolated=options.isolated_mode,
  271. use_pep517=options.use_pep517
  272. )
  273. resolver.resolve(requirement_set)
  274. protect_pip_from_modification_on_windows(
  275. modifying_pip=requirement_set.has_requirement("pip")
  276. )
  277. # Consider legacy and PEP517-using requirements separately
  278. legacy_requirements = []
  279. pep517_requirements = []
  280. for req in requirement_set.requirements.values():
  281. if req.use_pep517:
  282. pep517_requirements.append(req)
  283. else:
  284. legacy_requirements.append(req)
  285. # We don't build wheels for legacy requirements if we
  286. # don't have wheel installed or we don't have a cache dir
  287. try:
  288. import wheel # noqa: F401
  289. build_legacy = bool(options.cache_dir)
  290. except ImportError:
  291. build_legacy = False
  292. wb = WheelBuilder(
  293. finder, preparer, wheel_cache,
  294. build_options=[], global_options=[],
  295. )
  296. # Always build PEP 517 requirements
  297. build_failures = wb.build(
  298. pep517_requirements,
  299. session=session, autobuilding=True
  300. )
  301. if build_legacy:
  302. # We don't care about failures building legacy
  303. # requirements, as we'll fall through to a direct
  304. # install for those.
  305. wb.build(
  306. legacy_requirements,
  307. session=session, autobuilding=True
  308. )
  309. # If we're using PEP 517, we cannot do a direct install
  310. # so we fail here.
  311. if build_failures:
  312. raise InstallationError(
  313. "Could not build wheels for {} which use"
  314. " PEP 517 and cannot be installed directly".format(
  315. ", ".join(r.name for r in build_failures)))
  316. to_install = resolver.get_installation_order(
  317. requirement_set
  318. )
  319. # Consistency Checking of the package set we're installing.
  320. should_warn_about_conflicts = (
  321. not options.ignore_dependencies and
  322. options.warn_about_conflicts
  323. )
  324. if should_warn_about_conflicts:
  325. self._warn_about_conflicts(to_install)
  326. # Don't warn about script install locations if
  327. # --target has been specified
  328. warn_script_location = options.warn_script_location
  329. if options.target_dir:
  330. warn_script_location = False
  331. installed = install_given_reqs(
  332. to_install,
  333. install_options,
  334. global_options,
  335. root=options.root_path,
  336. home=target_temp_dir.path,
  337. prefix=options.prefix_path,
  338. pycompile=options.compile,
  339. warn_script_location=warn_script_location,
  340. use_user_site=options.use_user_site,
  341. )
  342. lib_locations = get_lib_location_guesses(
  343. user=options.use_user_site,
  344. home=target_temp_dir.path,
  345. root=options.root_path,
  346. prefix=options.prefix_path,
  347. isolated=options.isolated_mode,
  348. )
  349. working_set = pkg_resources.WorkingSet(lib_locations)
  350. reqs = sorted(installed, key=operator.attrgetter('name'))
  351. items = []
  352. for req in reqs:
  353. item = req.name
  354. try:
  355. installed_version = get_installed_version(
  356. req.name, working_set=working_set
  357. )
  358. if installed_version:
  359. item += '-' + installed_version
  360. except Exception:
  361. pass
  362. items.append(item)
  363. installed = ' '.join(items)
  364. if installed:
  365. logger.info('Successfully installed %s', installed)
  366. except EnvironmentError as error:
  367. show_traceback = (self.verbosity >= 1)
  368. message = create_env_error_message(
  369. error, show_traceback, options.use_user_site,
  370. )
  371. logger.error(message, exc_info=show_traceback)
  372. return ERROR
  373. except PreviousBuildDirError:
  374. options.no_clean = True
  375. raise
  376. finally:
  377. # Clean up
  378. if not options.no_clean:
  379. requirement_set.cleanup_files()
  380. wheel_cache.cleanup()
  381. if options.target_dir:
  382. self._handle_target_dir(
  383. options.target_dir, target_temp_dir, options.upgrade
  384. )
  385. return requirement_set
  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 create_env_error_message(error, show_traceback, using_user_site):
  463. """Format an error message for an EnvironmentError
  464. It may occur anytime during the execution of the install command.
  465. """
  466. parts = []
  467. # Mention the error if we are not going to show a traceback
  468. parts.append("Could not install packages due to an EnvironmentError")
  469. if not show_traceback:
  470. parts.append(": ")
  471. parts.append(str(error))
  472. else:
  473. parts.append(".")
  474. # Spilt the error indication from a helper message (if any)
  475. parts[-1] += "\n"
  476. # Suggest useful actions to the user:
  477. # (1) using user site-packages or (2) verifying the permissions
  478. if error.errno == errno.EACCES:
  479. user_option_part = "Consider using the `--user` option"
  480. permissions_part = "Check the permissions"
  481. if not using_user_site:
  482. parts.extend([
  483. user_option_part, " or ",
  484. permissions_part.lower(),
  485. ])
  486. else:
  487. parts.append(permissions_part)
  488. parts.append(".\n")
  489. return "".join(parts).strip() + "\n"