req_set.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. from __future__ import absolute_import
  2. from collections import defaultdict
  3. import functools
  4. import itertools
  5. import logging
  6. import os
  7. from pip._vendor import pkg_resources
  8. from pip._vendor import requests
  9. from pip.download import (url_to_path, unpack_url)
  10. from pip.exceptions import (InstallationError, BestVersionAlreadyInstalled,
  11. DistributionNotFound, PreviousBuildDirError)
  12. from pip.locations import (PIP_DELETE_MARKER_FILENAME, build_prefix)
  13. from pip.req.req_install import InstallRequirement
  14. from pip.utils import (display_path, rmtree, dist_in_usersite, normalize_path)
  15. from pip.utils.logging import indent_log
  16. from pip.vcs import vcs
  17. logger = logging.getLogger(__name__)
  18. class Requirements(object):
  19. def __init__(self):
  20. self._keys = []
  21. self._dict = {}
  22. def keys(self):
  23. return self._keys
  24. def values(self):
  25. return [self._dict[key] for key in self._keys]
  26. def __contains__(self, item):
  27. return item in self._keys
  28. def __setitem__(self, key, value):
  29. if key not in self._keys:
  30. self._keys.append(key)
  31. self._dict[key] = value
  32. def __getitem__(self, key):
  33. return self._dict[key]
  34. def __repr__(self):
  35. values = ['%s: %s' % (repr(k), repr(self[k])) for k in self.keys()]
  36. return 'Requirements({%s})' % ', '.join(values)
  37. class DistAbstraction(object):
  38. """Abstracts out the wheel vs non-wheel prepare_files logic.
  39. The requirements for anything installable are as follows:
  40. - we must be able to determine the requirement name
  41. (or we can't correctly handle the non-upgrade case).
  42. - we must be able to generate a list of run-time dependencies
  43. without installing any additional packages (or we would
  44. have to either burn time by doing temporary isolated installs
  45. or alternatively violate pips 'don't start installing unless
  46. all requirements are available' rule - neither of which are
  47. desirable).
  48. - for packages with setup requirements, we must also be able
  49. to determine their requirements without installing additional
  50. packages (for the same reason as run-time dependencies)
  51. - we must be able to create a Distribution object exposing the
  52. above metadata.
  53. """
  54. def __init__(self, req_to_install):
  55. self.req_to_install = req_to_install
  56. def dist(self, finder):
  57. """Return a setuptools Dist object."""
  58. raise NotImplementedError(self.dist)
  59. def prep_for_dist(self):
  60. """Ensure that we can get a Dist for this requirement."""
  61. raise NotImplementedError(self.dist)
  62. def make_abstract_dist(req_to_install):
  63. """Factory to make an abstract dist object.
  64. Preconditions: Either an editable req with a source_dir, or satisfied_by or
  65. a wheel link, or a non-editable req with a source_dir.
  66. :return: A concrete DistAbstraction.
  67. """
  68. if req_to_install.editable:
  69. return IsSDist(req_to_install)
  70. elif req_to_install.link and req_to_install.link.is_wheel:
  71. return IsWheel(req_to_install)
  72. else:
  73. return IsSDist(req_to_install)
  74. class IsWheel(DistAbstraction):
  75. def dist(self, finder):
  76. return list(pkg_resources.find_distributions(
  77. self.req_to_install.source_dir))[0]
  78. def prep_for_dist(self):
  79. # FIXME:https://github.com/pypa/pip/issues/1112
  80. pass
  81. class IsSDist(DistAbstraction):
  82. def dist(self, finder):
  83. dist = self.req_to_install.get_dist()
  84. # FIXME: shouldn't be globally added:
  85. if dist.has_metadata('dependency_links.txt'):
  86. finder.add_dependency_links(
  87. dist.get_metadata_lines('dependency_links.txt')
  88. )
  89. return dist
  90. def prep_for_dist(self):
  91. self.req_to_install.run_egg_info()
  92. self.req_to_install.assert_source_matches_version()
  93. class Installed(DistAbstraction):
  94. def dist(self, finder):
  95. return self.req_to_install.satisfied_by
  96. def prep_for_dist(self):
  97. pass
  98. class RequirementSet(object):
  99. def __init__(self, build_dir, src_dir, download_dir, upgrade=False,
  100. ignore_installed=False, as_egg=False, target_dir=None,
  101. ignore_dependencies=False, force_reinstall=False,
  102. use_user_site=False, session=None, pycompile=True,
  103. isolated=False, wheel_download_dir=None):
  104. if session is None:
  105. raise TypeError(
  106. "RequirementSet() missing 1 required keyword argument: "
  107. "'session'"
  108. )
  109. self.build_dir = build_dir
  110. self.src_dir = src_dir
  111. # XXX: download_dir and wheel_download_dir overlap semantically and may
  112. # be combinable.
  113. self.download_dir = download_dir
  114. self.upgrade = upgrade
  115. self.ignore_installed = ignore_installed
  116. self.force_reinstall = force_reinstall
  117. self.requirements = Requirements()
  118. # Mapping of alias: real_name
  119. self.requirement_aliases = {}
  120. self.unnamed_requirements = []
  121. self.ignore_dependencies = ignore_dependencies
  122. self.successfully_downloaded = []
  123. self.successfully_installed = []
  124. self.reqs_to_cleanup = []
  125. self.as_egg = as_egg
  126. self.use_user_site = use_user_site
  127. self.target_dir = target_dir # set from --target option
  128. self.session = session
  129. self.pycompile = pycompile
  130. self.isolated = isolated
  131. if wheel_download_dir:
  132. wheel_download_dir = normalize_path(wheel_download_dir)
  133. self.wheel_download_dir = wheel_download_dir
  134. # Maps from install_req -> dependencies_of_install_req
  135. self._dependencies = defaultdict(list)
  136. def __str__(self):
  137. reqs = [req for req in self.requirements.values()
  138. if not req.comes_from]
  139. reqs.sort(key=lambda req: req.name.lower())
  140. return ' '.join([str(req.req) for req in reqs])
  141. def __repr__(self):
  142. reqs = [req for req in self.requirements.values()]
  143. reqs.sort(key=lambda req: req.name.lower())
  144. reqs_str = ', '.join([str(req.req) for req in reqs])
  145. return ('<%s object; %d requirement(s): %s>'
  146. % (self.__class__.__name__, len(reqs), reqs_str))
  147. def add_requirement(self, install_req, parent_req_name=None):
  148. """Add install_req as a requirement to install.
  149. :param parent_req_name: The name of the requirement that needed this
  150. added. The name is used because when multiple unnamed requirements
  151. resolve to the same name, we could otherwise end up with dependency
  152. links that point outside the Requirements set. parent_req must
  153. already be added. Note that None implies that this is a user
  154. supplied requirement, vs an inferred one.
  155. :return: Additional requirements to scan. That is either [] if
  156. the requirement is not applicable, or [install_req] if the
  157. requirement is applicable and has just been added.
  158. """
  159. name = install_req.name
  160. if ((not name or not self.has_requirement(name)) and not
  161. install_req.match_markers()):
  162. # Only log if we haven't already got install_req from somewhere.
  163. logger.debug("Ignore %s: markers %r don't match",
  164. install_req.name, install_req.markers)
  165. return []
  166. install_req.as_egg = self.as_egg
  167. install_req.use_user_site = self.use_user_site
  168. install_req.target_dir = self.target_dir
  169. install_req.pycompile = self.pycompile
  170. if not name:
  171. # url or path requirement w/o an egg fragment
  172. self.unnamed_requirements.append(install_req)
  173. return [install_req]
  174. else:
  175. if parent_req_name is None and self.has_requirement(name):
  176. raise InstallationError(
  177. 'Double requirement given: %s (already in %s, name=%r)'
  178. % (install_req, self.get_requirement(name), name))
  179. if not self.has_requirement(name):
  180. # Add requirement
  181. self.requirements[name] = install_req
  182. # FIXME: what about other normalizations? E.g., _ vs. -?
  183. if name.lower() != name:
  184. self.requirement_aliases[name.lower()] = name
  185. result = [install_req]
  186. else:
  187. # Canonicalise to the already-added object
  188. install_req = self.get_requirement(name)
  189. # No need to scan, this is a duplicate requirement.
  190. result = []
  191. if parent_req_name:
  192. parent_req = self.get_requirement(parent_req_name)
  193. self._dependencies[parent_req].append(install_req)
  194. return result
  195. def has_requirement(self, project_name):
  196. for name in project_name, project_name.lower():
  197. if name in self.requirements or name in self.requirement_aliases:
  198. return True
  199. return False
  200. @property
  201. def has_requirements(self):
  202. return list(self.requirements.values()) or self.unnamed_requirements
  203. @property
  204. def is_download(self):
  205. if self.download_dir:
  206. self.download_dir = os.path.expanduser(self.download_dir)
  207. if os.path.exists(self.download_dir):
  208. return True
  209. else:
  210. logger.critical('Could not find download directory')
  211. raise InstallationError(
  212. "Could not find or access download directory '%s'"
  213. % display_path(self.download_dir))
  214. return False
  215. def get_requirement(self, project_name):
  216. for name in project_name, project_name.lower():
  217. if name in self.requirements:
  218. return self.requirements[name]
  219. if name in self.requirement_aliases:
  220. return self.requirements[self.requirement_aliases[name]]
  221. raise KeyError("No project with the name %r" % project_name)
  222. def uninstall(self, auto_confirm=False):
  223. for req in self.requirements.values():
  224. req.uninstall(auto_confirm=auto_confirm)
  225. req.commit_uninstall()
  226. def _walk_req_to_install(self, handler):
  227. """Call handler for all pending reqs.
  228. :param handler: Handle a single requirement. Should take a requirement
  229. to install. Can optionally return an iterable of additional
  230. InstallRequirements to cover.
  231. """
  232. # The list() here is to avoid potential mutate-while-iterating bugs.
  233. discovered_reqs = []
  234. reqs = itertools.chain(
  235. list(self.unnamed_requirements), list(self.requirements.values()),
  236. discovered_reqs)
  237. for req_to_install in reqs:
  238. more_reqs = handler(req_to_install)
  239. if more_reqs:
  240. discovered_reqs.extend(more_reqs)
  241. def locate_files(self):
  242. """Remove in 7.0: used by --no-download"""
  243. self._walk_req_to_install(self._locate_file)
  244. def _locate_file(self, req_to_install):
  245. install_needed = True
  246. if not self.ignore_installed and not req_to_install.editable:
  247. req_to_install.check_if_exists()
  248. if req_to_install.satisfied_by:
  249. if self.upgrade:
  250. # don't uninstall conflict if user install and
  251. # conflict is not user install
  252. if not (self.use_user_site and
  253. not dist_in_usersite(
  254. req_to_install.satisfied_by
  255. )):
  256. req_to_install.conflicts_with = \
  257. req_to_install.satisfied_by
  258. req_to_install.satisfied_by = None
  259. else:
  260. install_needed = False
  261. logger.info(
  262. 'Requirement already satisfied (use --upgrade to '
  263. 'upgrade): %s',
  264. req_to_install,
  265. )
  266. if req_to_install.editable:
  267. if req_to_install.source_dir is None:
  268. req_to_install.source_dir = req_to_install.build_location(
  269. self.src_dir
  270. )
  271. elif install_needed:
  272. req_to_install.source_dir = req_to_install.build_location(
  273. self.build_dir,
  274. )
  275. if (req_to_install.source_dir is not None and not
  276. os.path.isdir(req_to_install.source_dir)):
  277. raise InstallationError(
  278. 'Could not install requirement %s because source folder %s'
  279. ' does not exist (perhaps --no-download was used without '
  280. 'first running an equivalent install with --no-install?)' %
  281. (req_to_install, req_to_install.source_dir)
  282. )
  283. def prepare_files(self, finder):
  284. """
  285. Prepare process. Create temp directories, download and/or unpack files.
  286. """
  287. self._walk_req_to_install(
  288. functools.partial(self._prepare_file, finder))
  289. def _check_skip_installed(self, req_to_install, finder):
  290. """Check if req_to_install should be skipped.
  291. This will check if the req is installed, and whether we should upgrade
  292. or reinstall it, taking into account all the relevant user options.
  293. After calling this req_to_install will only have satisfied_by set to
  294. None if the req_to_install is to be upgraded/reinstalled etc. Any
  295. other value will be a dist recording the current thing installed that
  296. satisfies the requirement.
  297. Note that for vcs urls and the like we can't assess skipping in this
  298. routine - we simply identify that we need to pull the thing down,
  299. then later on it is pulled down and introspected to assess upgrade/
  300. reinstalls etc.
  301. :return: A text reason for why it was skipped, or None.
  302. """
  303. # Check whether to upgrade/reinstall this req or not.
  304. req_to_install.check_if_exists()
  305. if req_to_install.satisfied_by:
  306. skip_reason = 'satisfied (use --upgrade to upgrade)'
  307. if self.upgrade:
  308. best_installed = False
  309. # For link based requirements we have to pull the
  310. # tree down and inspect to assess the version #, so
  311. # its handled way down.
  312. if not (self.force_reinstall or req_to_install.link):
  313. try:
  314. finder.find_requirement(req_to_install, self.upgrade)
  315. except BestVersionAlreadyInstalled:
  316. skip_reason = 'up-to-date'
  317. best_installed = True
  318. except DistributionNotFound:
  319. # No distribution found, so we squash the
  320. # error - it will be raised later when we
  321. # re-try later to do the install.
  322. # Why don't we just raise here?
  323. pass
  324. if not best_installed:
  325. # don't uninstall conflict if user install and
  326. # conflict is not user install
  327. if not (self.use_user_site and not
  328. dist_in_usersite(req_to_install.satisfied_by)):
  329. req_to_install.conflicts_with = \
  330. req_to_install.satisfied_by
  331. req_to_install.satisfied_by = None
  332. return skip_reason
  333. else:
  334. return None
  335. def _prepare_file(self, finder, req_to_install):
  336. """Prepare a single requirements files.
  337. :return: A list of addition InstallRequirements to also install.
  338. """
  339. # Tell user what we are doing for this requirement:
  340. # obtain (editable), skipping, processing (local url), collecting
  341. # (remote url or package name)
  342. if req_to_install.editable:
  343. logger.info('Obtaining %s', req_to_install)
  344. else:
  345. # satisfied_by is only evaluated by calling _check_skip_installed,
  346. # so it must be None here.
  347. assert req_to_install.satisfied_by is None
  348. if not self.ignore_installed:
  349. skip_reason = self._check_skip_installed(
  350. req_to_install, finder)
  351. if req_to_install.satisfied_by:
  352. assert skip_reason is not None, (
  353. '_check_skip_installed returned None but '
  354. 'req_to_install.satisfied_by is set to %r'
  355. % (req_to_install.satisfied_by,))
  356. logger.info(
  357. 'Requirement already %s: %s', skip_reason,
  358. req_to_install)
  359. else:
  360. if (req_to_install.link and
  361. req_to_install.link.scheme == 'file'):
  362. path = url_to_path(req_to_install.link.url)
  363. logger.info('Processing %s', display_path(path))
  364. else:
  365. logger.info('Collecting %s', req_to_install)
  366. with indent_log():
  367. # ################################ #
  368. # # vcs update or unpack archive # #
  369. # ################################ #
  370. if req_to_install.editable:
  371. req_to_install.ensure_has_source_dir(self.src_dir)
  372. req_to_install.update_editable(not self.is_download)
  373. abstract_dist = make_abstract_dist(req_to_install)
  374. abstract_dist.prep_for_dist()
  375. if self.is_download:
  376. req_to_install.archive(self.download_dir)
  377. elif req_to_install.satisfied_by:
  378. abstract_dist = Installed(req_to_install)
  379. else:
  380. # @@ if filesystem packages are not marked
  381. # editable in a req, a non deterministic error
  382. # occurs when the script attempts to unpack the
  383. # build directory
  384. req_to_install.ensure_has_source_dir(self.build_dir)
  385. # If a checkout exists, it's unwise to keep going. version
  386. # inconsistencies are logged later, but do not fail the
  387. # installation.
  388. # FIXME: this won't upgrade when there's an existing
  389. # package unpacked in `req_to_install.source_dir`
  390. if os.path.exists(
  391. os.path.join(req_to_install.source_dir, 'setup.py')):
  392. raise PreviousBuildDirError(
  393. "pip can't proceed with requirements '%s' due to a"
  394. " pre-existing build directory (%s). This is "
  395. "likely due to a previous installation that failed"
  396. ". pip is being responsible and not assuming it "
  397. "can delete this. Please delete it and try again."
  398. % (req_to_install, req_to_install.source_dir)
  399. )
  400. req_to_install.populate_link(finder, self.upgrade)
  401. # We can't hit this spot and have populate_link return None.
  402. # req_to_install.satisfied_by is None here (because we're
  403. # guarded) and upgrade has no impact except when satisfied_by
  404. # is not None.
  405. # Then inside find_requirement existing_applicable -> False
  406. # If no new versions are found, DistributionNotFound is raised,
  407. # otherwise a result is guaranteed.
  408. assert req_to_install.link
  409. try:
  410. if req_to_install.link.is_wheel and \
  411. self.wheel_download_dir:
  412. # when doing 'pip wheel`
  413. download_dir = self.wheel_download_dir
  414. do_download = True
  415. else:
  416. download_dir = self.download_dir
  417. do_download = self.is_download
  418. unpack_url(
  419. req_to_install.link, req_to_install.source_dir,
  420. download_dir, do_download, session=self.session,
  421. )
  422. except requests.HTTPError as exc:
  423. logger.critical(
  424. 'Could not install requirement %s because '
  425. 'of error %s',
  426. req_to_install,
  427. exc,
  428. )
  429. raise InstallationError(
  430. 'Could not install requirement %s because '
  431. 'of HTTP error %s for URL %s' %
  432. (req_to_install, exc, req_to_install.link)
  433. )
  434. abstract_dist = make_abstract_dist(req_to_install)
  435. abstract_dist.prep_for_dist()
  436. if self.is_download:
  437. # Make a .zip of the source_dir we already created.
  438. if req_to_install.link.scheme in vcs.all_schemes:
  439. req_to_install.archive(self.download_dir)
  440. # req_to_install.req is only avail after unpack for URL
  441. # pkgs repeat check_if_exists to uninstall-on-upgrade
  442. # (#14)
  443. if not self.ignore_installed:
  444. req_to_install.check_if_exists()
  445. if req_to_install.satisfied_by:
  446. if self.upgrade or self.ignore_installed:
  447. # don't uninstall conflict if user install and
  448. # conflict is not user install
  449. if not (self.use_user_site and not
  450. dist_in_usersite(
  451. req_to_install.satisfied_by)):
  452. req_to_install.conflicts_with = \
  453. req_to_install.satisfied_by
  454. req_to_install.satisfied_by = None
  455. else:
  456. logger.info(
  457. 'Requirement already satisfied (use '
  458. '--upgrade to upgrade): %s',
  459. req_to_install,
  460. )
  461. # ###################### #
  462. # # parse dependencies # #
  463. # ###################### #
  464. dist = abstract_dist.dist(finder)
  465. more_reqs = []
  466. def add_req(subreq):
  467. sub_install_req = InstallRequirement(
  468. str(subreq),
  469. req_to_install,
  470. isolated=self.isolated,
  471. )
  472. more_reqs.extend(self.add_requirement(
  473. sub_install_req, req_to_install.name))
  474. # We add req_to_install before its dependencies, so that we
  475. # can refer to it when adding dependencies.
  476. if not self.has_requirement(req_to_install.name):
  477. # 'unnamed' requirements will get added here
  478. self.add_requirement(req_to_install, None)
  479. if not self.ignore_dependencies:
  480. if (req_to_install.extras):
  481. logger.debug(
  482. "Installing extra requirements: %r",
  483. ','.join(req_to_install.extras),
  484. )
  485. missing_requested = sorted(
  486. set(req_to_install.extras) - set(dist.extras)
  487. )
  488. for missing in missing_requested:
  489. logger.warning(
  490. '%s does not provide the extra \'%s\'',
  491. dist, missing
  492. )
  493. available_requested = sorted(
  494. set(dist.extras) & set(req_to_install.extras)
  495. )
  496. for subreq in dist.requires(available_requested):
  497. add_req(subreq)
  498. # cleanup tmp src
  499. self.reqs_to_cleanup.append(req_to_install)
  500. if not req_to_install.editable and not req_to_install.satisfied_by:
  501. # XXX: --no-install leads this to report 'Successfully
  502. # downloaded' for only non-editable reqs, even though we took
  503. # action on them.
  504. self.successfully_downloaded.append(req_to_install)
  505. return more_reqs
  506. def cleanup_files(self):
  507. """Clean up files, remove builds."""
  508. logger.debug('Cleaning up...')
  509. with indent_log():
  510. for req in self.reqs_to_cleanup:
  511. req.remove_temporary_source()
  512. if self._pip_has_created_build_dir():
  513. logger.debug('Removing temporary dir %s...', self.build_dir)
  514. rmtree(self.build_dir)
  515. def _pip_has_created_build_dir(self):
  516. return (
  517. self.build_dir == build_prefix and
  518. os.path.exists(
  519. os.path.join(self.build_dir, PIP_DELETE_MARKER_FILENAME)
  520. )
  521. )
  522. def _to_install(self):
  523. """Create the installation order.
  524. The installation order is topological - requirements are installed
  525. before the requiring thing. We break cycles at an arbitrary point,
  526. and make no other guarantees.
  527. """
  528. # The current implementation, which we may change at any point
  529. # installs the user specified things in the order given, except when
  530. # dependencies must come earlier to achieve topological order.
  531. order = []
  532. ordered_reqs = set()
  533. def schedule(req):
  534. if req.satisfied_by or req in ordered_reqs:
  535. return
  536. ordered_reqs.add(req)
  537. for dep in self._dependencies[req]:
  538. schedule(dep)
  539. order.append(req)
  540. for install_req in self.requirements.values():
  541. schedule(install_req)
  542. return order
  543. def install(self, install_options, global_options=(), *args, **kwargs):
  544. """
  545. Install everything in this set (after having downloaded and unpacked
  546. the packages)
  547. """
  548. to_install = self._to_install()
  549. # DISTRIBUTE TO SETUPTOOLS UPGRADE HACK (1 of 3 parts)
  550. # move the distribute-0.7.X wrapper to the end because it does not
  551. # install a setuptools package. by moving it to the end, we ensure it's
  552. # setuptools dependency is handled first, which will provide the
  553. # setuptools package
  554. # TODO: take this out later
  555. distribute_req = pkg_resources.Requirement.parse("distribute>=0.7")
  556. for req in to_install:
  557. if (req.name == 'distribute' and
  558. req.installed_version is not None and
  559. req.installed_version in distribute_req):
  560. to_install.remove(req)
  561. to_install.append(req)
  562. if to_install:
  563. logger.info(
  564. 'Installing collected packages: %s',
  565. ', '.join([req.name for req in to_install]),
  566. )
  567. with indent_log():
  568. for requirement in to_install:
  569. # DISTRIBUTE TO SETUPTOOLS UPGRADE HACK (1 of 3 parts)
  570. # when upgrading from distribute-0.6.X to the new merged
  571. # setuptools in py2, we need to force setuptools to uninstall
  572. # distribute. In py3, which is always using distribute, this
  573. # conversion is already happening in distribute's
  574. # pkg_resources. It's ok *not* to check if setuptools>=0.7
  575. # because if someone were actually trying to ugrade from
  576. # distribute to setuptools 0.6.X, then all this could do is
  577. # actually help, although that upgade path was certainly never
  578. # "supported"
  579. # TODO: remove this later
  580. if requirement.name == 'setuptools':
  581. try:
  582. # only uninstall distribute<0.7. For >=0.7, setuptools
  583. # will also be present, and that's what we need to
  584. # uninstall
  585. distribute_requirement = \
  586. pkg_resources.Requirement.parse("distribute<0.7")
  587. existing_distribute = \
  588. pkg_resources.get_distribution("distribute")
  589. if existing_distribute in distribute_requirement:
  590. requirement.conflicts_with = existing_distribute
  591. except pkg_resources.DistributionNotFound:
  592. # distribute wasn't installed, so nothing to do
  593. pass
  594. if requirement.conflicts_with:
  595. logger.info(
  596. 'Found existing installation: %s',
  597. requirement.conflicts_with,
  598. )
  599. with indent_log():
  600. requirement.uninstall(auto_confirm=True)
  601. try:
  602. requirement.install(
  603. install_options,
  604. global_options,
  605. *args,
  606. **kwargs
  607. )
  608. except:
  609. # if install did not succeed, rollback previous uninstall
  610. if (requirement.conflicts_with and not
  611. requirement.install_succeeded):
  612. requirement.rollback_uninstall()
  613. raise
  614. else:
  615. if (requirement.conflicts_with and
  616. requirement.install_succeeded):
  617. requirement.commit_uninstall()
  618. requirement.remove_temporary_source()
  619. self.successfully_installed = to_install