@@ -99,6 +99,9 @@ class VirtualPythonEnvironment(Environment):
99
99
The path of the python venv.
100
100
creator : Callable[[Path], Any], optional
101
101
A callable for creating the venv, by default None
102
+ env : dict[str, str], optional
103
+ A dictionary of environment variables which are overridden in the
104
+ virtual environment, by default None
102
105
103
106
Attributes
104
107
----------
@@ -108,6 +111,8 @@ class VirtualPythonEnvironment(Environment):
108
111
The name of the environment.
109
112
venv : Path
110
113
The path of the python venv.
114
+ env : dict
115
+ The user-specified environment variables for the virtual environment.
111
116
112
117
"""
113
118
@@ -118,6 +123,7 @@ def __init__(
118
123
venv : str | Path ,
119
124
* ,
120
125
creator : Callable [[Path ], Any ] | None = None ,
126
+ env : dict [str , str ] | None = None ,
121
127
):
122
128
"""
123
129
Environment for building inside a python virtual environment.
@@ -132,11 +138,15 @@ def __init__(
132
138
The path of the python venv.
133
139
creator : Callable[[Path], Any], optional
134
140
A callable for creating the venv, by default None
141
+ env : dict[str, str], optional
142
+ A dictionary of environment variables which are forwarded to the
143
+ virtual environment, by default None
135
144
136
145
"""
137
146
super ().__init__ (path , name )
138
147
self .venv = Path (venv ).resolve ()
139
148
self ._creator = creator
149
+ self .env = env or {}
140
150
141
151
async def create_venv (self ) -> None :
142
152
"""
@@ -191,6 +201,45 @@ def activate(self, env: dict[str, str]) -> dict[str, str]:
191
201
env ["PATH" ] = str (self .venv / "bin" ) + ":" + env ["PATH" ]
192
202
return env
193
203
204
+ def apply_overrides (self , env : dict [str , str ]) -> dict [str , str ]:
205
+ """
206
+ Prepare the environment for the build.
207
+
208
+ This method is used to modify the environment before running a
209
+ build command. It :py:meth:`activates <activate>` the python venv
210
+ and overrides those environment variables that were passed to the
211
+ :py:class:`constructor <VirtualPythonEnvironment>`.
212
+ `PATH` is never replaced but extended instead.
213
+
214
+ .. warning:: This modifies the given dictionary in-place.
215
+
216
+ Parameters
217
+ ----------
218
+ env : dict[str, str]
219
+ The environment to modify.
220
+
221
+ Returns
222
+ -------
223
+ dict[str, str]
224
+ The updated environment.
225
+
226
+ """
227
+ # add user-supplied values to env
228
+ for key , value in self .env .items ():
229
+ if key == "PATH" :
230
+ # extend PATH instead of replacing
231
+ env ["PATH" ] = value + ":" + env ["PATH" ]
232
+ continue
233
+ if key in env :
234
+ logger .info (
235
+ "Overwriting environment variable %s=%s with user-specified value '%s'." ,
236
+ key ,
237
+ env [key ],
238
+ value ,
239
+ )
240
+ env [key ] = value
241
+ return env
242
+
194
243
async def run (
195
244
self , * cmd : str , ** kwargs : Any
196
245
) -> Tuple [str | bytes | None , str | bytes | None , int ]:
@@ -199,8 +248,9 @@ async def run(
199
248
200
249
This implementation passes the arguments to
201
250
:func:`asyncio.create_subprocess_exec`. But alters `env` to
202
- activate the correct python venv. If a python venv is already activated
203
- this activation is overridden.
251
+ :py:meth:`activates <activate>` the correct python
252
+ and overrides the use-specified vars using :py:meth:`prepare_env`.
253
+ If a python venv is already activated this activation is overridden.
204
254
205
255
Returns
206
256
-------
@@ -213,7 +263,9 @@ async def run(
213
263
214
264
"""
215
265
# activate venv
216
- kwargs ["env" ] = self .activate (kwargs .get ("env" , os .environ ).copy ())
266
+ kwargs ["env" ] = self .activate (
267
+ self .apply_overrides (kwargs .get ("env" , os .environ ).copy ())
268
+ )
217
269
return await super ().run (* cmd , ** kwargs )
218
270
219
271
@@ -232,10 +284,20 @@ class Poetry(VirtualPythonEnvironment):
232
284
The name of the environment (usually the name of the revision).
233
285
args : Iterable[str]
234
286
The cmd arguments to pass to `poetry install`.
287
+ env : dict[str, str], optional
288
+ A dictionary of environment variables which are overidden in the
289
+ virtual environment, by default None
235
290
236
291
"""
237
292
238
- def __init__ (self , path : Path , name : str , * , args : Iterable [str ]):
293
+ def __init__ (
294
+ self ,
295
+ path : Path ,
296
+ name : str ,
297
+ * ,
298
+ args : Iterable [str ],
299
+ env : dict [str , str ] | None = None ,
300
+ ):
239
301
"""
240
302
Build Environment for isolated builds using poetry.
241
303
@@ -247,12 +309,16 @@ def __init__(self, path: Path, name: str, *, args: Iterable[str]):
247
309
The name of the environment (usually the name of the revision).
248
310
args : Iterable[str]
249
311
The cmd arguments to pass to `poetry install`.
312
+ env : dict[str, str], optional
313
+ A dictionary of environment variables which are forwarded to the
314
+ virtual environment, by default None
250
315
251
316
"""
252
317
super ().__init__ (
253
318
path ,
254
319
name ,
255
320
path / ".venv" , # placeholder, determined later
321
+ env = env ,
256
322
)
257
323
self .args = args
258
324
@@ -273,6 +339,8 @@ async def __aenter__(self) -> Self:
273
339
cmd += self .args
274
340
275
341
env = os .environ .copy ()
342
+ self .apply_overrides (env )
343
+
276
344
env .pop ("VIRTUAL_ENV" , None ) # unset poetry env
277
345
env ["POETRY_VIRTUALENVS_IN_PROJECT" ] = "False"
278
346
venv_path = self .path / ".venv"
@@ -344,6 +412,9 @@ class Pip(VirtualPythonEnvironment):
344
412
The cmd arguments to pass to `pip install`.
345
413
creator : Callable[[Path], Any] | None, optional
346
414
A callable for creating the venv, by default None
415
+ env : dict[str, str], optional
416
+ A dictionary of environment variables which are overridden in the
417
+ virtual environment, by default None
347
418
348
419
"""
349
420
@@ -355,6 +426,7 @@ def __init__(
355
426
* ,
356
427
args : Iterable [str ],
357
428
creator : Callable [[Path ], Any ] | None = None ,
429
+ env : dict [str , str ] | None = None ,
358
430
):
359
431
"""
360
432
Build Environment for using a venv and pip.
@@ -371,9 +443,12 @@ def __init__(
371
443
The cmd arguments to pass to `pip install`.
372
444
creator : Callable[[Path], Any], optional
373
445
A callable for creating the venv, by default None
446
+ env : dict[str, str], optional
447
+ A dictionary of environment variables which are overridden in the
448
+ virtual environment, by default None
374
449
375
450
"""
376
- super ().__init__ (path , name , venv , creator = creator )
451
+ super ().__init__ (path , name , venv , creator = creator , env = env )
377
452
self .args = args
378
453
379
454
async def __aenter__ (self ) -> Self :
@@ -396,7 +471,7 @@ async def __aenter__(self) -> Self:
396
471
process = await asyncio .create_subprocess_exec (
397
472
* cmd ,
398
473
cwd = self .path ,
399
- env = self .activate (os .environ .copy ()),
474
+ env = self .activate (self . apply_overrides ( os .environ .copy () )),
400
475
stdout = PIPE ,
401
476
stderr = PIPE ,
402
477
)
0 commit comments