Merge pull request #11315 from bluetech/pytest-monkeypatch-chdir

pytester: use `monkeypatch.chdir()` for dir changing
This commit is contained in:
Ran Benita 2023-09-25 12:10:14 +03:00 committed by GitHub
commit 1a16bac131
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 47 additions and 59 deletions

View File

@ -0,0 +1,3 @@
The :fixture:`pytester` fixture now uses the :fixture:`monkeypatch` fixture to manage the current working directory.
If you use ``pytester`` in combination with :func:`monkeypatch.undo() <pytest.MonkeyPatch.undo>`, the CWD might get restored.
Use :func:`monkeypatch.context() <pytest.MonkeyPatch.context>` instead.

View File

@ -625,14 +625,6 @@ class RunResult:
) )
class CwdSnapshot:
def __init__(self) -> None:
self.__saved = os.getcwd()
def restore(self) -> None:
os.chdir(self.__saved)
class SysModulesSnapshot: class SysModulesSnapshot:
def __init__(self, preserve: Optional[Callable[[str], bool]] = None) -> None: def __init__(self, preserve: Optional[Callable[[str], bool]] = None) -> None:
self.__preserve = preserve self.__preserve = preserve
@ -696,15 +688,14 @@ class Pytester:
#: be added to the list. The type of items to add to the list depends on #: be added to the list. The type of items to add to the list depends on
#: the method using them so refer to them for details. #: the method using them so refer to them for details.
self.plugins: List[Union[str, _PluggyPlugin]] = [] self.plugins: List[Union[str, _PluggyPlugin]] = []
self._cwd_snapshot = CwdSnapshot()
self._sys_path_snapshot = SysPathsSnapshot() self._sys_path_snapshot = SysPathsSnapshot()
self._sys_modules_snapshot = self.__take_sys_modules_snapshot() self._sys_modules_snapshot = self.__take_sys_modules_snapshot()
self.chdir()
self._request.addfinalizer(self._finalize) self._request.addfinalizer(self._finalize)
self._method = self._request.config.getoption("--runpytest") self._method = self._request.config.getoption("--runpytest")
self._test_tmproot = tmp_path_factory.mktemp(f"tmp-{name}", numbered=True) self._test_tmproot = tmp_path_factory.mktemp(f"tmp-{name}", numbered=True)
self._monkeypatch = mp = monkeypatch self._monkeypatch = mp = monkeypatch
self.chdir()
mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self._test_tmproot)) mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self._test_tmproot))
# Ensure no unexpected caching via tox. # Ensure no unexpected caching via tox.
mp.delenv("TOX_ENV_DIR", raising=False) mp.delenv("TOX_ENV_DIR", raising=False)
@ -735,7 +726,6 @@ class Pytester:
""" """
self._sys_modules_snapshot.restore() self._sys_modules_snapshot.restore()
self._sys_path_snapshot.restore() self._sys_path_snapshot.restore()
self._cwd_snapshot.restore()
def __take_sys_modules_snapshot(self) -> SysModulesSnapshot: def __take_sys_modules_snapshot(self) -> SysModulesSnapshot:
# Some zope modules used by twisted-related tests keep internal state # Some zope modules used by twisted-related tests keep internal state
@ -760,7 +750,7 @@ class Pytester:
This is done automatically upon instantiation. This is done automatically upon instantiation.
""" """
os.chdir(self.path) self._monkeypatch.chdir(self.path)
def _makefile( def _makefile(
self, self,

View File

@ -1080,14 +1080,14 @@ class TestImport:
name = "pointsback123" name = "pointsback123"
ModuleType = type(os) ModuleType = type(os)
p = tmpdir.ensure(name + ".py") p = tmpdir.ensure(name + ".py")
with monkeypatch.context() as mp:
for ending in (".pyc", "$py.class", ".pyo"): for ending in (".pyc", "$py.class", ".pyo"):
mod = ModuleType(name) mod = ModuleType(name)
pseudopath = tmpdir.ensure(name + ending) pseudopath = tmpdir.ensure(name + ending)
mod.__file__ = str(pseudopath) mod.__file__ = str(pseudopath)
monkeypatch.setitem(sys.modules, name, mod) mp.setitem(sys.modules, name, mod)
newmod = p.pyimport() newmod = p.pyimport()
assert mod == newmod assert mod == newmod
monkeypatch.undo()
mod = ModuleType(name) mod = ModuleType(name)
pseudopath = tmpdir.ensure(name + "123.py") pseudopath = tmpdir.ensure(name + "123.py")
mod.__file__ = str(pseudopath) mod.__file__ = str(pseudopath)

View File

@ -854,7 +854,11 @@ raise ValueError()
reprtb = p.repr_traceback(excinfo) reprtb = p.repr_traceback(excinfo)
assert len(reprtb.reprentries) == 3 assert len(reprtb.reprentries) == 3
def test_traceback_short_no_source(self, importasmod, monkeypatch) -> None: def test_traceback_short_no_source(
self,
importasmod,
monkeypatch: pytest.MonkeyPatch,
) -> None:
mod = importasmod( mod = importasmod(
""" """
def func1(): def func1():
@ -866,14 +870,14 @@ raise ValueError()
excinfo = pytest.raises(ValueError, mod.entry) excinfo = pytest.raises(ValueError, mod.entry)
from _pytest._code.code import Code from _pytest._code.code import Code
monkeypatch.setattr(Code, "path", "bogus") with monkeypatch.context() as mp:
mp.setattr(Code, "path", "bogus")
p = FormattedExcinfo(style="short") p = FormattedExcinfo(style="short")
reprtb = p.repr_traceback_entry(excinfo.traceback[-2]) reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
lines = reprtb.lines lines = reprtb.lines
last_p = FormattedExcinfo(style="short") last_p = FormattedExcinfo(style="short")
last_reprtb = last_p.repr_traceback_entry(excinfo.traceback[-1], excinfo) last_reprtb = last_p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
last_lines = last_reprtb.lines last_lines = last_reprtb.lines
monkeypatch.undo()
assert lines[0] == " func1()" assert lines[0] == " func1()"
assert last_lines[0] == ' raise ValueError("hello")' assert last_lines[0] == ' raise ValueError("hello")'

View File

@ -895,7 +895,11 @@ def test_rewritten():
) )
@pytest.mark.skipif('"__pypy__" in sys.modules') @pytest.mark.skipif('"__pypy__" in sys.modules')
def test_pyc_vs_pyo(self, pytester: Pytester, monkeypatch) -> None: def test_pyc_vs_pyo(
self,
pytester: Pytester,
monkeypatch: pytest.MonkeyPatch,
) -> None:
pytester.makepyfile( pytester.makepyfile(
""" """
import pytest import pytest
@ -905,13 +909,13 @@ def test_rewritten():
) )
p = make_numbered_dir(root=Path(pytester.path), prefix="runpytest-") p = make_numbered_dir(root=Path(pytester.path), prefix="runpytest-")
tmp = "--basetemp=%s" % p tmp = "--basetemp=%s" % p
monkeypatch.setenv("PYTHONOPTIMIZE", "2") with monkeypatch.context() as mp:
monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False) mp.setenv("PYTHONOPTIMIZE", "2")
monkeypatch.delenv("PYTHONPYCACHEPREFIX", raising=False) mp.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
mp.delenv("PYTHONPYCACHEPREFIX", raising=False)
assert pytester.runpytest_subprocess(tmp).ret == 0 assert pytester.runpytest_subprocess(tmp).ret == 0
tagged = "test_pyc_vs_pyo." + PYTEST_TAG tagged = "test_pyc_vs_pyo." + PYTEST_TAG
assert tagged + ".pyo" in os.listdir("__pycache__") assert tagged + ".pyo" in os.listdir("__pycache__")
monkeypatch.undo()
monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False) monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
monkeypatch.delenv("PYTHONPYCACHEPREFIX", raising=False) monkeypatch.delenv("PYTHONPYCACHEPREFIX", raising=False)
assert pytester.runpytest_subprocess(tmp).ret == 1 assert pytester.runpytest_subprocess(tmp).ret == 1

