install.py 19 KB

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