candidates.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. import logging
  2. import sys
  3. from pip._vendor.packaging.specifiers import InvalidSpecifier, SpecifierSet
  4. from pip._vendor.packaging.utils import canonicalize_name
  5. from pip._vendor.packaging.version import Version
  6. from pip._internal.req.constructors import (
  7. install_req_from_editable,
  8. install_req_from_line,
  9. )
  10. from pip._internal.req.req_install import InstallRequirement
  11. from pip._internal.utils.misc import normalize_version_info
  12. from pip._internal.utils.packaging import get_requires_python
  13. from pip._internal.utils.typing import MYPY_CHECK_RUNNING
  14. from .base import Candidate, format_name
  15. if MYPY_CHECK_RUNNING:
  16. from typing import Any, Optional, Sequence, Set, Tuple, Union
  17. from pip._vendor.packaging.version import _BaseVersion
  18. from pip._vendor.pkg_resources import Distribution
  19. from pip._internal.distributions import AbstractDistribution
  20. from pip._internal.models.link import Link
  21. from .base import Requirement
  22. from .factory import Factory
  23. BaseCandidate = Union[
  24. "AlreadyInstalledCandidate",
  25. "EditableCandidate",
  26. "LinkCandidate",
  27. ]
  28. logger = logging.getLogger(__name__)
  29. def make_install_req_from_link(link, parent):
  30. # type: (Link, InstallRequirement) -> InstallRequirement
  31. assert not parent.editable, "parent is editable"
  32. return install_req_from_line(
  33. link.url,
  34. comes_from=parent.comes_from,
  35. use_pep517=parent.use_pep517,
  36. isolated=parent.isolated,
  37. constraint=parent.constraint,
  38. options=dict(
  39. install_options=parent.install_options,
  40. global_options=parent.global_options,
  41. hashes=parent.hash_options
  42. ),
  43. )
  44. def make_install_req_from_editable(link, parent):
  45. # type: (Link, InstallRequirement) -> InstallRequirement
  46. assert parent.editable, "parent not editable"
  47. return install_req_from_editable(
  48. link.url,
  49. comes_from=parent.comes_from,
  50. use_pep517=parent.use_pep517,
  51. isolated=parent.isolated,
  52. constraint=parent.constraint,
  53. options=dict(
  54. install_options=parent.install_options,
  55. global_options=parent.global_options,
  56. hashes=parent.hash_options
  57. ),
  58. )
  59. def make_install_req_from_dist(dist, parent):
  60. # type: (Distribution, InstallRequirement) -> InstallRequirement
  61. ireq = install_req_from_line(
  62. "{}=={}".format(
  63. canonicalize_name(dist.project_name),
  64. dist.parsed_version,
  65. ),
  66. comes_from=parent.comes_from,
  67. use_pep517=parent.use_pep517,
  68. isolated=parent.isolated,
  69. constraint=parent.constraint,
  70. options=dict(
  71. install_options=parent.install_options,
  72. global_options=parent.global_options,
  73. hashes=parent.hash_options
  74. ),
  75. )
  76. ireq.satisfied_by = dist
  77. return ireq
  78. class _InstallRequirementBackedCandidate(Candidate):
  79. def __init__(
  80. self,
  81. link, # type: Link
  82. ireq, # type: InstallRequirement
  83. factory, # type: Factory
  84. name=None, # type: Optional[str]
  85. version=None, # type: Optional[_BaseVersion]
  86. ):
  87. # type: (...) -> None
  88. self.link = link
  89. self._factory = factory
  90. self._ireq = ireq
  91. self._name = name
  92. self._version = version
  93. self._dist = None # type: Optional[Distribution]
  94. def __repr__(self):
  95. # type: () -> str
  96. return "{class_name}({link!r})".format(
  97. class_name=self.__class__.__name__,
  98. link=str(self.link),
  99. )
  100. def __eq__(self, other):
  101. # type: (Any) -> bool
  102. if isinstance(other, self.__class__):
  103. return self.link == other.link
  104. return False
  105. # Needed for Python 2, which does not implement this by default
  106. def __ne__(self, other):
  107. # type: (Any) -> bool
  108. return not self.__eq__(other)
  109. @property
  110. def name(self):
  111. # type: () -> str
  112. """The normalised name of the project the candidate refers to"""
  113. if self._name is None:
  114. self._name = canonicalize_name(self.dist.project_name)
  115. return self._name
  116. @property
  117. def version(self):
  118. # type: () -> _BaseVersion
  119. if self._version is None:
  120. self._version = self.dist.parsed_version
  121. return self._version
  122. def _prepare_abstract_distribution(self):
  123. # type: () -> AbstractDistribution
  124. raise NotImplementedError("Override in subclass")
  125. def _prepare(self):
  126. # type: () -> None
  127. if self._dist is not None:
  128. return
  129. abstract_dist = self._prepare_abstract_distribution()
  130. self._dist = abstract_dist.get_pkg_resources_distribution()
  131. assert self._dist is not None, "Distribution already installed"
  132. # TODO: Abort cleanly here, as the resolution has been
  133. # based on the wrong name/version until now, and
  134. # so is wrong.
  135. # TODO: (Longer term) Rather than abort, reject this candidate
  136. # and backtrack. This would need resolvelib support.
  137. # These should be "proper" errors, not just asserts, as they
  138. # can result from user errors like a requirement "foo @ URL"
  139. # when the project at URL has a name of "bar" in its metadata.
  140. assert (
  141. self._name is None or
  142. self._name == canonicalize_name(self._dist.project_name)
  143. ), "Name mismatch: {!r} vs {!r}".format(
  144. self._name, canonicalize_name(self._dist.project_name),
  145. )
  146. assert (
  147. self._version is None or
  148. self._version == self._dist.parsed_version
  149. ), "Version mismatch: {!r} vs {!r}".format(
  150. self._version, self._dist.parsed_version,
  151. )
  152. @property
  153. def dist(self):
  154. # type: () -> Distribution
  155. self._prepare()
  156. return self._dist
  157. def _get_requires_python_specifier(self):
  158. # type: () -> Optional[SpecifierSet]
  159. requires_python = get_requires_python(self.dist)
  160. if requires_python is None:
  161. return None
  162. try:
  163. spec = SpecifierSet(requires_python)
  164. except InvalidSpecifier as e:
  165. logger.warning(
  166. "Package %r has an invalid Requires-Python: %s", self.name, e,
  167. )
  168. return None
  169. return spec
  170. def get_dependencies(self):
  171. # type: () -> Sequence[Requirement]
  172. deps = [
  173. self._factory.make_requirement_from_spec(str(r), self._ireq)
  174. for r in self.dist.requires()
  175. ]
  176. python_dep = self._factory.make_requires_python_requirement(
  177. self._get_requires_python_specifier(),
  178. )
  179. if python_dep:
  180. deps.append(python_dep)
  181. return deps
  182. def get_install_requirement(self):
  183. # type: () -> Optional[InstallRequirement]
  184. self._prepare()
  185. return self._ireq
  186. class LinkCandidate(_InstallRequirementBackedCandidate):
  187. def __init__(
  188. self,
  189. link, # type: Link
  190. parent, # type: InstallRequirement
  191. factory, # type: Factory
  192. name=None, # type: Optional[str]
  193. version=None, # type: Optional[_BaseVersion]
  194. ):
  195. # type: (...) -> None
  196. super(LinkCandidate, self).__init__(
  197. link=link,
  198. ireq=make_install_req_from_link(link, parent),
  199. factory=factory,
  200. name=name,
  201. version=version,
  202. )
  203. def _prepare_abstract_distribution(self):
  204. # type: () -> AbstractDistribution
  205. return self._factory.preparer.prepare_linked_requirement(self._ireq)
  206. class EditableCandidate(_InstallRequirementBackedCandidate):
  207. def __init__(
  208. self,
  209. link, # type: Link
  210. parent, # type: InstallRequirement
  211. factory, # type: Factory
  212. name=None, # type: Optional[str]
  213. version=None, # type: Optional[_BaseVersion]
  214. ):
  215. # type: (...) -> None
  216. super(EditableCandidate, self).__init__(
  217. link=link,
  218. ireq=make_install_req_from_editable(link, parent),
  219. factory=factory,
  220. name=name,
  221. version=version,
  222. )
  223. def _prepare_abstract_distribution(self):
  224. # type: () -> AbstractDistribution
  225. return self._factory.preparer.prepare_editable_requirement(self._ireq)
  226. class AlreadyInstalledCandidate(Candidate):
  227. def __init__(
  228. self,
  229. dist, # type: Distribution
  230. parent, # type: InstallRequirement
  231. factory, # type: Factory
  232. ):
  233. # type: (...) -> None
  234. self.dist = dist
  235. self._ireq = make_install_req_from_dist(dist, parent)
  236. self._factory = factory
  237. # This is just logging some messages, so we can do it eagerly.
  238. # The returned dist would be exactly the same as self.dist because we
  239. # set satisfied_by in make_install_req_from_dist.
  240. # TODO: Supply reason based on force_reinstall and upgrade_strategy.
  241. skip_reason = "already satisfied"
  242. factory.preparer.prepare_installed_requirement(self._ireq, skip_reason)
  243. def __repr__(self):
  244. # type: () -> str
  245. return "{class_name}({distribution!r})".format(
  246. class_name=self.__class__.__name__,
  247. distribution=self.dist,
  248. )
  249. def __eq__(self, other):
  250. # type: (Any) -> bool
  251. if isinstance(other, self.__class__):
  252. return self.name == other.name and self.version == other.version
  253. return False
  254. # Needed for Python 2, which does not implement this by default
  255. def __ne__(self, other):
  256. # type: (Any) -> bool
  257. return not self.__eq__(other)
  258. @property
  259. def name(self):
  260. # type: () -> str
  261. return canonicalize_name(self.dist.project_name)
  262. @property
  263. def version(self):
  264. # type: () -> _BaseVersion
  265. return self.dist.parsed_version
  266. def get_dependencies(self):
  267. # type: () -> Sequence[Requirement]
  268. return [
  269. self._factory.make_requirement_from_spec(str(r), self._ireq)
  270. for r in self.dist.requires()
  271. ]
  272. def get_install_requirement(self):
  273. # type: () -> Optional[InstallRequirement]
  274. return None
  275. class ExtrasCandidate(Candidate):
  276. """A candidate that has 'extras', indicating additional dependencies.
  277. Requirements can be for a project with dependencies, something like
  278. foo[extra]. The extras don't affect the project/version being installed
  279. directly, but indicate that we need additional dependencies. We model that
  280. by having an artificial ExtrasCandidate that wraps the "base" candidate.
  281. The ExtrasCandidate differs from the base in the following ways:
  282. 1. It has a unique name, of the form foo[extra]. This causes the resolver
  283. to treat it as a separate node in the dependency graph.
  284. 2. When we're getting the candidate's dependencies,
  285. a) We specify that we want the extra dependencies as well.
  286. b) We add a dependency on the base candidate (matching the name and
  287. version). See below for why this is needed.
  288. 3. We return None for the underlying InstallRequirement, as the base
  289. candidate will provide it, and we don't want to end up with duplicates.
  290. The dependency on the base candidate is needed so that the resolver can't
  291. decide that it should recommend foo[extra1] version 1.0 and foo[extra2]
  292. version 2.0. Having those candidates depend on foo=1.0 and foo=2.0
  293. respectively forces the resolver to recognise that this is a conflict.
  294. """
  295. def __init__(
  296. self,
  297. base, # type: BaseCandidate
  298. extras, # type: Set[str]
  299. ):
  300. # type: (...) -> None
  301. self.base = base
  302. self.extras = extras
  303. def __repr__(self):
  304. # type: () -> str
  305. return "{class_name}(base={base!r}, extras={extras!r})".format(
  306. class_name=self.__class__.__name__,
  307. base=self.base,
  308. extras=self.extras,
  309. )
  310. def __eq__(self, other):
  311. # type: (Any) -> bool
  312. if isinstance(other, self.__class__):
  313. return self.base == other.base and self.extras == other.extras
  314. return False
  315. # Needed for Python 2, which does not implement this by default
  316. def __ne__(self, other):
  317. # type: (Any) -> bool
  318. return not self.__eq__(other)
  319. @property
  320. def name(self):
  321. # type: () -> str
  322. """The normalised name of the project the candidate refers to"""
  323. return format_name(self.base.name, self.extras)
  324. @property
  325. def version(self):
  326. # type: () -> _BaseVersion
  327. return self.base.version
  328. def get_dependencies(self):
  329. # type: () -> Sequence[Requirement]
  330. factory = self.base._factory
  331. # The user may have specified extras that the candidate doesn't
  332. # support. We ignore any unsupported extras here.
  333. valid_extras = self.extras.intersection(self.base.dist.extras)
  334. invalid_extras = self.extras.difference(self.base.dist.extras)
  335. if invalid_extras:
  336. logger.warning(
  337. "Invalid extras specified in %s: %s",
  338. self.name,
  339. ','.join(sorted(invalid_extras))
  340. )
  341. deps = [
  342. factory.make_requirement_from_spec(str(r), self.base._ireq)
  343. for r in self.base.dist.requires(valid_extras)
  344. ]
  345. # Add a dependency on the exact base.
  346. # (See note 2b in the class docstring)
  347. spec = "{}=={}".format(self.base.name, self.base.version)
  348. deps.append(factory.make_requirement_from_spec(spec, self.base._ireq))
  349. return deps
  350. def get_install_requirement(self):
  351. # type: () -> Optional[InstallRequirement]
  352. # We don't return anything here, because we always
  353. # depend on the base candidate, and we'll get the
  354. # install requirement from that.
  355. return None
  356. class RequiresPythonCandidate(Candidate):
  357. def __init__(self, py_version_info):
  358. # type: (Optional[Tuple[int, ...]]) -> None
  359. if py_version_info is not None:
  360. version_info = normalize_version_info(py_version_info)
  361. else:
  362. version_info = sys.version_info[:3]
  363. self._version = Version(".".join(str(c) for c in version_info))
  364. # We don't need to implement __eq__() and __ne__() since there is always
  365. # only one RequiresPythonCandidate in a resolution, i.e. the host Python.
  366. # The built-in object.__eq__() and object.__ne__() do exactly what we want.
  367. @property
  368. def name(self):
  369. # type: () -> str
  370. # Avoid conflicting with the PyPI package "Python".
  371. return "<Python fom Requires-Python>"
  372. @property
  373. def version(self):
  374. # type: () -> _BaseVersion
  375. return self._version
  376. def get_dependencies(self):
  377. # type: () -> Sequence[Requirement]
  378. return []
  379. def get_install_requirement(self):
  380. # type: () -> Optional[InstallRequirement]
  381. return None