req_set.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798
  1. from __future__ import absolute_import
  2. from collections import defaultdict
  3. from itertools import chain
  4. import logging
  5. import os
  6. from pip._vendor import pkg_resources
  7. from pip._vendor import requests
  8. from pip.compat import expanduser
  9. from pip.download import (is_file_url, is_dir_url, is_vcs_url, url_to_path,
  10. unpack_url)
  11. from pip.exceptions import (InstallationError, BestVersionAlreadyInstalled,
  12. DistributionNotFound, PreviousBuildDirError,
  13. HashError, HashErrors, HashUnpinned,
  14. DirectoryUrlHashUnsupported, VcsHashUnsupported,
  15. UnsupportedPythonVersion)
  16. from pip.req.req_install import InstallRequirement
  17. from pip.utils import (
  18. display_path, dist_in_usersite, ensure_dir, normalize_path)
  19. from pip.utils.hashes import MissingHashes
  20. from pip.utils.logging import indent_log
  21. from pip.utils.packaging import check_dist_requires_python
  22. from pip.vcs import vcs
  23. from pip.wheel import Wheel
  24. logger = logging.getLogger(__name__)
  25. class Requirements(object):
  26. def __init__(self):
  27. self._keys = []
  28. self._dict = {}
  29. def keys(self):
  30. return self._keys
  31. def values(self):
  32. return [self._dict[key] for key in self._keys]
  33. def __contains__(self, item):
  34. return item in self._keys
  35. def __setitem__(self, key, value):
  36. if key not in self._keys:
  37. self._keys.append(key)
  38. self._dict[key] = value
  39. def __getitem__(self, key):
  40. return self._dict[key]
  41. def __repr__(self):
  42. values = ['%s: %s' % (repr(k), repr(self[k])) for k in self.keys()]
  43. return 'Requirements({%s})' % ', '.join(values)
  44. class DistAbstraction(object):
  45. """Abstracts out the wheel vs non-wheel prepare_files logic.
  46. The requirements for anything installable are as follows:
  47. - we must be able to determine the requirement name
  48. (or we can't correctly handle the non-upgrade case).
  49. - we must be able to generate a list of run-time dependencies
  50. without installing any additional packages (or we would
  51. have to either burn time by doing temporary isolated installs
  52. or alternatively violate pips 'don't start installing unless
  53. all requirements are available' rule - neither of which are
  54. desirable).
  55. - for packages with setup requirements, we must also be able
  56. to determine their requirements without installing additional
  57. packages (for the same reason as run-time dependencies)
  58. - we must be able to create a Distribution object exposing the
  59. above metadata.
  60. """
  61. def __init__(self, req_to_install):
  62. self.req_to_install = req_to_install
  63. def dist(self, finder):
  64. """Return a setuptools Dist object."""
  65. raise NotImplementedError(self.dist)
  66. def prep_for_dist(self):
  67. """Ensure that we can get a Dist for this requirement."""
  68. raise NotImplementedError(self.dist)
  69. def make_abstract_dist(req_to_install):
  70. """Factory to make an abstract dist object.
  71. Preconditions: Either an editable req with a source_dir, or satisfied_by or
  72. a wheel link, or a non-editable req with a source_dir.
  73. :return: A concrete DistAbstraction.
  74. """
  75. if req_to_install.editable:
  76. return IsSDist(req_to_install)
  77. elif req_to_install.link and req_to_install.link.is_wheel:
  78. return IsWheel(req_to_install)
  79. else:
  80. return IsSDist(req_to_install)
  81. class IsWheel(DistAbstraction):
  82. def dist(self, finder):
  83. return list(pkg_resources.find_distributions(
  84. self.req_to_install.source_dir))[0]
  85. def prep_for_dist(self):
  86. # FIXME:https://github.com/pypa/pip/issues/1112
  87. pass
  88. class IsSDist(DistAbstraction):
  89. def dist(self, finder):
  90. dist = self.req_to_install.get_dist()
  91. # FIXME: shouldn't be globally added:
  92. if dist.has_metadata('dependency_links.txt'):
  93. finder.add_dependency_links(
  94. dist.get_metadata_lines('dependency_links.txt')
  95. )
  96. return dist
  97. def prep_for_dist(self):
  98. self.req_to_install.run_egg_info()
  99. self.req_to_install.assert_source_matches_version()
  100. class Installed(DistAbstraction):
  101. def dist(self, finder):
  102. return self.req_to_install.satisfied_by
  103. def prep_for_dist(self):
  104. pass
  105. class RequirementSet(object):
  106. def __init__(self, build_dir, src_dir, download_dir, upgrade=False,
  107. upgrade_strategy=None, ignore_installed=False, as_egg=False,
  108. target_dir=None, ignore_dependencies=False,
  109. force_reinstall=False, use_user_site=False, session=None,
  110. pycompile=True, isolated=False, wheel_download_dir=None,
  111. wheel_cache=None, require_hashes=False,
  112. ignore_requires_python=False):
  113. """Create a RequirementSet.
  114. :param wheel_download_dir: Where still-packed .whl files should be
  115. written to. If None they are written to the download_dir parameter.
  116. Separate to download_dir to permit only keeping wheel archives for
  117. pip wheel.
  118. :param download_dir: Where still packed archives should be written to.
  119. If None they are not saved, and are deleted immediately after
  120. unpacking.
  121. :param wheel_cache: The pip wheel cache, for passing to
  122. InstallRequirement.
  123. """
  124. if session is None:
  125. raise TypeError(
  126. "RequirementSet() missing 1 required keyword argument: "
  127. "'session'"
  128. )
  129. self.build_dir = build_dir
  130. self.src_dir = src_dir
  131. # XXX: download_dir and wheel_download_dir overlap semantically and may
  132. # be combined if we're willing to have non-wheel archives present in
  133. # the wheelhouse output by 'pip wheel'.
  134. self.download_dir = download_dir
  135. self.upgrade = upgrade
  136. self.upgrade_strategy = upgrade_strategy
  137. self.ignore_installed = ignore_installed
  138. self.force_reinstall = force_reinstall
  139. self.requirements = Requirements()
  140. # Mapping of alias: real_name
  141. self.requirement_aliases = {}
  142. self.unnamed_requirements = []
  143. self.ignore_dependencies = ignore_dependencies
  144. self.ignore_requires_python = ignore_requires_python
  145. self.successfully_downloaded = []
  146. self.successfully_installed = []
  147. self.reqs_to_cleanup = []
  148. self.as_egg = as_egg
  149. self.use_user_site = use_user_site
  150. self.target_dir = target_dir # set from --target option
  151. self.session = session
  152. self.pycompile = pycompile
  153. self.isolated = isolated
  154. if wheel_download_dir:
  155. wheel_download_dir = normalize_path(wheel_download_dir)
  156. self.wheel_download_dir = wheel_download_dir
  157. self._wheel_cache = wheel_cache
  158. self.require_hashes = require_hashes
  159. # Maps from install_req -> dependencies_of_install_req
  160. self._dependencies = defaultdict(list)
  161. def __str__(self):
  162. reqs = [req for req in self.requirements.values()
  163. if not req.comes_from]
  164. reqs.sort(key=lambda req: req.name.lower())
  165. return ' '.join([str(req.req) for req in reqs])
  166. def __repr__(self):
  167. reqs = [req for req in self.requirements.values()]
  168. reqs.sort(key=lambda req: req.name.lower())
  169. reqs_str = ', '.join([str(req.req) for req in reqs])
  170. return ('<%s object; %d requirement(s): %s>'
  171. % (self.__class__.__name__, len(reqs), reqs_str))
  172. def add_requirement(self, install_req, parent_req_name=None,
  173. extras_requested=None):
  174. """Add install_req as a requirement to install.
  175. :param parent_req_name: The name of the requirement that needed this
  176. added. The name is used because when multiple unnamed requirements
  177. resolve to the same name, we could otherwise end up with dependency
  178. links that point outside the Requirements set. parent_req must
  179. already be added. Note that None implies that this is a user
  180. supplied requirement, vs an inferred one.
  181. :param extras_requested: an iterable of extras used to evaluate the
  182. environement markers.
  183. :return: Additional requirements to scan. That is either [] if
  184. the requirement is not applicable, or [install_req] if the
  185. requirement is applicable and has just been added.
  186. """
  187. name = install_req.name
  188. if not install_req.match_markers(extras_requested):
  189. logger.warning("Ignoring %s: markers '%s' don't match your "
  190. "environment", install_req.name,
  191. install_req.markers)
  192. return []
  193. # This check has to come after we filter requirements with the
  194. # environment markers.
  195. if install_req.link and install_req.link.is_wheel:
  196. wheel = Wheel(install_req.link.filename)
  197. if not wheel.supported():
  198. raise InstallationError(
  199. "%s is not a supported wheel on this platform." %
  200. wheel.filename
  201. )
  202. install_req.as_egg = self.as_egg
  203. install_req.use_user_site = self.use_user_site
  204. install_req.target_dir = self.target_dir
  205. install_req.pycompile = self.pycompile
  206. install_req.is_direct = (parent_req_name is None)
  207. if not name:
  208. # url or path requirement w/o an egg fragment
  209. self.unnamed_requirements.append(install_req)
  210. return [install_req]
  211. else:
  212. try:
  213. existing_req = self.get_requirement(name)
  214. except KeyError:
  215. existing_req = None
  216. if (parent_req_name is None and existing_req and not
  217. existing_req.constraint and
  218. existing_req.extras == install_req.extras and not
  219. existing_req.req.specifier == install_req.req.specifier):
  220. raise InstallationError(
  221. 'Double requirement given: %s (already in %s, name=%r)'
  222. % (install_req, existing_req, name))
  223. if not existing_req:
  224. # Add requirement
  225. self.requirements[name] = install_req
  226. # FIXME: what about other normalizations? E.g., _ vs. -?
  227. if name.lower() != name:
  228. self.requirement_aliases[name.lower()] = name
  229. result = [install_req]
  230. else:
  231. # Assume there's no need to scan, and that we've already
  232. # encountered this for scanning.
  233. result = []
  234. if not install_req.constraint and existing_req.constraint:
  235. if (install_req.link and not (existing_req.link and
  236. install_req.link.path == existing_req.link.path)):
  237. self.reqs_to_cleanup.append(install_req)
  238. raise InstallationError(
  239. "Could not satisfy constraints for '%s': "
  240. "installation from path or url cannot be "
  241. "constrained to a version" % name)
  242. # If we're now installing a constraint, mark the existing
  243. # object for real installation.
  244. existing_req.constraint = False
  245. existing_req.extras = tuple(
  246. sorted(set(existing_req.extras).union(
  247. set(install_req.extras))))
  248. logger.debug("Setting %s extras to: %s",
  249. existing_req, existing_req.extras)
  250. # And now we need to scan this.
  251. result = [existing_req]
  252. # Canonicalise to the already-added object for the backref
  253. # check below.
  254. install_req = existing_req
  255. if parent_req_name:
  256. parent_req = self.get_requirement(parent_req_name)
  257. self._dependencies[parent_req].append(install_req)
  258. return result
  259. def has_requirement(self, project_name):
  260. name = project_name.lower()
  261. if (name in self.requirements and
  262. not self.requirements[name].constraint or
  263. name in self.requirement_aliases and
  264. not self.requirements[self.requirement_aliases[name]].constraint):
  265. return True
  266. return False
  267. @property
  268. def has_requirements(self):
  269. return list(req for req in self.requirements.values() if not
  270. req.constraint) or self.unnamed_requirements
  271. @property
  272. def is_download(self):
  273. if self.download_dir:
  274. self.download_dir = expanduser(self.download_dir)
  275. if os.path.exists(self.download_dir):
  276. return True
  277. else:
  278. logger.critical('Could not find download directory')
  279. raise InstallationError(
  280. "Could not find or access download directory '%s'"
  281. % display_path(self.download_dir))
  282. return False
  283. def get_requirement(self, project_name):
  284. for name in project_name, project_name.lower():
  285. if name in self.requirements:
  286. return self.requirements[name]
  287. if name in self.requirement_aliases:
  288. return self.requirements[self.requirement_aliases[name]]
  289. raise KeyError("No project with the name %r" % project_name)
  290. def uninstall(self, auto_confirm=False):
  291. for req in self.requirements.values():
  292. if req.constraint:
  293. continue
  294. req.uninstall(auto_confirm=auto_confirm)
  295. req.commit_uninstall()
  296. def prepare_files(self, finder):
  297. """
  298. Prepare process. Create temp directories, download and/or unpack files.
  299. """
  300. # make the wheelhouse
  301. if self.wheel_download_dir:
  302. ensure_dir(self.wheel_download_dir)
  303. # If any top-level requirement has a hash specified, enter
  304. # hash-checking mode, which requires hashes from all.
  305. root_reqs = self.unnamed_requirements + self.requirements.values()
  306. require_hashes = (self.require_hashes or
  307. any(req.has_hash_options for req in root_reqs))
  308. if require_hashes and self.as_egg:
  309. raise InstallationError(
  310. '--egg is not allowed with --require-hashes mode, since it '
  311. 'delegates dependency resolution to setuptools and could thus '
  312. 'result in installation of unhashed packages.')
  313. # Actually prepare the files, and collect any exceptions. Most hash
  314. # exceptions cannot be checked ahead of time, because
  315. # req.populate_link() needs to be called before we can make decisions
  316. # based on link type.
  317. discovered_reqs = []
  318. hash_errors = HashErrors()
  319. for req in chain(root_reqs, discovered_reqs):
  320. try:
  321. discovered_reqs.extend(self._prepare_file(
  322. finder,
  323. req,
  324. require_hashes=require_hashes,
  325. ignore_dependencies=self.ignore_dependencies))
  326. except HashError as exc:
  327. exc.req = req
  328. hash_errors.append(exc)
  329. if hash_errors:
  330. raise hash_errors
  331. def _is_upgrade_allowed(self, req):
  332. return self.upgrade and (
  333. self.upgrade_strategy == "eager" or (
  334. self.upgrade_strategy == "only-if-needed" and req.is_direct
  335. )
  336. )
  337. def _check_skip_installed(self, req_to_install, finder):
  338. """Check if req_to_install should be skipped.
  339. This will check if the req is installed, and whether we should upgrade
  340. or reinstall it, taking into account all the relevant user options.
  341. After calling this req_to_install will only have satisfied_by set to
  342. None if the req_to_install is to be upgraded/reinstalled etc. Any
  343. other value will be a dist recording the current thing installed that
  344. satisfies the requirement.
  345. Note that for vcs urls and the like we can't assess skipping in this
  346. routine - we simply identify that we need to pull the thing down,
  347. then later on it is pulled down and introspected to assess upgrade/
  348. reinstalls etc.
  349. :return: A text reason for why it was skipped, or None.
  350. """
  351. # Check whether to upgrade/reinstall this req or not.
  352. req_to_install.check_if_exists()
  353. if req_to_install.satisfied_by:
  354. upgrade_allowed = self._is_upgrade_allowed(req_to_install)
  355. # Is the best version is installed.
  356. best_installed = False
  357. if upgrade_allowed:
  358. # For link based requirements we have to pull the
  359. # tree down and inspect to assess the version #, so
  360. # its handled way down.
  361. if not (self.force_reinstall or req_to_install.link):
  362. try:
  363. finder.find_requirement(
  364. req_to_install, upgrade_allowed)
  365. except BestVersionAlreadyInstalled:
  366. best_installed = True
  367. except DistributionNotFound:
  368. # No distribution found, so we squash the
  369. # error - it will be raised later when we
  370. # re-try later to do the install.
  371. # Why don't we just raise here?
  372. pass
  373. if not best_installed:
  374. # don't uninstall conflict if user install and
  375. # conflict is not user install
  376. if not (self.use_user_site and not
  377. dist_in_usersite(req_to_install.satisfied_by)):
  378. req_to_install.conflicts_with = \
  379. req_to_install.satisfied_by
  380. req_to_install.satisfied_by = None
  381. # Figure out a nice message to say why we're skipping this.
  382. if best_installed:
  383. skip_reason = 'already up-to-date'
  384. elif self.upgrade_strategy == "only-if-needed":
  385. skip_reason = 'not upgraded as not directly required'
  386. else:
  387. skip_reason = 'already satisfied'
  388. return skip_reason
  389. else:
  390. return None
  391. def _prepare_file(self,
  392. finder,
  393. req_to_install,
  394. require_hashes=False,
  395. ignore_dependencies=False):
  396. """Prepare a single requirements file.
  397. :return: A list of additional InstallRequirements to also install.
  398. """
  399. # Tell user what we are doing for this requirement:
  400. # obtain (editable), skipping, processing (local url), collecting
  401. # (remote url or package name)
  402. if req_to_install.constraint or req_to_install.prepared:
  403. return []
  404. req_to_install.prepared = True
  405. # ###################### #
  406. # # print log messages # #
  407. # ###################### #
  408. if req_to_install.editable:
  409. logger.info('Obtaining %s', req_to_install)
  410. else:
  411. # satisfied_by is only evaluated by calling _check_skip_installed,
  412. # so it must be None here.
  413. assert req_to_install.satisfied_by is None
  414. if not self.ignore_installed:
  415. skip_reason = self._check_skip_installed(
  416. req_to_install, finder)
  417. if req_to_install.satisfied_by:
  418. assert skip_reason is not None, (
  419. '_check_skip_installed returned None but '
  420. 'req_to_install.satisfied_by is set to %r'
  421. % (req_to_install.satisfied_by,))
  422. logger.info(
  423. 'Requirement %s: %s', skip_reason,
  424. req_to_install)
  425. else:
  426. if (req_to_install.link and
  427. req_to_install.link.scheme == 'file'):
  428. path = url_to_path(req_to_install.link.url)
  429. logger.info('Processing %s', display_path(path))
  430. else:
  431. logger.info('Collecting %s', req_to_install)
  432. with indent_log():
  433. # ################################ #
  434. # # vcs update or unpack archive # #
  435. # ################################ #
  436. if req_to_install.editable:
  437. if require_hashes:
  438. raise InstallationError(
  439. 'The editable requirement %s cannot be installed when '
  440. 'requiring hashes, because there is no single file to '
  441. 'hash.' % req_to_install)
  442. req_to_install.ensure_has_source_dir(self.src_dir)
  443. req_to_install.update_editable(not self.is_download)
  444. abstract_dist = make_abstract_dist(req_to_install)
  445. abstract_dist.prep_for_dist()
  446. if self.is_download:
  447. req_to_install.archive(self.download_dir)
  448. req_to_install.check_if_exists()
  449. elif req_to_install.satisfied_by:
  450. if require_hashes:
  451. logger.debug(
  452. 'Since it is already installed, we are trusting this '
  453. 'package without checking its hash. To ensure a '
  454. 'completely repeatable environment, install into an '
  455. 'empty virtualenv.')
  456. abstract_dist = Installed(req_to_install)
  457. else:
  458. # @@ if filesystem packages are not marked
  459. # editable in a req, a non deterministic error
  460. # occurs when the script attempts to unpack the
  461. # build directory
  462. req_to_install.ensure_has_source_dir(self.build_dir)
  463. # If a checkout exists, it's unwise to keep going. version
  464. # inconsistencies are logged later, but do not fail the
  465. # installation.
  466. # FIXME: this won't upgrade when there's an existing
  467. # package unpacked in `req_to_install.source_dir`
  468. if os.path.exists(
  469. os.path.join(req_to_install.source_dir, 'setup.py')):
  470. raise PreviousBuildDirError(
  471. "pip can't proceed with requirements '%s' due to a"
  472. " pre-existing build directory (%s). This is "
  473. "likely due to a previous installation that failed"
  474. ". pip is being responsible and not assuming it "
  475. "can delete this. Please delete it and try again."
  476. % (req_to_install, req_to_install.source_dir)
  477. )
  478. req_to_install.populate_link(
  479. finder,
  480. self._is_upgrade_allowed(req_to_install),
  481. require_hashes
  482. )
  483. # We can't hit this spot and have populate_link return None.
  484. # req_to_install.satisfied_by is None here (because we're
  485. # guarded) and upgrade has no impact except when satisfied_by
  486. # is not None.
  487. # Then inside find_requirement existing_applicable -> False
  488. # If no new versions are found, DistributionNotFound is raised,
  489. # otherwise a result is guaranteed.
  490. assert req_to_install.link
  491. link = req_to_install.link
  492. # Now that we have the real link, we can tell what kind of
  493. # requirements we have and raise some more informative errors
  494. # than otherwise. (For example, we can raise VcsHashUnsupported
  495. # for a VCS URL rather than HashMissing.)
  496. if require_hashes:
  497. # We could check these first 2 conditions inside
  498. # unpack_url and save repetition of conditions, but then
  499. # we would report less-useful error messages for
  500. # unhashable requirements, complaining that there's no
  501. # hash provided.
  502. if is_vcs_url(link):
  503. raise VcsHashUnsupported()
  504. elif is_file_url(link) and is_dir_url(link):
  505. raise DirectoryUrlHashUnsupported()
  506. if (not req_to_install.original_link and
  507. not req_to_install.is_pinned):
  508. # Unpinned packages are asking for trouble when a new
  509. # version is uploaded. This isn't a security check, but
  510. # it saves users a surprising hash mismatch in the
  511. # future.
  512. #
  513. # file:/// URLs aren't pinnable, so don't complain
  514. # about them not being pinned.
  515. raise HashUnpinned()
  516. hashes = req_to_install.hashes(
  517. trust_internet=not require_hashes)
  518. if require_hashes and not hashes:
  519. # Known-good hashes are missing for this requirement, so
  520. # shim it with a facade object that will provoke hash
  521. # computation and then raise a HashMissing exception
  522. # showing the user what the hash should be.
  523. hashes = MissingHashes()
  524. try:
  525. download_dir = self.download_dir
  526. # We always delete unpacked sdists after pip ran.
  527. autodelete_unpacked = True
  528. if req_to_install.link.is_wheel \
  529. and self.wheel_download_dir:
  530. # when doing 'pip wheel` we download wheels to a
  531. # dedicated dir.
  532. download_dir = self.wheel_download_dir
  533. if req_to_install.link.is_wheel:
  534. if download_dir:
  535. # When downloading, we only unpack wheels to get
  536. # metadata.
  537. autodelete_unpacked = True
  538. else:
  539. # When installing a wheel, we use the unpacked
  540. # wheel.
  541. autodelete_unpacked = False
  542. unpack_url(
  543. req_to_install.link, req_to_install.source_dir,
  544. download_dir, autodelete_unpacked,
  545. session=self.session, hashes=hashes)
  546. except requests.HTTPError as exc:
  547. logger.critical(
  548. 'Could not install requirement %s because '
  549. 'of error %s',
  550. req_to_install,
  551. exc,
  552. )
  553. raise InstallationError(
  554. 'Could not install requirement %s because '
  555. 'of HTTP error %s for URL %s' %
  556. (req_to_install, exc, req_to_install.link)
  557. )
  558. abstract_dist = make_abstract_dist(req_to_install)
  559. abstract_dist.prep_for_dist()
  560. if self.is_download:
  561. # Make a .zip of the source_dir we already created.
  562. if req_to_install.link.scheme in vcs.all_schemes:
  563. req_to_install.archive(self.download_dir)
  564. # req_to_install.req is only avail after unpack for URL
  565. # pkgs repeat check_if_exists to uninstall-on-upgrade
  566. # (#14)
  567. if not self.ignore_installed:
  568. req_to_install.check_if_exists()
  569. if req_to_install.satisfied_by:
  570. if self.upgrade or self.ignore_installed:
  571. # don't uninstall conflict if user install and
  572. # conflict is not user install
  573. if not (self.use_user_site and not
  574. dist_in_usersite(
  575. req_to_install.satisfied_by)):
  576. req_to_install.conflicts_with = \
  577. req_to_install.satisfied_by
  578. req_to_install.satisfied_by = None
  579. else:
  580. logger.info(
  581. 'Requirement already satisfied (use '
  582. '--upgrade to upgrade): %s',
  583. req_to_install,
  584. )
  585. # ###################### #
  586. # # parse dependencies # #
  587. # ###################### #
  588. dist = abstract_dist.dist(finder)
  589. try:
  590. check_dist_requires_python(dist)
  591. except UnsupportedPythonVersion as e:
  592. if self.ignore_requires_python:
  593. logger.warning(e.args[0])
  594. else:
  595. req_to_install.remove_temporary_source()
  596. raise
  597. more_reqs = []
  598. def add_req(subreq, extras_requested):
  599. sub_install_req = InstallRequirement(
  600. str(subreq),
  601. req_to_install,
  602. isolated=self.isolated,
  603. wheel_cache=self._wheel_cache,
  604. )
  605. more_reqs.extend(self.add_requirement(
  606. sub_install_req, req_to_install.name,
  607. extras_requested=extras_requested))
  608. # We add req_to_install before its dependencies, so that we
  609. # can refer to it when adding dependencies.
  610. if not self.has_requirement(req_to_install.name):
  611. # 'unnamed' requirements will get added here
  612. self.add_requirement(req_to_install, None)
  613. if not ignore_dependencies:
  614. if (req_to_install.extras):
  615. logger.debug(
  616. "Installing extra requirements: %r",
  617. ','.join(req_to_install.extras),
  618. )
  619. missing_requested = sorted(
  620. set(req_to_install.extras) - set(dist.extras)
  621. )
  622. for missing in missing_requested:
  623. logger.warning(
  624. '%s does not provide the extra \'%s\'',
  625. dist, missing
  626. )
  627. available_requested = sorted(
  628. set(dist.extras) & set(req_to_install.extras)
  629. )
  630. for subreq in dist.requires(available_requested):
  631. add_req(subreq, extras_requested=available_requested)
  632. # cleanup tmp src
  633. self.reqs_to_cleanup.append(req_to_install)
  634. if not req_to_install.editable and not req_to_install.satisfied_by:
  635. # XXX: --no-install leads this to report 'Successfully
  636. # downloaded' for only non-editable reqs, even though we took
  637. # action on them.
  638. self.successfully_downloaded.append(req_to_install)
  639. return more_reqs
  640. def cleanup_files(self):
  641. """Clean up files, remove builds."""
  642. logger.debug('Cleaning up...')
  643. with indent_log():
  644. for req in self.reqs_to_cleanup:
  645. req.remove_temporary_source()
  646. def _to_install(self):
  647. """Create the installation order.
  648. The installation order is topological - requirements are installed
  649. before the requiring thing. We break cycles at an arbitrary point,
  650. and make no other guarantees.
  651. """
  652. # The current implementation, which we may change at any point
  653. # installs the user specified things in the order given, except when
  654. # dependencies must come earlier to achieve topological order.
  655. order = []
  656. ordered_reqs = set()
  657. def schedule(req):
  658. if req.satisfied_by or req in ordered_reqs:
  659. return
  660. if req.constraint:
  661. return
  662. ordered_reqs.add(req)
  663. for dep in self._dependencies[req]:
  664. schedule(dep)
  665. order.append(req)
  666. for install_req in self.requirements.values():
  667. schedule(install_req)
  668. return order
  669. def install(self, install_options, global_options=(), *args, **kwargs):
  670. """
  671. Install everything in this set (after having downloaded and unpacked
  672. the packages)
  673. """
  674. to_install = self._to_install()
  675. if to_install:
  676. logger.info(
  677. 'Installing collected packages: %s',
  678. ', '.join([req.name for req in to_install]),
  679. )
  680. with indent_log():
  681. for requirement in to_install:
  682. if requirement.conflicts_with:
  683. logger.info(
  684. 'Found existing installation: %s',
  685. requirement.conflicts_with,
  686. )
  687. with indent_log():
  688. requirement.uninstall(auto_confirm=True)
  689. try:
  690. requirement.install(
  691. install_options,
  692. global_options,
  693. *args,
  694. **kwargs
  695. )
  696. except:
  697. # if install did not succeed, rollback previous uninstall
  698. if (requirement.conflicts_with and not
  699. requirement.install_succeeded):
  700. requirement.rollback_uninstall()
  701. raise
  702. else:
  703. if (requirement.conflicts_with and
  704. requirement.install_succeeded):
  705. requirement.commit_uninstall()
  706. requirement.remove_temporary_source()
  707. self.successfully_installed = to_install