View File

@ -236,15 +236,15 @@ class TestImportPath:
name = "pointsback123" name = "pointsback123"
p = tmp_path.joinpath(name + ".py") p = tmp_path.joinpath(name + ".py")
p.touch() p.touch()
with monkeypatch.context() as mp:
for ending in (".pyc", ".pyo"): for ending in (".pyc", ".pyo"):
mod = ModuleType(name) mod = ModuleType(name)
pseudopath = tmp_path.joinpath(name + ending) pseudopath = tmp_path.joinpath(name + ending)
pseudopath.touch() pseudopath.touch()
mod.__file__ = str(pseudopath) mod.__file__ = str(pseudopath)
monkeypatch.setitem(sys.modules, name, mod) mp.setitem(sys.modules, name, mod)
newmod = import_path(p, root=tmp_path) newmod = import_path(p, root=tmp_path)
assert mod == newmod assert mod == newmod
monkeypatch.undo()
mod = ModuleType(name) mod = ModuleType(name)
pseudopath = tmp_path.joinpath(name + "123.py") pseudopath = tmp_path.joinpath(name + "123.py")
pseudopath.touch() pseudopath.touch()

View File

@ -2,7 +2,6 @@ import os
import subprocess import subprocess
import sys import sys
import time import time
from pathlib import Path
from types import ModuleType from types import ModuleType
from typing import List from typing import List
@ -11,7 +10,6 @@ import pytest
from _pytest.config import ExitCode from _pytest.config import ExitCode
from _pytest.config import PytestPluginManager from _pytest.config import PytestPluginManager
from _pytest.monkeypatch import MonkeyPatch from _pytest.monkeypatch import MonkeyPatch
from _pytest.pytester import CwdSnapshot
from _pytest.pytester import HookRecorder from _pytest.pytester import HookRecorder
from _pytest.pytester import LineMatcher from _pytest.pytester import LineMatcher
from _pytest.pytester import Pytester from _pytest.pytester import Pytester
@ -301,17 +299,6 @@ def test_assert_outcomes_after_pytest_error(pytester: Pytester) -> None:
result.assert_outcomes(passed=0) result.assert_outcomes(passed=0)
def test_cwd_snapshot(pytester: Pytester) -> None:
foo = pytester.mkdir("foo")
bar = pytester.mkdir("bar")
os.chdir(foo)
snapshot = CwdSnapshot()
os.chdir(bar)
assert Path().absolute() == bar
snapshot.restore()
assert Path().absolute() == foo
class TestSysModulesSnapshot: class TestSysModulesSnapshot:
key = "my-test-module" key = "my-test-module"