5
5
import re
6
6
import sys
7
7
import warnings
8
+ from itertools import chain
8
9
9
10
import py
10
11
@@ -231,7 +232,7 @@ def _needs_reinstall(self, setupdir, action):
231
232
setup_py = setupdir .join ("setup.py" )
232
233
setup_cfg = setupdir .join ("setup.cfg" )
233
234
args = [self .envconfig .envpython , str (setup_py ), "--name" ]
234
- env = self ._getenv ()
235
+ env = self ._get_os_environ ()
235
236
output = action .popen (args , cwd = setupdir , redirect = False , returnout = True , env = env )
236
237
name = output .strip ()
237
238
args = [self .envconfig .envpython , "-c" , "import sys; print(sys.path)" ]
@@ -297,37 +298,51 @@ def _installopts(self, indexserver):
297
298
return options
298
299
299
300
def run_install_command (self , packages , action , options = ()):
300
- argv = self .envconfig .install_command [:]
301
- i = argv .index ("{packages}" )
302
- argv [i : i + 1 ] = packages
303
- if "{opts}" in argv :
304
- i = argv .index ("{opts}" )
305
- argv [i : i + 1 ] = list (options )
301
+ def expand (val ):
302
+ # expand an install command
303
+ if val == "{packages}" :
304
+ for package in packages :
305
+ yield package
306
+ elif val == "{opts}" :
307
+ for opt in options :
308
+ yield opt
309
+ else :
310
+ yield val
306
311
307
- for x in ("PIP_RESPECT_VIRTUALENV" , "PIP_REQUIRE_VIRTUALENV" , "__PYVENV_LAUNCHER__" ):
308
- os .environ .pop (x , None )
312
+ cmd = list (chain .from_iterable (expand (val ) for val in self .envconfig .install_command ))
309
313
310
- if "PYTHONPATH" not in self .envconfig .passenv :
311
- # If PYTHONPATH not explicitly asked for, remove it.
312
- if "PYTHONPATH" in os .environ :
313
- self .session .report .warning (
314
- "Discarding $PYTHONPATH from environment, to override "
315
- "specify PYTHONPATH in 'passenv' in your configuration."
316
- )
317
- os .environ .pop ("PYTHONPATH" )
314
+ self .ensure_pip_os_environ_ok ()
318
315
319
316
old_stdout = sys .stdout
320
317
sys .stdout = codecs .getwriter ("utf8" )(sys .stdout )
321
318
try :
322
319
self ._pcall (
323
- argv ,
320
+ cmd ,
324
321
cwd = self .envconfig .config .toxinidir ,
325
322
action = action ,
326
323
redirect = self .session .report .verbosity < 2 ,
327
324
)
328
325
finally :
329
326
sys .stdout = old_stdout
330
327
328
+ def ensure_pip_os_environ_ok (self ):
329
+ for key in ("PIP_RESPECT_VIRTUALENV" , "PIP_REQUIRE_VIRTUALENV" , "__PYVENV_LAUNCHER__" ):
330
+ os .environ .pop (key , None )
331
+ if "PYTHONPATH" not in self .envconfig .passenv :
332
+ # If PYTHONPATH not explicitly asked for, remove it.
333
+ if "PYTHONPATH" in os .environ :
334
+ self .session .report .warning (
335
+ "Discarding $PYTHONPATH from environment, to override "
336
+ "specify PYTHONPATH in 'passenv' in your configuration."
337
+ )
338
+ os .environ .pop ("PYTHONPATH" )
339
+
340
+ # installing packages at user level may mean we're not installing inside the venv
341
+ os .environ ["PIP_USER" ] = "0"
342
+
343
+ # installing without dependencies may lead to broken packages
344
+ os .environ ["PIP_NO_DEPS" ] = "0"
345
+
331
346
def _install (self , deps , extraopts = None , action = None ):
332
347
if not deps :
333
348
return
@@ -353,13 +368,13 @@ def _install(self, deps, extraopts=None, action=None):
353
368
options .extend (extraopts )
354
369
self .run_install_command (packages = packages , options = options , action = action )
355
370
356
- def _getenv (self , testcommand = False ):
357
- if testcommand :
371
+ def _get_os_environ (self , is_test_command = False ):
372
+ if is_test_command :
358
373
# for executing tests we construct a clean environment
359
374
env = {}
360
- for envname in self .envconfig .passenv :
361
- if envname in os .environ :
362
- env [envname ] = os .environ [envname ]
375
+ for env_key in self .envconfig .passenv :
376
+ if env_key in os .environ :
377
+ env [env_key ] = os .environ [env_key ]
363
378
else :
364
379
# for executing non-test commands we use the full
365
380
# invocation environment
@@ -377,7 +392,7 @@ def test(self, redirect=False):
377
392
self .session .make_emptydir (self .envconfig .envtmpdir )
378
393
self .envconfig .envtmpdir .ensure (dir = 1 )
379
394
cwd = self .envconfig .changedir
380
- env = self ._getenv ( testcommand = True )
395
+ env = self ._get_os_environ ( is_test_command = True )
381
396
# Display PYTHONHASHSEED to assist with reproducibility.
382
397
action .setactivity ("runtests" , "PYTHONHASHSEED={!r}" .format (env .get ("PYTHONHASHSEED" )))
383
398
for i , argv in enumerate (self .envconfig .commands ):
@@ -405,7 +420,7 @@ def test(self, redirect=False):
405
420
action = action ,
406
421
redirect = redirect ,
407
422
ignore_ret = ignore_ret ,
408
- testcommand = True ,
423
+ is_test_command = True ,
409
424
)
410
425
except tox .exception .InvocationError as err :
411
426
if self .envconfig .ignore_outcome :
@@ -424,18 +439,28 @@ def test(self, redirect=False):
424
439
raise
425
440
426
441
def _pcall (
427
- self , args , cwd , venv = True , testcommand = False , action = None , redirect = True , ignore_ret = False
442
+ self ,
443
+ args ,
444
+ cwd ,
445
+ venv = True ,
446
+ is_test_command = False ,
447
+ action = None ,
448
+ redirect = True ,
449
+ ignore_ret = False ,
428
450
):
451
+ # construct environment variables
429
452
os .environ .pop ("VIRTUALENV_PYTHON" , None )
453
+ env = self ._get_os_environ (is_test_command = is_test_command )
454
+ bin_dir = str (self .envconfig .envbindir )
455
+ env ["PATH" ] = os .pathsep .join ([bin_dir , os .environ ["PATH" ]])
456
+ self .session .report .verbosity2 ("setting PATH={}" .format (env ["PATH" ]))
430
457
431
- cwd . ensure ( dir = 1 )
458
+ # get command
432
459
args [0 ] = self .getcommandpath (args [0 ], venv , cwd )
433
460
if sys .platform != "win32" and "TOX_LIMITED_SHEBANG" in os .environ :
434
461
args = prepend_shebang_interpreter (args )
435
- env = self ._getenv (testcommand = testcommand )
436
- bindir = str (self .envconfig .envbindir )
437
- env ["PATH" ] = p = os .pathsep .join ([bindir , os .environ ["PATH" ]])
438
- self .session .report .verbosity2 ("setting PATH={}" .format (p ))
462
+
463
+ cwd .ensure (dir = 1 ) # ensure the cwd exists
439
464
return action .popen (args , cwd = cwd , env = env , redirect = redirect , ignore_ret = ignore_ret )
440
465
441
466
0 commit comments