Merge pull request #8006 from bluetech/export-MonkeyPatch

Export MonkeyPatch as pytest.MonkeyPatch
This commit is contained in:
Ran Benita 2020-11-09 11:45:38 +02:00 committed by GitHub
commit e986d84466
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 36 additions and 8 deletions

View File

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

View File

@ -486,16 +486,14 @@ caplog
monkeypatch
~~~~~~~~~~~
.. currentmodule:: _pytest.monkeypatch
**Tutorial**: :doc:`monkeypatch`.
.. autofunction:: _pytest.monkeypatch.monkeypatch()
:no-auto-options:
Returns a :class:`MonkeyPatch` instance.
Returns a :class:`~pytest.MonkeyPatch` instance.
.. autoclass:: _pytest.monkeypatch.MonkeyPatch
.. autoclass:: pytest.MonkeyPatch
:members:

View File

@ -111,8 +111,17 @@ notset = Notset()
@final
class MonkeyPatch:
"""Object returned by the ``monkeypatch`` fixture keeping a record of
setattr/item/env/syspath changes."""
"""Helper to conveniently monkeypatch attributes/items/environment
variables/syspath.
Returned by the :fixture:`monkeypatch` fixture.
:versionchanged:: 6.2
Can now also be used directly as `pytest.MonkeyPatch()`, for when
the fixture is not available. In this case, use
:meth:`with MonkeyPatch.context() as mp: <context>` or remember to call
:meth:`undo` explicitly.
"""
def __init__(self) -> None:
self._setattr: List[Tuple[object, str, object]] = []
@ -120,8 +129,9 @@ class MonkeyPatch:
self._cwd: Optional[str] = None
self._savesyspath: Optional[List[str]] = None
@classmethod
@contextmanager
def context(self) -> Generator["MonkeyPatch", None, None]:
def context(cls) -> Generator["MonkeyPatch", None, None]:
"""Context manager that returns a new :class:`MonkeyPatch` object
which undoes any patching done inside the ``with`` block upon exit.
@ -140,7 +150,7 @@ class MonkeyPatch:
such as mocking ``stdlib`` functions that might break pytest itself if mocked (for examples
of this see `#3290 <https://github.com/pytest-dev/pytest/issues/3290>`_.
"""
m = MonkeyPatch()
m = cls()
try:
yield m
finally:

View File

@ -19,6 +19,7 @@ from _pytest.freeze_support import freeze_includes
from _pytest.main import Session
from _pytest.mark import MARK_GEN as mark
from _pytest.mark import param
from _pytest.monkeypatch import MonkeyPatch
from _pytest.nodes import Collector
from _pytest.nodes import File
from _pytest.nodes import Item
@ -74,6 +75,7 @@ __all__ = [
"main",
"mark",
"Module",
"MonkeyPatch",
"Package",
"param",
"PytestAssertRewriteWarning",

View File

@ -409,6 +409,16 @@ def test_context() -> None:
assert inspect.isclass(functools.partial)
def test_context_classmethod() -> None:
class A:
x = 1
with MonkeyPatch.context() as m:
m.setattr(A, "x", 2)
assert A.x == 2
assert A.x == 1
def test_syspath_prepend_with_namespace_packages(
testdir: Testdir, monkeypatch: MonkeyPatch
) -> None: