- 
                Notifications
    
You must be signed in to change notification settings  - Fork 3.2k
 
Add build constraints #13534
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add build constraints #13534
Changes from 4 commits
2e4086e
              1aa1d32
              db4fcf7
              d9d5f5d
              8d170b5
              9f9032c
              a9e81d7
              4fbafeb
              8943172
              ef06010
              d564457
              ebd55e7
              c41496e
              e015f3d
              b333b85
              bc48f0b
              fc1bfb5
              74b08e1
              41164aa
              e53db93
              f372c74
              d86d520
              05aeb84
              4c04652
              ab36b15
              d8f372c
              d0cf197
              b091be2
              aa5fcd9
              d0bcf5c
              11676b0
              035396d
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| Add experimental build constraints support via ``--use-feature=build-constraint``. | ||
| This allows constraining the versions of packages used during the build process | ||
| (e.g., setuptools). Build constraints can be specified via ``PIP_BUILD_CONSTRAINT`` | ||
| environment variable or ``--build-constraint`` flag. | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| 
          
            
          
           | 
    @@ -11,14 +11,15 @@ | |
| from collections import OrderedDict | ||
| from collections.abc import Iterable | ||
| from types import TracebackType | ||
| from typing import TYPE_CHECKING, Protocol | ||
| from typing import TYPE_CHECKING, Protocol, TypedDict | ||
| 
     | 
||
| from pip._vendor.packaging.version import Version | ||
| 
     | 
||
| from pip import __file__ as pip_location | ||
| from pip._internal.cli.spinners import open_spinner | ||
| from pip._internal.locations import get_platlib, get_purelib, get_scheme | ||
| from pip._internal.metadata import get_default_environment, get_environment | ||
| from pip._internal.utils.deprecation import deprecated | ||
| from pip._internal.utils.logging import VERBOSE | ||
| from pip._internal.utils.packaging import get_requirement | ||
| from pip._internal.utils.subprocess import call_subprocess | ||
| 
        
          
        
         | 
    @@ -31,6 +32,10 @@ | |
| logger = logging.getLogger(__name__) | ||
| 
     | 
||
| 
     | 
||
| class ExtraEnviron(TypedDict, total=False): | ||
| extra_environ: dict[str, str] | ||
                
       | 
||
| 
     | 
||
| 
     | 
||
| def _dedup(a: str, b: str) -> tuple[str] | tuple[str, str]: | ||
| return (a, b) if a != b else (a,) | ||
| 
     | 
||
| 
          
            
          
           | 
    @@ -101,8 +106,49 @@ class SubprocessBuildEnvironmentInstaller: | |
| Install build dependencies by calling pip in a subprocess. | ||
| """ | ||
| 
     | 
||
| def __init__(self, finder: PackageFinder) -> None: | ||
| def __init__( | ||
| self, | ||
| finder: PackageFinder, | ||
| build_constraints: list[str] | None = None, | ||
| build_constraint_feature_enabled: bool = False, | ||
| constraints: list[str] | None = None, | ||
| ) -> None: | ||
| self.finder = finder | ||
| self._build_constraints = build_constraints or [] | ||
| self._build_constraint_feature_enabled = build_constraint_feature_enabled | ||
| self._constraints = constraints or [] | ||
| 
     | 
||
| def _deprecation_constraint_check(self) -> None: | ||
| """ | ||
| Check for deprecation warning: PIP_CONSTRAINT affecting build environments. | ||
| 
     | 
