Skip to content

Commit

Permalink
Merge pull request #11315 from bluetech/pytest-monkeypatch-chdir
Browse files Browse the repository at this point in the history
pytester: use `monkeypatch.chdir()` for dir changing
  • Loading branch information
bluetech committed Sep 25, 2023
2 parents 486a9ed + 81192ca commit 1a16bac
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 59 deletions.
3 changes: 3 additions & 0 deletions changelog/11315.trivial.rst
@@ -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.
14 changes: 2 additions & 12 deletions src/_pytest/pytester.py
Expand Up @@ -625,14 +625,6 @@ def assert_outcomes(
)


class CwdSnapshot:
def __init__(self) -> None:
self.__saved = os.getcwd()

def restore(self) -> None:
os.chdir(self.__saved)


class SysModulesSnapshot:
def __init__(self, preserve: Optional[Callable[[str], bool]] = None) -> None:
self.__preserve = preserve
Expand Down Expand Up @@ -696,15 +688,14 @@ def __init__(
#: 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.
self.plugins: List[Union[str, _PluggyPlugin]] = []
self._cwd_snapshot = CwdSnapshot()
self._sys_path_snapshot = SysPathsSnapshot()
self._sys_modules_snapshot = self.__take_sys_modules_snapshot()
self.chdir()
self._request.addfinalizer(self._finalize)
self._method = self._request.config.getoption("--runpytest")
self._test_tmproot = tmp_path_factory.mktemp(f"tmp-{name}", numbered=True)

self._monkeypatch = mp = monkeypatch
self.chdir()
mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self._test_tmproot))
# Ensure no unexpected caching via tox.
mp.delenv("TOX_ENV_DIR", raising=False)
Expand Down Expand Up @@ -735,7 +726,6 @@ def _finalize(self) -> None:
"""
self._sys_modules_snapshot.restore()
self._sys_path_snapshot.restore()
self._cwd_snapshot.restore()

def __take_sys_modules_snapshot(self) -> SysModulesSnapshot:
# Some zope modules used by twisted-related tests keep internal state
Expand All @@ -760,7 +750,7 @@ def chdir(self) -> None:
This is done automatically upon instantiation.
"""
os.chdir(self.path)
self._monkeypatch.chdir(self.path)

def _makefile(
self,
Expand Down
16 changes: 8 additions & 8 deletions testing/_py/test_local.py
Expand Up @@ -1080,14 +1080,14 @@ def test_pyimport_check_filepath_consistency(self, monkeypatch, tmpdir):
name = "pointsback123"
ModuleType = type(os)
p = tmpdir.ensure(name + ".py")
for ending in (".pyc", "$py.class", ".pyo"):
mod = ModuleType(name)
pseudopath = tmpdir.ensure(name + ending)
mod.__file__ = str(pseudopath)
monkeypatch.setitem(sys.modules, name, mod)
newmod = p.pyimport()
assert mod == newmod
monkeypatch.undo()
with monkeypatch.context() as mp:
for ending in (".pyc", "$py.class", ".pyo"):
mod = ModuleType(name)
pseudopath = tmpdir.ensure(name + ending)
mod.__file__ = str(pseudopath)
mp.setitem(sys.modules, name, mod)
newmod = p.pyimport()
assert mod == newmod
mod = ModuleType(name)
pseudopath = tmpdir.ensure(name + "123.py")
mod.__file__ = str(pseudopath)
Expand Down
22 changes: 13 additions & 9 deletions testing/code/test_excinfo.py
Expand Up @@ -854,7 +854,11 @@ def entry():
reprtb = p.repr_traceback(excinfo)
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(
"""
def func1():
Expand All @@ -866,14 +870,14 @@ def entry():
excinfo = pytest.raises(ValueError, mod.entry)
from _pytest._code.code import Code

monkeypatch.setattr(Code, "path", "bogus")
p = FormattedExcinfo(style="short")
reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
lines = reprtb.lines
last_p = FormattedExcinfo(style="short")
last_reprtb = last_p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
last_lines = last_reprtb.lines
monkeypatch.undo()
with monkeypatch.context() as mp:
mp.setattr(Code, "path", "bogus")
p = FormattedExcinfo(style="short")
reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
lines = reprtb.lines
last_p = FormattedExcinfo(style="short")
last_reprtb = last_p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
last_lines = last_reprtb.lines
assert lines[0] == " func1()"

assert last_lines[0] == ' raise ValueError("hello")'
Expand Down
20 changes: 12 additions & 8 deletions testing/test_assertrewrite.py
Expand Up @@ -895,7 +895,11 @@ def test_foo():
)

@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(
"""
import pytest
Expand All @@ -905,13 +909,13 @@ def test_optimized():
)
p = make_numbered_dir(root=Path(pytester.path), prefix="runpytest-")
tmp = "--basetemp=%s" % p
monkeypatch.setenv("PYTHONOPTIMIZE", "2")
monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
monkeypatch.delenv("PYTHONPYCACHEPREFIX", raising=False)
assert pytester.runpytest_subprocess(tmp).ret == 0
tagged = "test_pyc_vs_pyo." + PYTEST_TAG
assert tagged + ".pyo" in os.listdir("__pycache__")
monkeypatch.undo()
with monkeypatch.context() as mp:
mp.setenv("PYTHONOPTIMIZE", "2")
mp.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
mp.delenv("PYTHONPYCACHEPREFIX", raising=False)
assert pytester.runpytest_subprocess(tmp).ret == 0
tagged = "test_pyc_vs_pyo." + PYTEST_TAG
assert tagged + ".pyo" in os.listdir("__pycache__")
monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
monkeypatch.delenv("PYTHONPYCACHEPREFIX", raising=False)
assert pytester.runpytest_subprocess(tmp).ret == 1
Expand Down
18 changes: 9 additions & 9 deletions testing/test_pathlib.py
Expand Up @@ -236,15 +236,15 @@ def test_check_filepath_consistency(
name = "pointsback123"
p = tmp_path.joinpath(name + ".py")
p.touch()
for ending in (".pyc", ".pyo"):
mod = ModuleType(name)
pseudopath = tmp_path.joinpath(name + ending)
pseudopath.touch()
mod.__file__ = str(pseudopath)
monkeypatch.setitem(sys.modules, name, mod)
newmod = import_path(p, root=tmp_path)
assert mod == newmod
monkeypatch.undo()
with monkeypatch.context() as mp:
for ending in (".pyc", ".pyo"):
mod = ModuleType(name)
pseudopath = tmp_path.joinpath(name + ending)
pseudopath.touch()
mod.__file__ = str(pseudopath)
mp.setitem(sys.modules, name, mod)
newmod = import_path(p, root=tmp_path)
assert mod == newmod
mod = ModuleType(name)
pseudopath = tmp_path.joinpath(name + "123.py")
pseudopath.touch()
Expand Down
13 changes: 0 additions & 13 deletions testing/test_pytester.py
Expand Up @@ -2,7 +2,6 @@
import subprocess
import sys
import time
from pathlib import Path
from types import ModuleType
from typing import List

Expand All @@ -11,7 +10,6 @@
from _pytest.config import ExitCode
from _pytest.config import PytestPluginManager
from _pytest.monkeypatch import MonkeyPatch
from _pytest.pytester import CwdSnapshot
from _pytest.pytester import HookRecorder
from _pytest.pytester import LineMatcher
from _pytest.pytester import Pytester
Expand Down Expand Up @@ -301,17 +299,6 @@ def test_assert_outcomes_after_pytest_error(pytester: Pytester) -> None:
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:
key = "my-test-module"

Expand Down

0 comments on commit 1a16bac

Please sign in to comment.