You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

144 lines
5.4 KiB

4 years ago
  1. from __future__ import absolute_import
  2. import io
  3. import os
  4. from pip._vendor import pytoml, six
  5. from pip._internal.exceptions import InstallationError
  6. def _is_list_of_str(obj):
  7. return (
  8. isinstance(obj, list) and
  9. all(isinstance(item, six.string_types) for item in obj)
  10. )
  11. def load_pyproject_toml(use_pep517, pyproject_toml, setup_py, req_name):
  12. """Load the pyproject.toml file.
  13. Parameters:
  14. use_pep517 - Has the user requested PEP 517 processing? None
  15. means the user hasn't explicitly specified.
  16. pyproject_toml - Location of the project's pyproject.toml file
  17. setup_py - Location of the project's setup.py file
  18. req_name - The name of the requirement we're processing (for
  19. error reporting)
  20. Returns:
  21. None if we should use the legacy code path, otherwise a tuple
  22. (
  23. requirements from pyproject.toml,
  24. name of PEP 517 backend,
  25. requirements we should check are installed after setting
  26. up the build environment
  27. )
  28. """
  29. has_pyproject = os.path.isfile(pyproject_toml)
  30. has_setup = os.path.isfile(setup_py)
  31. if has_pyproject:
  32. with io.open(pyproject_toml, encoding="utf-8") as f:
  33. pp_toml = pytoml.load(f)
  34. build_system = pp_toml.get("build-system")
  35. else:
  36. build_system = None
  37. # The following cases must use PEP 517
  38. # We check for use_pep517 equalling False because that
  39. # means the user explicitly requested --no-use-pep517
  40. if has_pyproject and not has_setup:
  41. if use_pep517 is False:
  42. raise InstallationError(
  43. "Disabling PEP 517 processing is invalid: "
  44. "project does not have a setup.py"
  45. )
  46. use_pep517 = True
  47. elif build_system and "build-backend" in build_system:
  48. if use_pep517 is False:
  49. raise InstallationError(
  50. "Disabling PEP 517 processing is invalid: "
  51. "project specifies a build backend of {} "
  52. "in pyproject.toml".format(
  53. build_system["build-backend"]
  54. )
  55. )
  56. use_pep517 = True
  57. # If we haven't worked out whether to use PEP 517 yet,
  58. # and the user hasn't explicitly stated a preference,
  59. # we do so if the project has a pyproject.toml file.
  60. elif use_pep517 is None:
  61. use_pep517 = has_pyproject
  62. # At this point, we know whether we're going to use PEP 517.
  63. assert use_pep517 is not None
  64. # If we're using the legacy code path, there is nothing further
  65. # for us to do here.
  66. if not use_pep517:
  67. return None
  68. if build_system is None:
  69. # Either the user has a pyproject.toml with no build-system
  70. # section, or the user has no pyproject.toml, but has opted in
  71. # explicitly via --use-pep517.
  72. # In the absence of any explicit backend specification, we
  73. # assume the setuptools backend, and require wheel and a version
  74. # of setuptools that supports that backend.
  75. build_system = {
  76. "requires": ["setuptools>=38.2.5", "wheel"],
  77. "build-backend": "setuptools.build_meta",
  78. }
  79. # If we're using PEP 517, we have build system information (either
  80. # from pyproject.toml, or defaulted by the code above).
  81. # Note that at this point, we do not know if the user has actually
  82. # specified a backend, though.
  83. assert build_system is not None
  84. # Ensure that the build-system section in pyproject.toml conforms
  85. # to PEP 518.
  86. error_template = (
  87. "{package} has a pyproject.toml file that does not comply "
  88. "with PEP 518: {reason}"
  89. )
  90. # Specifying the build-system table but not the requires key is invalid
  91. if "requires" not in build_system:
  92. raise InstallationError(
  93. error_template.format(package=req_name, reason=(
  94. "it has a 'build-system' table but not "
  95. "'build-system.requires' which is mandatory in the table"
  96. ))
  97. )
  98. # Error out if requires is not a list of strings
  99. requires = build_system["requires"]
  100. if not _is_list_of_str(requires):
  101. raise InstallationError(error_template.format(
  102. package=req_name,
  103. reason="'build-system.requires' is not a list of strings.",
  104. ))
  105. backend = build_system.get("build-backend")
  106. check = []
  107. if backend is None:
  108. # If the user didn't specify a backend, we assume they want to use
  109. # the setuptools backend. But we can't be sure they have included
  110. # a version of setuptools which supplies the backend, or wheel
  111. # (which is neede by the backend) in their requirements. So we
  112. # make a note to check that those requirements are present once
  113. # we have set up the environment.
  114. # TODO: Review this - it's quite a lot of work to check for a very
  115. # specific case. The problem is, that case is potentially quite
  116. # common - projects that adopted PEP 518 early for the ability to
  117. # specify requirements to execute setup.py, but never considered
  118. # needing to mention the build tools themselves. The original PEP
  119. # 518 code had a similar check (but implemented in a different
  120. # way).
  121. backend = "setuptools.build_meta"
  122. check = ["setuptools>=38.2.5", "wheel"]
  123. return (requires, backend, check)