diff options
author | Ran Benita <ran@unusedvar.com> | 2020-11-06 19:27:33 +0200 |
---|---|---|
committer | Ran Benita <ran@unusedvar.com> | 2020-11-09 11:28:15 +0200 |
commit | 6f13d1b03b1e1af7def99505234075878407767d (patch) | |
tree | 48ff8f9b17347a521f8151d3a9e0d5691d4a2def | |
parent | 3bcd316f076b185bcc89c41a41345861f752aff7 (diff) | |
download | pytest-6f13d1b03b1e1af7def99505234075878407767d.tar.gz |
Export MonkeyPatch as pytest.MonkeyPatch
We want to export `pytest.MonkeyPatch` for the purpose of
type-annotating the `monkeypatch` fixture. For other fixtures we export
in this way, we also make direct construction of them (e.g.
`MonkeyPatch()`) private. But unlike the others, `MonkeyPatch` is also
widely used directly already, mostly because the `monkeypatch` fixture
only works in `function` scope (issue #363), but also in other cases. So
making it private will be annoying and we don't offer a decent
replacement yet.
So, let's just make direct construction public & documented.
-rw-r--r-- | changelog/8006.feature.rst | 8 | ||||
-rw-r--r-- | doc/en/reference.rst | 6 | ||||
-rw-r--r-- | src/_pytest/monkeypatch.py | 18 | ||||
-rw-r--r-- | src/pytest/__init__.py | 2 | ||||
-rw-r--r-- | testing/test_monkeypatch.py | 10 |
5 files changed, 36 insertions, 8 deletions
diff --git a/changelog/8006.feature.rst b/changelog/8006.feature.rst new file mode 100644 index 000000000..0203689ba --- /dev/null +++ b/changelog/8006.feature.rst @@ -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. diff --git a/doc/en/reference.rst b/doc/en/reference.rst index c04b8da0b..cbe89fe0b 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -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: diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index df4726705..31b7b125b 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -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: diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index a9c1ee028..d7a5b2299 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -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", diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index 73fe313e5..c20ff7480 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -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: |