sdist.py 3.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import logging
  2. from typing import Set, Tuple
  3. from pip._vendor.pkg_resources import Distribution
  4. from pip._internal.build_env import BuildEnvironment
  5. from pip._internal.distributions.base import AbstractDistribution
  6. from pip._internal.exceptions import InstallationError
  7. from pip._internal.index.package_finder import PackageFinder
  8. from pip._internal.utils.subprocess import runner_with_spinner_message
  9. logger = logging.getLogger(__name__)
  10. class SourceDistribution(AbstractDistribution):
  11. """Represents a source distribution.
  12. The preparation step for these needs metadata for the packages to be
  13. generated, either using PEP 517 or using the legacy `setup.py egg_info`.
  14. """
  15. def get_pkg_resources_distribution(self) -> Distribution:
  16. return self.req.get_dist()
  17. def prepare_distribution_metadata(
  18. self, finder: PackageFinder, build_isolation: bool
  19. ) -> None:
  20. # Load pyproject.toml, to determine whether PEP 517 is to be used
  21. self.req.load_pyproject_toml()
  22. # Set up the build isolation, if this requirement should be isolated
  23. should_isolate = self.req.use_pep517 and build_isolation
  24. if should_isolate:
  25. self._setup_isolation(finder)
  26. self.req.prepare_metadata()
  27. def _setup_isolation(self, finder: PackageFinder) -> None:
  28. def _raise_conflicts(
  29. conflicting_with: str, conflicting_reqs: Set[Tuple[str, str]]
  30. ) -> None:
  31. format_string = (
  32. "Some build dependencies for {requirement} "
  33. "conflict with {conflicting_with}: {description}."
  34. )
  35. error_message = format_string.format(
  36. requirement=self.req,
  37. conflicting_with=conflicting_with,
  38. description=", ".join(
  39. f"{installed} is incompatible with {wanted}"
  40. for installed, wanted in sorted(conflicting)
  41. ),
  42. )
  43. raise InstallationError(error_message)
  44. # Isolate in a BuildEnvironment and install the build-time
  45. # requirements.
  46. pyproject_requires = self.req.pyproject_requires
  47. assert pyproject_requires is not None
  48. self.req.build_env = BuildEnvironment()
  49. self.req.build_env.install_requirements(
  50. finder, pyproject_requires, "overlay", "Installing build dependencies"
  51. )
  52. conflicting, missing = self.req.build_env.check_requirements(
  53. self.req.requirements_to_check
  54. )
  55. if conflicting:
  56. _raise_conflicts("PEP 517/518 supported requirements", conflicting)
  57. if missing:
  58. logger.warning(
  59. "Missing build requirements in pyproject.toml for %s.",
  60. self.req,
  61. )
  62. logger.warning(
  63. "The project does not specify a build backend, and "
  64. "pip cannot fall back to setuptools without %s.",
  65. " and ".join(map(repr, sorted(missing))),
  66. )
  67. # Install any extra build dependencies that the backend requests.
  68. # This must be done in a second pass, as the pyproject.toml
  69. # dependencies must be installed before we can call the backend.
  70. with self.req.build_env:
  71. runner = runner_with_spinner_message("Getting requirements to build wheel")
  72. backend = self.req.pep517_backend
  73. assert backend is not None
  74. with backend.subprocess_runner(runner):
  75. reqs = backend.get_requires_for_build_wheel()
  76. conflicting, missing = self.req.build_env.check_requirements(reqs)
  77. if conflicting:
  78. _raise_conflicts("the backend dependencies", conflicting)
  79. self.req.build_env.install_requirements(
  80. finder, missing, "normal", "Installing backend dependencies"
  81. )