sdist.py 4.0 KB

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