11
11
from collections import OrderedDict
12
12
from collections .abc import Iterable
13
13
from types import TracebackType
14
- from typing import TYPE_CHECKING , Protocol
14
+ from typing import TYPE_CHECKING , Protocol , TypedDict
15
15
16
16
from pip ._vendor .packaging .version import Version
17
17
18
18
from pip import __file__ as pip_location
19
19
from pip ._internal .cli .spinners import open_spinner
20
20
from pip ._internal .locations import get_platlib , get_purelib , get_scheme
21
21
from pip ._internal .metadata import get_default_environment , get_environment
22
+ from pip ._internal .utils .deprecation import deprecated
22
23
from pip ._internal .utils .logging import VERBOSE
23
24
from pip ._internal .utils .packaging import get_requirement
24
25
from pip ._internal .utils .subprocess import call_subprocess
31
32
logger = logging .getLogger (__name__ )
32
33
33
34
35
+ class ExtraEnviron (TypedDict , total = False ):
36
+ extra_environ : dict [str , str ]
37
+
38
+
34
39
def _dedup (a : str , b : str ) -> tuple [str ] | tuple [str , str ]:
35
40
return (a , b ) if a != b else (a ,)
36
41
@@ -101,8 +106,49 @@ class SubprocessBuildEnvironmentInstaller:
101
106
Install build dependencies by calling pip in a subprocess.
102
107
"""
103
108
104
- def __init__ (self , finder : PackageFinder ) -> None :
109
+ def __init__ (
110
+ self ,
111
+ finder : PackageFinder ,
112
+ build_constraints : list [str ] | None = None ,
113
+ build_constraint_feature_enabled : bool = False ,
114
+ constraints : list [str ] | None = None ,
115
+ ) -> None :
105
116
self .finder = finder
117
+ self ._build_constraints = build_constraints or []
118
+ self ._build_constraint_feature_enabled = build_constraint_feature_enabled
119
+ self ._constraints = constraints or []
120
+
121
+ def _deprecation_constraint_check (self ) -> None :
122
+ """
123
+ Check for deprecation warning: PIP_CONSTRAINT affecting build environments.
124
+
125
+ This warns when build-constraint feature is NOT enabled but regular constraints
126
+ match what PIP_CONSTRAINT environment variable points to.
127
+ """
128
+ if self ._build_constraint_feature_enabled :
129
+ return
130
+
131
+ if not self ._constraints :
132
+ return
133
+
134
+ if not os .environ .get ("PIP_CONSTRAINT" ):
135
+ return
136
+
137
+ pip_constraint_files = [
138
+ f .strip () for f in os .environ ["PIP_CONSTRAINT" ].split () if f .strip ()
139
+ ]
140
+ if pip_constraint_files and set (pip_constraint_files ) == set (self ._constraints ):
141
+ deprecated (
142
+ reason = (
143
+ "Setting PIP_CONSTRAINT will not affect "
144
+ "build constraints in the future,"
145
+ ),
146
+ replacement = (
147
+ 'PIP_BUILD_CONSTRAINT with PIP_USE_FEATURE="build-constraint"'
148
+ ),
149
+ gone_in = "26.2" ,
150
+ issue = None ,
151
+ )
106
152
107
153
def install (
108
154
self ,
@@ -112,6 +158,8 @@ def install(
112
158
kind : str ,
113
159
for_req : InstallRequirement | None ,
114
160
) -> None :
161
+ self ._deprecation_constraint_check ()
162
+
115
163
finder = self .finder
116
164
args : list [str ] = [
117
165
sys .executable ,
@@ -167,13 +215,32 @@ def install(
167
215
args .append ("--pre" )
168
216
if finder .prefer_binary :
169
217
args .append ("--prefer-binary" )
218
+
219
+ # Handle build constraints
220
+ extra_environ : ExtraEnviron = {}
221
+ if self ._build_constraint_feature_enabled :
222
+ # Build constraints must be passed as both constraints
223
+ # and build constraints to the subprocess
224
+ for constraint_file in self ._build_constraints :
225
+ args .extend (["--constraint" , constraint_file ])
226
+ args .extend (["--build-constraint" , constraint_file ])
227
+ args .extend (["--use-feature" , "build-constraint" ])
228
+
229
+ # If there are no build constraints but the build constraint
230
+ # process is enabled then we must ignore regular constraints
231
+ if not self ._build_constraints :
232
+ extra_environ = {
233
+ "extra_environ" : {"_PIP_IN_BUILD_IGNORE_CONSTRAINTS" : "1" }
234
+ }
235
+
170
236
args .append ("--" )
171
237
args .extend (requirements )
172
238
with open_spinner (f"Installing { kind } " ) as spinner :
173
239
call_subprocess (
174
240
args ,
175
241
command_desc = f"pip subprocess to install { kind } " ,
176
242
spinner = spinner ,
243
+ ** extra_environ ,
177
244
)
178
245
179
246
0 commit comments