30
30
31
31
import py .path
32
32
33
+ import _pytest .terminal
33
34
import pytest
34
35
from _pytest ._code import Source
35
36
from _pytest .capture import CLOSE_STDIN
56
57
57
58
58
59
if TYPE_CHECKING :
60
+ from typing import Any
59
61
from typing import Type
60
62
from typing_extensions import Literal # noqa: F401
61
63
@@ -542,20 +544,53 @@ def _display_running(header: str, *args: str) -> None:
542
544
543
545
544
546
class PytesterManageEnv :
547
+ """Setup/activate testdir's monkeypatching only during test calls.
548
+
549
+ When it would be done via the instance/fixture directly it would also be
550
+ active during teardown (e.g. with the terminal plugin's reporting), where
551
+ it might mess with the column width etc.
552
+ """
545
553
@pytest .hookimpl (hookwrapper = True , tryfirst = True )
546
554
def pytest_runtest_setup (self ):
547
- initial_home = os .getenv ("HOME" )
555
+ self ._initial_env = {
556
+ k : os .getenv (k )
557
+ for k in (
558
+ "HOME" ,
559
+ "PYTEST_ADDOPTS" ,
560
+ "PYTEST_DEBUG_TEMPROOT" ,
561
+ "PY_COLORS" ,
562
+ "TOX_ENV_DIR" ,
563
+ "USERPROFILE" ,
564
+ )
565
+ }
566
+ self ._initial_attr = {
567
+ (
568
+ _pytest .terminal ,
569
+ "get_terminal_width" ,
570
+ ): _pytest .terminal .get_terminal_width ,
571
+ }
548
572
yield
549
- self ._initial_home_changed = os .getenv ("HOME" ) != initial_home
573
+
574
+ def is_unchanged (self , initial : "Any" , current : "Any" ) -> bool :
575
+ if initial is None :
576
+ return current is None
577
+ else :
578
+ return bool (initial == current )
579
+
580
+ def setenv (self , mp : "MonkeyPatch" , key : str , value : "Optional[str]" ) -> None :
581
+ initial = self ._initial_env [key ]
582
+ current = os .getenv (key )
583
+ if self .is_unchanged (initial , current ):
584
+ mp .setenv (key , value )
585
+
586
+ def setattr (self , mp : "MonkeyPatch" , target : object , name : str , value : "Any" ) -> None :
587
+ initial = self ._initial_attr [(target , name )]
588
+ current = getattr (target , name )
589
+ if self .is_unchanged (initial , current ):
590
+ mp .setattr (target , name , value )
550
591
551
592
@pytest .hookimpl (hookwrapper = True , trylast = True )
552
593
def pytest_runtest_call (self , item : Function ) -> Generator [None , None , None ]:
553
- """Setup/activate testdir's monkeypatching only during test calls.
554
-
555
- When it would be done via the instance/fixture directly it would also be
556
- active during teardown (e.g. with the terminal plugin's reporting), where
557
- it might mess with the column width etc.
558
- """
559
594
try :
560
595
funcargs = item .funcargs
561
596
except AttributeError :
@@ -567,20 +602,19 @@ def pytest_runtest_call(self, item: Function) -> Generator[None, None, None]:
567
602
return
568
603
569
604
mp = testdir .monkeypatch
570
- mp .setenv ("PYTEST_DEBUG_TEMPROOT" , str (testdir .test_tmproot ))
605
+ self .setenv (mp , "PYTEST_DEBUG_TEMPROOT" , str (testdir .test_tmproot ))
571
606
# Ensure no unexpected caching via tox.
572
- mp . delenv ( "TOX_ENV_DIR" , raising = False )
607
+ self . setenv ( mp , "TOX_ENV_DIR" , None )
573
608
# Discard outer pytest options.
574
- mp . delenv ( "PYTEST_ADDOPTS" , raising = False )
609
+ self . setenv ( mp , "PYTEST_ADDOPTS" , None )
575
610
# Ensure no user config is used.
576
- if not self ._initial_home_changed :
577
- tmphome = str (testdir .tmpdir )
578
- mp .setenv ("HOME" , tmphome )
579
- mp .setenv ("USERPROFILE" , tmphome )
611
+ tmphome = str (testdir .tmpdir )
612
+ self .setenv (mp , "HOME" , tmphome )
613
+ self .setenv (mp , "USERPROFILE" , tmphome )
580
614
# Do not use colors for inner runs by default.
581
- mp .setenv ("PY_COLORS" , "0" )
615
+ self .setenv (mp , "PY_COLORS" , "0" )
582
616
583
- mp .setattr (" _pytest.terminal. get_terminal_width" , lambda : 80 )
617
+ self .setattr (mp , _pytest .terminal , " get_terminal_width" , lambda : 80 )
584
618
try :
585
619
yield
586
620
finally :
0 commit comments