Path: blob/master/venv/Lib/site-packages/pip/_internal/pyproject.py
811 views
from __future__ import absolute_import12import io3import os4import sys5from collections import namedtuple67from pip._vendor import six, toml8from pip._vendor.packaging.requirements import InvalidRequirement, Requirement910from pip._internal.exceptions import InstallationError11from pip._internal.utils.typing import MYPY_CHECK_RUNNING1213if MYPY_CHECK_RUNNING:14from typing import Any, Optional, List151617def _is_list_of_str(obj):18# type: (Any) -> bool19return (20isinstance(obj, list) and21all(isinstance(item, six.string_types) for item in obj)22)232425def make_pyproject_path(unpacked_source_directory):26# type: (str) -> str27path = os.path.join(unpacked_source_directory, 'pyproject.toml')2829# Python2 __file__ should not be unicode30if six.PY2 and isinstance(path, six.text_type):31path = path.encode(sys.getfilesystemencoding())3233return path343536BuildSystemDetails = namedtuple('BuildSystemDetails', [37'requires', 'backend', 'check', 'backend_path'38])394041def load_pyproject_toml(42use_pep517, # type: Optional[bool]43pyproject_toml, # type: str44setup_py, # type: str45req_name # type: str46):47# type: (...) -> Optional[BuildSystemDetails]48"""Load the pyproject.toml file.4950Parameters:51use_pep517 - Has the user requested PEP 517 processing? None52means the user hasn't explicitly specified.53pyproject_toml - Location of the project's pyproject.toml file54setup_py - Location of the project's setup.py file55req_name - The name of the requirement we're processing (for56error reporting)5758Returns:59None if we should use the legacy code path, otherwise a tuple60(61requirements from pyproject.toml,62name of PEP 517 backend,63requirements we should check are installed after setting64up the build environment65directory paths to import the backend from (backend-path),66relative to the project root.67)68"""69has_pyproject = os.path.isfile(pyproject_toml)70has_setup = os.path.isfile(setup_py)7172if has_pyproject:73with io.open(pyproject_toml, encoding="utf-8") as f:74pp_toml = toml.load(f)75build_system = pp_toml.get("build-system")76else:77build_system = None7879# The following cases must use PEP 51780# We check for use_pep517 being non-None and falsey because that means81# the user explicitly requested --no-use-pep517. The value 0 as82# opposed to False can occur when the value is provided via an83# environment variable or config file option (due to the quirk of84# strtobool() returning an integer in pip's configuration code).85if has_pyproject and not has_setup:86if use_pep517 is not None and not use_pep517:87raise InstallationError(88"Disabling PEP 517 processing is invalid: "89"project does not have a setup.py"90)91use_pep517 = True92elif build_system and "build-backend" in build_system:93if use_pep517 is not None and not use_pep517:94raise InstallationError(95"Disabling PEP 517 processing is invalid: "96"project specifies a build backend of {} "97"in pyproject.toml".format(98build_system["build-backend"]99)100)101use_pep517 = True102103# If we haven't worked out whether to use PEP 517 yet,104# and the user hasn't explicitly stated a preference,105# we do so if the project has a pyproject.toml file.106elif use_pep517 is None:107use_pep517 = has_pyproject108109# At this point, we know whether we're going to use PEP 517.110assert use_pep517 is not None111112# If we're using the legacy code path, there is nothing further113# for us to do here.114if not use_pep517:115return None116117if build_system is None:118# Either the user has a pyproject.toml with no build-system119# section, or the user has no pyproject.toml, but has opted in120# explicitly via --use-pep517.121# In the absence of any explicit backend specification, we122# assume the setuptools backend that most closely emulates the123# traditional direct setup.py execution, and require wheel and124# a version of setuptools that supports that backend.125126build_system = {127"requires": ["setuptools>=40.8.0", "wheel"],128"build-backend": "setuptools.build_meta:__legacy__",129}130131# If we're using PEP 517, we have build system information (either132# from pyproject.toml, or defaulted by the code above).133# Note that at this point, we do not know if the user has actually134# specified a backend, though.135assert build_system is not None136137# Ensure that the build-system section in pyproject.toml conforms138# to PEP 518.139error_template = (140"{package} has a pyproject.toml file that does not comply "141"with PEP 518: {reason}"142)143144# Specifying the build-system table but not the requires key is invalid145if "requires" not in build_system:146raise InstallationError(147error_template.format(package=req_name, reason=(148"it has a 'build-system' table but not "149"'build-system.requires' which is mandatory in the table"150))151)152153# Error out if requires is not a list of strings154requires = build_system["requires"]155if not _is_list_of_str(requires):156raise InstallationError(error_template.format(157package=req_name,158reason="'build-system.requires' is not a list of strings.",159))160161# Each requirement must be valid as per PEP 508162for requirement in requires:163try:164Requirement(requirement)165except InvalidRequirement:166raise InstallationError(167error_template.format(168package=req_name,169reason=(170"'build-system.requires' contains an invalid "171"requirement: {!r}".format(requirement)172),173)174)175176backend = build_system.get("build-backend")177backend_path = build_system.get("backend-path", [])178check = [] # type: List[str]179if backend is None:180# If the user didn't specify a backend, we assume they want to use181# the setuptools backend. But we can't be sure they have included182# a version of setuptools which supplies the backend, or wheel183# (which is needed by the backend) in their requirements. So we184# make a note to check that those requirements are present once185# we have set up the environment.186# This is quite a lot of work to check for a very specific case. But187# the problem is, that case is potentially quite common - projects that188# adopted PEP 518 early for the ability to specify requirements to189# execute setup.py, but never considered needing to mention the build190# tools themselves. The original PEP 518 code had a similar check (but191# implemented in a different way).192backend = "setuptools.build_meta:__legacy__"193check = ["setuptools>=40.8.0", "wheel"]194195return BuildSystemDetails(requires, backend, check, backend_path)196197198