install.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. from __future__ import absolute_import
  2. import logging
  3. import operator
  4. import os
  5. import tempfile
  6. import shutil
  7. import warnings
  8. try:
  9. import wheel
  10. except ImportError:
  11. wheel = None
  12. from pip.req import RequirementSet
  13. from pip.basecommand import RequirementCommand
  14. from pip.locations import virtualenv_no_global, distutils_scheme
  15. from pip.exceptions import (
  16. InstallationError, CommandError, PreviousBuildDirError,
  17. )
  18. from pip import cmdoptions
  19. from pip.utils import ensure_dir, get_installed_version
  20. from pip.utils.build import BuildDirectory
  21. from pip.utils.deprecation import RemovedInPip10Warning
  22. from pip.utils.filesystem import check_path_owner
  23. from pip.wheel import WheelCache, WheelBuilder
  24. logger = logging.getLogger(__name__)
  25. class InstallCommand(RequirementCommand):
  26. """
  27. Install packages from:
  28. - PyPI (and other indexes) using requirement specifiers.
  29. - VCS project urls.
  30. - Local project directories.
  31. - Local or remote source archives.
  32. pip also supports installing from "requirements files", which provide
  33. an easy way to specify a whole environment to be installed.
  34. """
  35. name = 'install'
  36. usage = """
  37. %prog [options] <requirement specifier> [package-index-options] ...
  38. %prog [options] -r <requirements file> [package-index-options] ...
  39. %prog [options] [-e] <vcs project url> ...
  40. %prog [options] [-e] <local project path> ...
  41. %prog [options] <archive url/path> ..."""
  42. summary = 'Install packages.'
  43. def __init__(self, *args, **kw):
  44. super(InstallCommand, self).__init__(*args, **kw)
  45. cmd_opts = self.cmd_opts
  46. cmd_opts.add_option(cmdoptions.constraints())
  47. cmd_opts.add_option(cmdoptions.editable())
  48. cmd_opts.add_option(cmdoptions.requirements())
  49. cmd_opts.add_option(cmdoptions.build_dir())
  50. cmd_opts.add_option(
  51. '-t', '--target',
  52. dest='target_dir',
  53. metavar='dir',
  54. default=None,
  55. help='Install packages into <dir>. '
  56. 'By default this will not replace existing files/folders in '
  57. '<dir>. Use --upgrade to replace existing packages in <dir> '
  58. 'with new versions.'
  59. )
  60. cmd_opts.add_option(
  61. '-d', '--download', '--download-dir', '--download-directory',
  62. dest='download_dir',
  63. metavar='dir',
  64. default=None,
  65. help=("Download packages into <dir> instead of installing them, "
  66. "regardless of what's already installed."),
  67. )
  68. cmd_opts.add_option(cmdoptions.src())
  69. cmd_opts.add_option(
  70. '-U', '--upgrade',
  71. dest='upgrade',
  72. action='store_true',
  73. help='Upgrade all specified packages to the newest available '
  74. 'version. The handling of dependencies depends on the '
  75. 'upgrade-strategy used.'
  76. )
  77. cmd_opts.add_option(
  78. '--upgrade-strategy',
  79. dest='upgrade_strategy',
  80. default='eager',
  81. choices=['only-if-needed', 'eager'],
  82. help='Determines how dependency upgrading should be handled. '
  83. '"eager" - dependencies are upgraded regardless of '
  84. 'whether the currently installed version satisfies the '
  85. 'requirements of the upgraded package(s). '
  86. '"only-if-needed" - are upgraded only when they do not '
  87. 'satisfy the requirements of the upgraded package(s).'
  88. )
  89. cmd_opts.add_option(
  90. '--force-reinstall',
  91. dest='force_reinstall',
  92. action='store_true',
  93. help='When upgrading, reinstall all packages even if they are '
  94. 'already up-to-date.')
  95. cmd_opts.add_option(
  96. '-I', '--ignore-installed',
  97. dest='ignore_installed',
  98. action='store_true',
  99. help='Ignore the installed packages (reinstalling instead).')
  100. cmd_opts.add_option(cmdoptions.ignore_requires_python())
  101. cmd_opts.add_option(cmdoptions.no_deps())
  102. cmd_opts.add_option(cmdoptions.install_options())
  103. cmd_opts.add_option(cmdoptions.global_options())
  104. cmd_opts.add_option(
  105. '--user',
  106. dest='use_user_site',
  107. action='store_true',
  108. help="Install to the Python user install directory for your "
  109. "platform. Typically ~/.local/, or %APPDATA%\Python on "
  110. "Windows. (See the Python documentation for site.USER_BASE "
  111. "for full details.)")
  112. cmd_opts.add_option(
  113. '--egg',
  114. dest='as_egg',
  115. action='store_true',
  116. help="Install packages as eggs, not 'flat', like pip normally "
  117. "does. This option is not about installing *from* eggs. "
  118. "(WARNING: Because this option overrides pip's normal install"
  119. " logic, requirements files may not behave as expected.)")
  120. cmd_opts.add_option(
  121. '--root',
  122. dest='root_path',
  123. metavar='dir',
  124. default=None,
  125. help="Install everything relative to this alternate root "
  126. "directory.")
  127. cmd_opts.add_option(
  128. '--prefix',
  129. dest='prefix_path',
  130. metavar='dir',
  131. default=None,
  132. help="Installation prefix where lib, bin and other top-level "
  133. "folders are placed")
  134. cmd_opts.add_option(
  135. "--compile",
  136. action="store_true",
  137. dest="compile",
  138. default=True,
  139. help="Compile py files to pyc",
  140. )
  141. cmd_opts.add_option(
  142. "--no-compile",
  143. action="store_false",
  144. dest="compile",
  145. help="Do not compile py files to pyc",
  146. )
  147. cmd_opts.add_option(cmdoptions.use_wheel())
  148. cmd_opts.add_option(cmdoptions.no_use_wheel())
  149. cmd_opts.add_option(cmdoptions.no_binary())
  150. cmd_opts.add_option(cmdoptions.only_binary())
  151. cmd_opts.add_option(cmdoptions.pre())
  152. cmd_opts.add_option(cmdoptions.no_clean())
  153. cmd_opts.add_option(cmdoptions.require_hashes())
  154. index_opts = cmdoptions.make_option_group(
  155. cmdoptions.index_group,
  156. self.parser,
  157. )
  158. self.parser.insert_option_group(0, index_opts)
  159. self.parser.insert_option_group(0, cmd_opts)
  160. def run(self, options, args):
  161. cmdoptions.resolve_wheel_no_use_binary(options)
  162. cmdoptions.check_install_build_global(options)
  163. if options.as_egg:
  164. warnings.warn(
  165. "--egg has been deprecated and will be removed in the future. "
  166. "This flag is mutually exclusive with large parts of pip, and "
  167. "actually using it invalidates pip's ability to manage the "
  168. "installation process.",
  169. RemovedInPip10Warning,
  170. )
  171. if options.allow_external:
  172. warnings.warn(
  173. "--allow-external has been deprecated and will be removed in "
  174. "the future. Due to changes in the repository protocol, it no "
  175. "longer has any effect.",
  176. RemovedInPip10Warning,
  177. )
  178. if options.allow_all_external:
  179. warnings.warn(
  180. "--allow-all-external has been deprecated and will be removed "
  181. "in the future. Due to changes in the repository protocol, it "
  182. "no longer has any effect.",
  183. RemovedInPip10Warning,
  184. )
  185. if options.allow_unverified:
  186. warnings.warn(
  187. "--allow-unverified has been deprecated and will be removed "
  188. "in the future. Due to changes in the repository protocol, it "
  189. "no longer has any effect.",
  190. RemovedInPip10Warning,
  191. )
  192. if options.download_dir:
  193. warnings.warn(
  194. "pip install --download has been deprecated and will be "
  195. "removed in the future. Pip now has a download command that "
  196. "should be used instead.",
  197. RemovedInPip10Warning,
  198. )
  199. options.ignore_installed = True
  200. if options.build_dir:
  201. options.build_dir = os.path.abspath(options.build_dir)
  202. options.src_dir = os.path.abspath(options.src_dir)
  203. install_options = options.install_options or []
  204. if options.use_user_site:
  205. if options.prefix_path:
  206. raise CommandError(
  207. "Can not combine '--user' and '--prefix' as they imply "
  208. "different installation locations"
  209. )
  210. if virtualenv_no_global():
  211. raise InstallationError(
  212. "Can not perform a '--user' install. User site-packages "
  213. "are not visible in this virtualenv."
  214. )
  215. install_options.append('--user')
  216. install_options.append('--prefix=')
  217. temp_target_dir = None
  218. if options.target_dir:
  219. options.ignore_installed = True
  220. temp_target_dir = tempfile.mkdtemp()
  221. options.target_dir = os.path.abspath(options.target_dir)
  222. if (os.path.exists(options.target_dir) and not
  223. os.path.isdir(options.target_dir)):
  224. raise CommandError(
  225. "Target path exists but is not a directory, will not "
  226. "continue."
  227. )
  228. install_options.append('--home=' + temp_target_dir)
  229. global_options = options.global_options or []
  230. with self._build_session(options) as session:
  231. finder = self._build_package_finder(options, session)
  232. build_delete = (not (options.no_clean or options.build_dir))
  233. wheel_cache = WheelCache(options.cache_dir, options.format_control)
  234. if options.cache_dir and not check_path_owner(options.cache_dir):
  235. logger.warning(
  236. "The directory '%s' or its parent directory is not owned "
  237. "by the current user and caching wheels has been "
  238. "disabled. check the permissions and owner of that "
  239. "directory. If executing pip with sudo, you may want "
  240. "sudo's -H flag.",
  241. options.cache_dir,
  242. )
  243. options.cache_dir = None
  244. with BuildDirectory(options.build_dir,
  245. delete=build_delete) as build_dir:
  246. requirement_set = RequirementSet(
  247. build_dir=build_dir,
  248. src_dir=options.src_dir,
  249. download_dir=options.download_dir,
  250. upgrade=options.upgrade,
  251. upgrade_strategy=options.upgrade_strategy,
  252. as_egg=options.as_egg,
  253. ignore_installed=options.ignore_installed,
  254. ignore_dependencies=options.ignore_dependencies,
  255. ignore_requires_python=options.ignore_requires_python,
  256. force_reinstall=options.force_reinstall,
  257. use_user_site=options.use_user_site,
  258. target_dir=temp_target_dir,
  259. session=session,
  260. pycompile=options.compile,
  261. isolated=options.isolated_mode,
  262. wheel_cache=wheel_cache,
  263. require_hashes=options.require_hashes,
  264. )
  265. self.populate_requirement_set(
  266. requirement_set, args, options, finder, session, self.name,
  267. wheel_cache
  268. )
  269. if not requirement_set.has_requirements:
  270. return
  271. try:
  272. if (options.download_dir or not wheel or not
  273. options.cache_dir):
  274. # on -d don't do complex things like building
  275. # wheels, and don't try to build wheels when wheel is
  276. # not installed.
  277. requirement_set.prepare_files(finder)
  278. else:
  279. # build wheels before install.
  280. wb = WheelBuilder(
  281. requirement_set,
  282. finder,
  283. build_options=[],
  284. global_options=[],
  285. )
  286. # Ignore the result: a failed wheel will be
  287. # installed from the sdist/vcs whatever.
  288. wb.build(autobuilding=True)
  289. if not options.download_dir:
  290. requirement_set.install(
  291. install_options,
  292. global_options,
  293. root=options.root_path,
  294. prefix=options.prefix_path,
  295. )
  296. possible_lib_locations = get_lib_location_guesses(
  297. user=options.use_user_site,
  298. home=temp_target_dir,
  299. root=options.root_path,
  300. prefix=options.prefix_path,
  301. isolated=options.isolated_mode,
  302. )
  303. reqs = sorted(
  304. requirement_set.successfully_installed,
  305. key=operator.attrgetter('name'))
  306. items = []
  307. for req in reqs:
  308. item = req.name
  309. try:
  310. installed_version = get_installed_version(
  311. req.name, possible_lib_locations
  312. )
  313. if installed_version:
  314. item += '-' + installed_version
  315. except Exception:
  316. pass
  317. items.append(item)
  318. installed = ' '.join(items)
  319. if installed:
  320. logger.info('Successfully installed %s', installed)
  321. else:
  322. downloaded = ' '.join([
  323. req.name
  324. for req in requirement_set.successfully_downloaded
  325. ])
  326. if downloaded:
  327. logger.info(
  328. 'Successfully downloaded %s', downloaded
  329. )
  330. except PreviousBuildDirError:
  331. options.no_clean = True
  332. raise
  333. finally:
  334. # Clean up
  335. if not options.no_clean:
  336. requirement_set.cleanup_files()
  337. if options.target_dir:
  338. ensure_dir(options.target_dir)
  339. # Checking both purelib and platlib directories for installed
  340. # packages to be moved to target directory
  341. lib_dir_list = []
  342. purelib_dir = distutils_scheme('', home=temp_target_dir)['purelib']
  343. platlib_dir = distutils_scheme('', home=temp_target_dir)['platlib']
  344. if os.path.exists(purelib_dir):
  345. lib_dir_list.append(purelib_dir)
  346. if os.path.exists(platlib_dir) and platlib_dir != purelib_dir:
  347. lib_dir_list.append(platlib_dir)
  348. for lib_dir in lib_dir_list:
  349. for item in os.listdir(lib_dir):
  350. target_item_dir = os.path.join(options.target_dir, item)
  351. if os.path.exists(target_item_dir):
  352. if not options.upgrade:
  353. logger.warning(
  354. 'Target directory %s already exists. Specify '
  355. '--upgrade to force replacement.',
  356. target_item_dir
  357. )
  358. continue
  359. if os.path.islink(target_item_dir):
  360. logger.warning(
  361. 'Target directory %s already exists and is '
  362. 'a link. Pip will not automatically replace '
  363. 'links, please remove if replacement is '
  364. 'desired.',
  365. target_item_dir
  366. )
  367. continue
  368. if os.path.isdir(target_item_dir):
  369. shutil.rmtree(target_item_dir)
  370. else:
  371. os.remove(target_item_dir)
  372. shutil.move(
  373. os.path.join(lib_dir, item),
  374. target_item_dir
  375. )
  376. shutil.rmtree(temp_target_dir)
  377. return requirement_set
  378. def get_lib_location_guesses(*args, **kwargs):
  379. scheme = distutils_scheme('', *args, **kwargs)
  380. return [scheme['purelib'], scheme['platlib']]