Skip to content

Commit e986d84

Browse files
authored
Merge pull request #8006 from bluetech/export-MonkeyPatch
Export MonkeyPatch as pytest.MonkeyPatch
2 parents 7aa5e49 + 6f13d1b commit e986d84

File tree

5 files changed

+36
-8
lines changed

5 files changed

+36
-8
lines changed

changelog/8006.feature.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
It is now possible to construct a :class:`~pytest.MonkeyPatch` object directly as ``pytest.MonkeyPatch()``,
2+
in cases when the :fixture:`monkeypatch` fixture cannot be used. Previously some users imported it
3+
from the private `_pytest.monkeypatch.MonkeyPatch` namespace.
4+
5+
Additionally, :meth:`MonkeyPatch.context <pytest.MonkeyPatch.context>` is now a classmethod,
6+
and can be used as ``with MonkeyPatch.context() as mp: ...``. This is the recommended way to use
7+
``MonkeyPatch`` directly, since unlike the ``monkeypatch`` fixture, an instance created directly
8+
is not ``undo()``-ed automatically.

doc/en/reference.rst

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -486,16 +486,14 @@ caplog
486486
monkeypatch
487487
~~~~~~~~~~~
488488

489-
.. currentmodule:: _pytest.monkeypatch
490-
491489
**Tutorial**: :doc:`monkeypatch`.
492490

493491
.. autofunction:: _pytest.monkeypatch.monkeypatch()
494492
:no-auto-options:
495493

496-
Returns a :class:`MonkeyPatch` instance.
494+
Returns a :class:`~pytest.MonkeyPatch` instance.
497495

498-
.. autoclass:: _pytest.monkeypatch.MonkeyPatch
496+
.. autoclass:: pytest.MonkeyPatch
499497
:members:
500498

501499

src/_pytest/monkeypatch.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,17 +111,27 @@ def __repr__(self) -> str:
111111

112112
@final
113113
class MonkeyPatch:
114-
"""Object returned by the ``monkeypatch`` fixture keeping a record of
115-
setattr/item/env/syspath changes."""
114+
"""Helper to conveniently monkeypatch attributes/items/environment
115+
variables/syspath.
116+
117+
Returned by the :fixture:`monkeypatch` fixture.
118+
119+
:versionchanged:: 6.2
120+
Can now also be used directly as `pytest.MonkeyPatch()`, for when
121+
the fixture is not available. In this case, use
122+
:meth:`with MonkeyPatch.context() as mp: <context>` or remember to call
123+
:meth:`undo` explicitly.
124+
"""
116125

117126
def __init__(self) -> None:
118127
self._setattr: List[Tuple[object, str, object]] = []
119128
self._setitem: List[Tuple[MutableMapping[Any, Any], object, object]] = ([])
120129
self._cwd: Optional[str] = None
121130
self._savesyspath: Optional[List[str]] = None
122131

132+
@classmethod
123133
@contextmanager
124-
def context(self) -> Generator["MonkeyPatch", None, None]:
134+
def context(cls) -> Generator["MonkeyPatch", None, None]:
125135
"""Context manager that returns a new :class:`MonkeyPatch` object
126136
which undoes any patching done inside the ``with`` block upon exit.
127137
@@ -140,7 +150,7 @@ def test_partial(monkeypatch):
140150
such as mocking ``stdlib`` functions that might break pytest itself if mocked (for examples
141151
of this see `#3290 <https://github.com/pytest-dev/pytest/issues/3290>`_.
142152
"""
143-
m = MonkeyPatch()
153+
m = cls()
144154
try:
145155
yield m
146156
finally:

src/pytest/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from _pytest.main import Session
2020
from _pytest.mark import MARK_GEN as mark
2121
from _pytest.mark import param
22+
from _pytest.monkeypatch import MonkeyPatch
2223
from _pytest.nodes import Collector
2324
from _pytest.nodes import File
2425
from _pytest.nodes import Item
@@ -74,6 +75,7 @@
7475
"main",
7576
"mark",
7677
"Module",
78+
"MonkeyPatch",
7779
"Package",
7880
"param",
7981
"PytestAssertRewriteWarning",

testing/test_monkeypatch.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,16 @@ def test_context() -> None:
409409
assert inspect.isclass(functools.partial)
410410

411411

412+
def test_context_classmethod() -> None:
413+
class A:
414+
x = 1
415+
416+
with MonkeyPatch.context() as m:
417+
m.setattr(A, "x", 2)
418+
assert A.x == 2
419+
assert A.x == 1
420+
421+
412422
def test_syspath_prepend_with_namespace_packages(
413423
testdir: Testdir, monkeypatch: MonkeyPatch
414424
) -> None:

0 commit comments

Comments
 (0)