aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/pytest/py3/_pytest/monkeypatch.py
diff options
context:
space:
mode:
authordeshevoy <deshevoy@yandex-team.ru>2022-02-10 16:46:57 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:46:57 +0300
commit28148f76dbfcc644d96427d41c92f36cbf2fdc6e (patch)
treeb83306b6e37edeea782e9eed673d89286c4fef35 /contrib/python/pytest/py3/_pytest/monkeypatch.py
parente988f30484abe5fdeedcc7a5d3c226c01a21800c (diff)
downloadydb-28148f76dbfcc644d96427d41c92f36cbf2fdc6e.tar.gz
Restoring authorship annotation for <deshevoy@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/pytest/py3/_pytest/monkeypatch.py')
-rw-r--r--contrib/python/pytest/py3/_pytest/monkeypatch.py418
1 files changed, 209 insertions, 209 deletions
diff --git a/contrib/python/pytest/py3/_pytest/monkeypatch.py b/contrib/python/pytest/py3/_pytest/monkeypatch.py
index e5d6c036bc..a052f693ac 100644
--- a/contrib/python/pytest/py3/_pytest/monkeypatch.py
+++ b/contrib/python/pytest/py3/_pytest/monkeypatch.py
@@ -1,9 +1,9 @@
"""Monkeypatching and mocking functionality."""
-import os
-import re
-import sys
-import warnings
-from contextlib import contextmanager
+import os
+import re
+import sys
+import warnings
+from contextlib import contextmanager
from pathlib import Path
from typing import Any
from typing import Generator
@@ -14,101 +14,101 @@ from typing import overload
from typing import Tuple
from typing import TypeVar
from typing import Union
-
+
from _pytest.compat import final
-from _pytest.fixtures import fixture
+from _pytest.fixtures import fixture
from _pytest.warning_types import PytestWarning
-
-RE_IMPORT_ERROR_NAME = re.compile(r"^No module named (.*)$")
-
-
+
+RE_IMPORT_ERROR_NAME = re.compile(r"^No module named (.*)$")
+
+
K = TypeVar("K")
V = TypeVar("V")
-@fixture
+@fixture
def monkeypatch() -> Generator["MonkeyPatch", None, None]:
"""A convenient fixture for monkey-patching.
-
+
The fixture provides these methods to modify objects, dictionaries or
os.environ::
- monkeypatch.setattr(obj, name, value, raising=True)
- monkeypatch.delattr(obj, name, raising=True)
- monkeypatch.setitem(mapping, name, value)
- monkeypatch.delitem(obj, name, raising=True)
- monkeypatch.setenv(name, value, prepend=False)
- monkeypatch.delenv(name, raising=True)
- monkeypatch.syspath_prepend(path)
- monkeypatch.chdir(path)
-
+ monkeypatch.setattr(obj, name, value, raising=True)
+ monkeypatch.delattr(obj, name, raising=True)
+ monkeypatch.setitem(mapping, name, value)
+ monkeypatch.delitem(obj, name, raising=True)
+ monkeypatch.setenv(name, value, prepend=False)
+ monkeypatch.delenv(name, raising=True)
+ monkeypatch.syspath_prepend(path)
+ monkeypatch.chdir(path)
+
All modifications will be undone after the requesting test function or
fixture has finished. The ``raising`` parameter determines if a KeyError
or AttributeError will be raised if the set/deletion operation has no target.
- """
- mpatch = MonkeyPatch()
- yield mpatch
- mpatch.undo()
-
-
+ """
+ mpatch = MonkeyPatch()
+ yield mpatch
+ mpatch.undo()
+
+
def resolve(name: str) -> object:
# Simplified from zope.dottedname.
- parts = name.split(".")
-
- used = parts.pop(0)
- found = __import__(used)
- for part in parts:
- used += "." + part
- try:
- found = getattr(found, part)
- except AttributeError:
- pass
- else:
- continue
+ parts = name.split(".")
+
+ used = parts.pop(0)
+ found = __import__(used)
+ for part in parts:
+ used += "." + part
+ try:
+ found = getattr(found, part)
+ except AttributeError:
+ pass
+ else:
+ continue
# We use explicit un-nesting of the handling block in order
# to avoid nested exceptions.
- try:
- __import__(used)
- except ImportError as ex:
- expected = str(ex).split()[-1]
- if expected == used:
- raise
- else:
+ try:
+ __import__(used)
+ except ImportError as ex:
+ expected = str(ex).split()[-1]
+ if expected == used:
+ raise
+ else:
raise ImportError(f"import error in {used}: {ex}") from ex
- found = annotated_getattr(found, part, used)
- return found
-
-
+ found = annotated_getattr(found, part, used)
+ return found
+
+
def annotated_getattr(obj: object, name: str, ann: str) -> object:
- try:
- obj = getattr(obj, name)
+ try:
+ obj = getattr(obj, name)
except AttributeError as e:
- raise AttributeError(
+ raise AttributeError(
"{!r} object at {} has no attribute {!r}".format(
type(obj).__name__, ann, name
)
) from e
- return obj
-
-
+ return obj
+
+
def derive_importpath(import_path: str, raising: bool) -> Tuple[str, object]:
if not isinstance(import_path, str) or "." not in import_path: # type: ignore[unreachable]
raise TypeError(f"must be absolute import path string, not {import_path!r}")
- module, attr = import_path.rsplit(".", 1)
- target = resolve(module)
- if raising:
- annotated_getattr(target, attr, ann=module)
- return attr, target
-
-
+ module, attr = import_path.rsplit(".", 1)
+ target = resolve(module)
+ if raising:
+ annotated_getattr(target, attr, ann=module)
+ return attr, target
+
+
class Notset:
def __repr__(self) -> str:
- return "<notset>"
-
-
-notset = Notset()
-
-
+ return "<notset>"
+
+
+notset = Notset()
+
+
@final
class MonkeyPatch:
"""Helper to conveniently monkeypatch attributes/items/environment
@@ -121,47 +121,47 @@ class MonkeyPatch:
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]] = []
self._setitem: List[Tuple[MutableMapping[Any, Any], object, object]] = ([])
self._cwd: Optional[str] = None
self._savesyspath: Optional[List[str]] = None
-
+
@classmethod
- @contextmanager
+ @contextmanager
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.
-
+
Example:
- .. code-block:: python
-
- import functools
+ .. code-block:: python
+
+ import functools
+
+ def test_partial(monkeypatch):
+ with monkeypatch.context() as m:
+ m.setattr(functools, "partial", 3)
- def test_partial(monkeypatch):
- with monkeypatch.context() as m:
- m.setattr(functools, "partial", 3)
-
- Useful in situations where it is desired to undo some patches before the test ends,
- 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>`_.
- """
+ Useful in situations where it is desired to undo some patches before the test ends,
+ 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 = cls()
- try:
- yield m
- finally:
- m.undo()
-
+ try:
+ yield m
+ finally:
+ m.undo()
+
@overload
def setattr(
self, target: str, name: object, value: Notset = ..., raising: bool = ...,
) -> None:
...
-
+
@overload
def setattr(
self, target: object, name: str, value: object, raising: bool = ...,
@@ -177,27 +177,27 @@ class MonkeyPatch:
) -> None:
"""Set attribute value on target, memorizing the old value.
- For convenience you can specify a string as ``target`` which
- will be interpreted as a dotted import path, with the last part
+ For convenience you can specify a string as ``target`` which
+ will be interpreted as a dotted import path, with the last part
being the attribute name. For example,
- ``monkeypatch.setattr("os.getcwd", lambda: "/")``
- would set the ``getcwd`` function of the ``os`` module.
-
+ ``monkeypatch.setattr("os.getcwd", lambda: "/")``
+ would set the ``getcwd`` function of the ``os`` module.
+
Raises AttributeError if the attribute does not exist, unless
``raising`` is set to False.
- """
- __tracebackhide__ = True
- import inspect
-
+ """
+ __tracebackhide__ = True
+ import inspect
+
if isinstance(value, Notset):
if not isinstance(target, str):
- raise TypeError(
- "use setattr(target, name, value) or "
- "setattr(target, value) with target being a dotted "
- "import string"
- )
- value = name
- name, target = derive_importpath(target, raising)
+ raise TypeError(
+ "use setattr(target, name, value) or "
+ "setattr(target, value) with target being a dotted "
+ "import string"
+ )
+ value = name
+ name, target = derive_importpath(target, raising)
else:
if not isinstance(name, str):
raise TypeError(
@@ -205,17 +205,17 @@ class MonkeyPatch:
"setattr(target, value) with target being a dotted "
"import string"
)
-
- oldval = getattr(target, name, notset)
- if raising and oldval is notset:
+
+ oldval = getattr(target, name, notset)
+ if raising and oldval is notset:
raise AttributeError(f"{target!r} has no attribute {name!r}")
-
- # avoid class descriptors like staticmethod/classmethod
- if inspect.isclass(target):
- oldval = target.__dict__.get(name, notset)
- self._setattr.append((target, name, oldval))
- setattr(target, name, value)
-
+
+ # avoid class descriptors like staticmethod/classmethod
+ if inspect.isclass(target):
+ oldval = target.__dict__.get(name, notset)
+ self._setattr.append((target, name, oldval))
+ setattr(target, name, value)
+
def delattr(
self,
target: Union[object, str],
@@ -223,55 +223,55 @@ class MonkeyPatch:
raising: bool = True,
) -> None:
"""Delete attribute ``name`` from ``target``.
-
- If no ``name`` is specified and ``target`` is a string
- it will be interpreted as a dotted import path with the
- last part being the attribute name.
-
+
+ If no ``name`` is specified and ``target`` is a string
+ it will be interpreted as a dotted import path with the
+ last part being the attribute name.
+
Raises AttributeError it the attribute does not exist, unless
``raising`` is set to False.
- """
- __tracebackhide__ = True
+ """
+ __tracebackhide__ = True
import inspect
if isinstance(name, Notset):
if not isinstance(target, str):
- raise TypeError(
- "use delattr(target, name) or "
- "delattr(target) with target being a dotted "
- "import string"
- )
- name, target = derive_importpath(target, raising)
-
- if not hasattr(target, name):
- if raising:
- raise AttributeError(name)
- else:
+ raise TypeError(
+ "use delattr(target, name) or "
+ "delattr(target) with target being a dotted "
+ "import string"
+ )
+ name, target = derive_importpath(target, raising)
+
+ if not hasattr(target, name):
+ if raising:
+ raise AttributeError(name)
+ else:
oldval = getattr(target, name, notset)
# Avoid class descriptors like staticmethod/classmethod.
if inspect.isclass(target):
oldval = target.__dict__.get(name, notset)
self._setattr.append((target, name, oldval))
- delattr(target, name)
-
+ delattr(target, name)
+
def setitem(self, dic: MutableMapping[K, V], name: K, value: V) -> None:
"""Set dictionary entry ``name`` to value."""
- self._setitem.append((dic, name, dic.get(name, notset)))
- dic[name] = value
-
+ self._setitem.append((dic, name, dic.get(name, notset)))
+ dic[name] = value
+
def delitem(self, dic: MutableMapping[K, V], name: K, raising: bool = True) -> None:
"""Delete ``name`` from dict.
-
+
Raises ``KeyError`` if it doesn't exist, unless ``raising`` is set to
False.
- """
- if name not in dic:
- if raising:
- raise KeyError(name)
- else:
- self._setitem.append((dic, name, dic.get(name, notset)))
- del dic[name]
-
+ """
+ if name not in dic:
+ if raising:
+ raise KeyError(name)
+ else:
+ self._setitem.append((dic, name, dic.get(name, notset)))
+ del dic[name]
+
def setenv(self, name: str, value: str, prepend: Optional[str] = None) -> None:
"""Set environment variable ``name`` to ``value``.
@@ -279,38 +279,38 @@ class MonkeyPatch:
value and prepend the ``value`` adjoined with the ``prepend``
character.
"""
- if not isinstance(value, str):
+ if not isinstance(value, str):
warnings.warn( # type: ignore[unreachable]
PytestWarning(
- "Value of environment variable {name} type should be str, but got "
- "{value!r} (type: {type}); converted to str implicitly".format(
- name=name, value=value, type=type(value).__name__
- )
- ),
- stacklevel=2,
- )
- value = str(value)
- if prepend and name in os.environ:
- value = value + prepend + os.environ[name]
- self.setitem(os.environ, name, value)
-
+ "Value of environment variable {name} type should be str, but got "
+ "{value!r} (type: {type}); converted to str implicitly".format(
+ name=name, value=value, type=type(value).__name__
+ )
+ ),
+ stacklevel=2,
+ )
+ value = str(value)
+ if prepend and name in os.environ:
+ value = value + prepend + os.environ[name]
+ self.setitem(os.environ, name, value)
+
def delenv(self, name: str, raising: bool = True) -> None:
"""Delete ``name`` from the environment.
-
+
Raises ``KeyError`` if it does not exist, unless ``raising`` is set to
False.
- """
+ """
environ: MutableMapping[str, str] = os.environ
self.delitem(environ, name, raising=raising)
-
+
def syspath_prepend(self, path) -> None:
"""Prepend ``path`` to ``sys.path`` list of import locations."""
from pkg_resources import fixup_namespace_packages
- if self._savesyspath is None:
- self._savesyspath = sys.path[:]
- sys.path.insert(0, str(path))
-
+ if self._savesyspath is None:
+ self._savesyspath = sys.path[:]
+ sys.path.insert(0, str(path))
+
# https://github.com/pypa/setuptools/blob/d8b901bc/docs/pkg_resources.txt#L162-L171
fixup_namespace_packages(str(path))
@@ -328,52 +328,52 @@ class MonkeyPatch:
def chdir(self, path) -> None:
"""Change the current working directory to the specified path.
- Path can be a string or a py.path.local object.
- """
- if self._cwd is None:
- self._cwd = os.getcwd()
- if hasattr(path, "chdir"):
- path.chdir()
- elif isinstance(path, Path):
+ Path can be a string or a py.path.local object.
+ """
+ if self._cwd is None:
+ self._cwd = os.getcwd()
+ if hasattr(path, "chdir"):
+ path.chdir()
+ elif isinstance(path, Path):
# Modern python uses the fspath protocol here LEGACY
- os.chdir(str(path))
- else:
- os.chdir(path)
-
+ os.chdir(str(path))
+ else:
+ os.chdir(path)
+
def undo(self) -> None:
"""Undo previous changes.
-
+
This call consumes the undo stack. Calling it a second time has no
effect unless you do more monkeypatching after the undo call.
- There is generally no need to call `undo()`, since it is
- called automatically during tear-down.
-
- Note that the same `monkeypatch` fixture is used across a
- single test function invocation. If `monkeypatch` is used both by
- the test function itself and one of the test fixtures,
- calling `undo()` will undo all of the changes made in
- both functions.
- """
- for obj, name, value in reversed(self._setattr):
- if value is not notset:
- setattr(obj, name, value)
- else:
- delattr(obj, name)
- self._setattr[:] = []
+ There is generally no need to call `undo()`, since it is
+ called automatically during tear-down.
+
+ Note that the same `monkeypatch` fixture is used across a
+ single test function invocation. If `monkeypatch` is used both by
+ the test function itself and one of the test fixtures,
+ calling `undo()` will undo all of the changes made in
+ both functions.
+ """
+ for obj, name, value in reversed(self._setattr):
+ if value is not notset:
+ setattr(obj, name, value)
+ else:
+ delattr(obj, name)
+ self._setattr[:] = []
for dictionary, key, value in reversed(self._setitem):
- if value is notset:
- try:
+ if value is notset:
+ try:
del dictionary[key]
- except KeyError:
+ except KeyError:
pass # Was already deleted, so we have the desired state.
- else:
+ else:
dictionary[key] = value
- self._setitem[:] = []
- if self._savesyspath is not None:
- sys.path[:] = self._savesyspath
- self._savesyspath = None
-
- if self._cwd is not None:
- os.chdir(self._cwd)
- self._cwd = None
+ self._setitem[:] = []
+ if self._savesyspath is not None:
+ sys.path[:] = self._savesyspath
+ self._savesyspath = None
+
+ if self._cwd is not None:
+ os.chdir(self._cwd)
+ self._cwd = None