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.

535 lines
20 KiB

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