codekingpro/portable-devtools
114k
1from __future__ import annotations2 3import os4from collections import namedtuple5from typing import Any6 7from pip._vendor.packaging.requirements import InvalidRequirement8 9from pip._internal.exceptions import (10 InstallationError,11 InvalidPyProjectBuildRequires,12 MissingPyProjectBuildRequires,13)14from pip._internal.utils.compat import tomllib15from pip._internal.utils.packaging import get_requirement16 17 18def _is_list_of_str(obj: Any) -> bool:19 return isinstance(obj, list) and all(isinstance(item, str) for item in obj)20 21 22def make_pyproject_path(unpacked_source_directory: str) -> str:23 return os.path.join(unpacked_source_directory, "pyproject.toml")24 25 26BuildSystemDetails = namedtuple(27 "BuildSystemDetails", ["requires", "backend", "check", "backend_path"]28)29 30 31def load_pyproject_toml(32 pyproject_toml: str, setup_py: str, req_name: str33) -> BuildSystemDetails:34 """Load the pyproject.toml file.35 36 Parameters:37 pyproject_toml - Location of the project's pyproject.toml file38 setup_py - Location of the project's setup.py file39 req_name - The name of the requirement we're processing (for40 error reporting)41 42 Returns:43 None if we should use the legacy code path, otherwise a tuple44 (45 requirements from pyproject.toml,46 name of PEP 517 backend,47 requirements we should check are installed after setting48 up the build environment49 directory paths to import the backend from (backend-path),50 relative to the project root.51 )52 """53 has_pyproject = os.path.isfile(pyproject_toml)54 has_setup = os.path.isfile(setup_py)55 56 if not has_pyproject and not has_setup:57 raise InstallationError(58 f"{req_name} does not appear to be a Python project: "59 f"neither 'setup.py' nor 'pyproject.toml' found."60 )61 62 if has_pyproject:63 with open(pyproject_toml, encoding="utf-8") as f:64 pp_toml = tomllib.loads(f.read())65 build_system = pp_toml.get("build-system")66 else:67 build_system = None68 69 if build_system is None:70 # In the absence of any explicit backend specification, we71 # assume the setuptools backend that most closely emulates the72 # traditional direct setup.py execution, and require wheel and73 # a version of setuptools that supports that backend.74 75 build_system = {76 "requires": ["setuptools>=40.8.0"],77 "build-backend": "setuptools.build_meta:__legacy__",78 }79 80 # Ensure that the build-system section in pyproject.toml conforms81 # to PEP 518.82 83 # Specifying the build-system table but not the requires key is invalid84 if "requires" not in build_system:85 raise MissingPyProjectBuildRequires(package=req_name)86 87 # Error out if requires is not a list of strings88 requires = build_system["requires"]89 if not _is_list_of_str(requires):90 raise InvalidPyProjectBuildRequires(91 package=req_name,92 reason="It is not a list of strings.",93 )94 95 # Each requirement must be valid as per PEP 50896 for requirement in requires:97 try:98 get_requirement(requirement)99 except InvalidRequirement as error:100 raise InvalidPyProjectBuildRequires(101 package=req_name,102 reason=f"It contains an invalid requirement: {requirement!r}",103 ) from error104 105 backend = build_system.get("build-backend")106 backend_path = build_system.get("backend-path", [])107 check: list[str] = []108 if backend is None:109 # If the user didn't specify a backend, we assume they want to use110 # the setuptools backend. But we can't be sure they have included111 # a version of setuptools which supplies the backend. So we112 # make a note to check that this requirement is present once113 # we have set up the environment.114 # This is quite a lot of work to check for a very specific case. But115 # the problem is, that case is potentially quite common - projects that116 # adopted PEP 518 early for the ability to specify requirements to117 # execute setup.py, but never considered needing to mention the build118 # tools themselves. The original PEP 518 code had a similar check (but119 # implemented in a different way).120 backend = "setuptools.build_meta:__legacy__"121 check = ["setuptools>=40.8.0"]122 123 return BuildSystemDetails(requires, backend, check, backend_path)124 