||
| This warns when build-constraint feature is NOT enabled but regular constraints | ||
| match what PIP_CONSTRAINT environment variable points to. | ||
| """ | ||
| if self._build_constraint_feature_enabled: | ||
| return | ||
| 
     | 
||
| if not self._constraints: | ||
| return | ||
| 
     | 
||
| if not os.environ.get("PIP_CONSTRAINT"): | ||
| return | ||
| 
     | 
||
| pip_constraint_files = [ | ||
| f.strip() for f in os.environ["PIP_CONSTRAINT"].split() if f.strip() | ||
| ] | ||
| if pip_constraint_files and set(pip_constraint_files) == set(self._constraints): | ||
| deprecated( | ||
| reason=( | ||
| "Setting PIP_CONSTRAINT will not affect " | ||
| "build constraints in the future," | ||
| ), | ||
| replacement=( | ||
| 'PIP_BUILD_CONSTRAINT with PIP_USE_FEATURE="build-constraint"' | ||
                
      
                  notatallshaw marked this conversation as resolved.
               
              
                Outdated
          
            Show resolved
            Hide resolved
         | 
||
| ), | ||
| gone_in="26.2", | ||
| issue=None, | ||
| ) | ||
| 
     | 
||
| def install( | ||
| self, | ||
| 
        
          
        
         | 
    @@ -112,6 +158,8 @@ def install( | |
| kind: str, | ||
| for_req: InstallRequirement | None, | ||
| ) -> None: | ||
| self._deprecation_constraint_check() | ||
                
      
                  ichard26 marked this conversation as resolved.
               
          
            Show resolved
            Hide resolved
         | 
||
| 
     | 
||
| finder = self.finder | ||
| args: list[str] = [ | ||
| sys.executable, | ||
| 
          
            
          
           | 
    @@ -167,13 +215,32 @@ def install( | |
| args.append("--pre") | ||
| if finder.prefer_binary: | ||
| args.append("--prefer-binary") | ||
| 
     | 
||
| # Handle build constraints | ||
| extra_environ: ExtraEnviron = {} | ||
| if self._build_constraint_feature_enabled: | ||
| # Build constraints must be passed as both constraints | ||
| # and build constraints to the subprocess | ||
| for constraint_file in self._build_constraints: | ||
| args.extend(["--constraint", constraint_file]) | ||
| args.extend(["--build-constraint", constraint_file]) | ||
| args.extend(["--use-feature", "build-constraint"]) | ||
| 
     | 
||
| # If there are no build constraints but the build constraint | ||
| # process is enabled then we must ignore regular constraints | ||
| if not self._build_constraints: | ||
| extra_environ = { | ||
| "extra_environ": {"_PIP_IN_BUILD_IGNORE_CONSTRAINTS": "1"} | ||
| } | ||
| 
     | 
||
| args.append("--") | ||
| args.extend(requirements) | ||
| with open_spinner(f"Installing {kind}") as spinner: | ||
| call_subprocess( | ||
| args, | ||
| command_desc=f"pip subprocess to install {kind}", | ||
| spinner=spinner, | ||
| **extra_environ, | ||
| ) | ||
| 
     | 
||
| 
     | 
||
| 
          
            
          
           | 
    ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| 
          
            
          
           | 
    @@ -101,6 +101,23 @@ def check_dist_restriction(options: Values, check_target: bool = False) -> None: | |
| ) | ||
| 
     | 
||
| 
     | 
||
| def check_build_constraints(options: Values) -> None: | ||
| """Function for validating build constraint options. | ||
                
       | 
||
| 
     | 
||
| :param options: The OptionParser options. | ||
| """ | ||
| if hasattr(options, "build_constraints") and options.build_constraints: | ||
| if "build-constraint" not in options.features_enabled: | ||
| raise CommandError( | ||
| "To use --build-constraint, you must enable this feature with " | ||
| "--use-feature=build-constraint." | ||
| ) | ||
| if not options.build_isolation: | ||
| raise CommandError( | ||
| "--build-constraint cannot be used with --no-build-isolation." | ||
| ) | ||
| 
     | 
||
| 
     | 
||
| def _path_option_check(option: Option, opt: str, value: str) -> str: | ||
| return os.path.expanduser(value) | ||
| 
     | 
||
| 
          
            
          
           | 
    @@ -430,6 +447,22 @@ def constraints() -> Option: | |
| ) | ||
| 
     | 
||
| 
     | 
||
| def build_constraint() -> Option: | ||
| return Option( | ||
| "--build-constraint", | ||
| dest="build_constraints", | ||
| action="append", | ||
| type="str", | ||
| default=[], | ||
| metavar="file", | ||
| help=( | ||
| "Constrain build dependencies using the given constraints file. " | ||
| "This option can be used multiple times. " | ||
| "Requires --use-feature=build-constraint." | ||
| ), | ||
| ) | ||
| 
     | 
||
| 
     | 
||
| def requirements() -> Option: | ||
| return Option( | ||
| "-r", | ||
| 
          
            
          
           | 
    @@ -1072,6 +1105,7 @@ def check_list_path_option(options: Values) -> None: | |
| default=[], | ||
| choices=[ | ||
| "fast-deps", | ||
| "build-constraint", | ||
| ] | ||
| + ALWAYS_ENABLED_FEATURES, | ||
| help="Enable new functionality, that may be backward incompatible.", | ||
| 
          
            
          
           | 
    ||
Uh oh!
There was an error while loading. Please reload this page.