updates
This commit is contained in:
@@ -1,27 +1,24 @@
|
||||
# mypy: allow-untyped-defs
|
||||
"""Monkeypatching and mocking functionality."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator
|
||||
from collections.abc import Mapping
|
||||
from collections.abc import MutableMapping
|
||||
from contextlib import contextmanager
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
from typing import Any
|
||||
from typing import final
|
||||
from typing import overload
|
||||
from typing import TypeVar
|
||||
import warnings
|
||||
from contextlib import contextmanager
|
||||
from typing import Any
|
||||
from typing import Generator
|
||||
from typing import List
|
||||
from typing import Mapping
|
||||
from typing import MutableMapping
|
||||
from typing import Optional
|
||||
from typing import overload
|
||||
from typing import Tuple
|
||||
from typing import TypeVar
|
||||
from typing import Union
|
||||
|
||||
from _pytest.deprecated import MONKEYPATCH_LEGACY_NAMESPACE_PACKAGES
|
||||
from _pytest.compat import final
|
||||
from _pytest.fixtures import fixture
|
||||
from _pytest.warning_types import PytestWarning
|
||||
|
||||
|
||||
RE_IMPORT_ERROR_NAME = re.compile(r"^No module named (.*)$")
|
||||
|
||||
|
||||
@@ -30,7 +27,7 @@ V = TypeVar("V")
|
||||
|
||||
|
||||
@fixture
|
||||
def monkeypatch() -> Generator[MonkeyPatch]:
|
||||
def monkeypatch() -> Generator["MonkeyPatch", None, None]:
|
||||
"""A convenient fixture for monkey-patching.
|
||||
|
||||
The fixture provides these methods to modify objects, dictionaries, or
|
||||
@@ -92,12 +89,14 @@ def annotated_getattr(obj: object, name: str, ann: str) -> object:
|
||||
obj = getattr(obj, name)
|
||||
except AttributeError as e:
|
||||
raise AttributeError(
|
||||
f"{type(obj).__name__!r} object at {ann} has no attribute {name!r}"
|
||||
"{!r} object at {} has no attribute {!r}".format(
|
||||
type(obj).__name__, ann, name
|
||||
)
|
||||
) from e
|
||||
return obj
|
||||
|
||||
|
||||
def derive_importpath(import_path: str, raising: bool) -> tuple[str, object]:
|
||||
def derive_importpath(import_path: str, raising: bool) -> Tuple[str, object]:
|
||||
if not isinstance(import_path, str) or "." not in import_path:
|
||||
raise TypeError(f"must be absolute import path string, not {import_path!r}")
|
||||
module, attr = import_path.rsplit(".", 1)
|
||||
@@ -130,14 +129,14 @@ class MonkeyPatch:
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._setattr: list[tuple[object, str, object]] = []
|
||||
self._setitem: list[tuple[Mapping[Any, Any], object, object]] = []
|
||||
self._cwd: str | None = None
|
||||
self._savesyspath: list[str] | None = None
|
||||
self._setattr: List[Tuple[object, str, object]] = []
|
||||
self._setitem: List[Tuple[Mapping[Any, Any], object, object]] = []
|
||||
self._cwd: Optional[str] = None
|
||||
self._savesyspath: Optional[List[str]] = None
|
||||
|
||||
@classmethod
|
||||
@contextmanager
|
||||
def context(cls) -> Generator[MonkeyPatch]:
|
||||
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.
|
||||
|
||||
@@ -169,7 +168,8 @@ class MonkeyPatch:
|
||||
name: object,
|
||||
value: Notset = ...,
|
||||
raising: bool = ...,
|
||||
) -> None: ...
|
||||
) -> None:
|
||||
...
|
||||
|
||||
@overload
|
||||
def setattr(
|
||||
@@ -178,12 +178,13 @@ class MonkeyPatch:
|
||||
name: str,
|
||||
value: object,
|
||||
raising: bool = ...,
|
||||
) -> None: ...
|
||||
) -> None:
|
||||
...
|
||||
|
||||
def setattr(
|
||||
self,
|
||||
target: str | object,
|
||||
name: object | str,
|
||||
target: Union[str, object],
|
||||
name: Union[object, str],
|
||||
value: object = notset,
|
||||
raising: bool = True,
|
||||
) -> None:
|
||||
@@ -254,8 +255,8 @@ class MonkeyPatch:
|
||||
|
||||
def delattr(
|
||||
self,
|
||||
target: object | str,
|
||||
name: str | Notset = notset,
|
||||
target: Union[object, str],
|
||||
name: Union[str, Notset] = notset,
|
||||
raising: bool = True,
|
||||
) -> None:
|
||||
"""Delete attribute ``name`` from ``target``.
|
||||
@@ -310,7 +311,7 @@ class MonkeyPatch:
|
||||
# Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict
|
||||
del dic[name] # type: ignore[attr-defined]
|
||||
|
||||
def setenv(self, name: str, value: str, prepend: str | None = None) -> None:
|
||||
def setenv(self, name: str, value: str, prepend: Optional[str] = None) -> None:
|
||||
"""Set environment variable ``name`` to ``value``.
|
||||
|
||||
If ``prepend`` is a character, read the current environment variable
|
||||
@@ -320,8 +321,10 @@ class MonkeyPatch:
|
||||
if not isinstance(value, str):
|
||||
warnings.warn( # type: ignore[unreachable]
|
||||
PytestWarning(
|
||||
f"Value of environment variable {name} type should be str, but got "
|
||||
f"{value!r} (type: {type(value).__name__}); converted to str implicitly"
|
||||
"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,
|
||||
)
|
||||
@@ -341,6 +344,7 @@ class MonkeyPatch:
|
||||
|
||||
def syspath_prepend(self, path) -> None:
|
||||
"""Prepend ``path`` to ``sys.path`` list of import locations."""
|
||||
|
||||
if self._savesyspath is None:
|
||||
self._savesyspath = sys.path[:]
|
||||
sys.path.insert(0, str(path))
|
||||
@@ -348,26 +352,8 @@ class MonkeyPatch:
|
||||
# https://github.com/pypa/setuptools/blob/d8b901bc/docs/pkg_resources.txt#L162-L171
|
||||
# this is only needed when pkg_resources was already loaded by the namespace package
|
||||
if "pkg_resources" in sys.modules:
|
||||
import pkg_resources
|
||||
from pkg_resources import fixup_namespace_packages
|
||||
|
||||
# Only issue deprecation warning if this call would actually have an
|
||||
# effect for this specific path.
|
||||
if (
|
||||
hasattr(pkg_resources, "_namespace_packages")
|
||||
and pkg_resources._namespace_packages
|
||||
):
|
||||
path_obj = Path(str(path))
|
||||
for ns_pkg in pkg_resources._namespace_packages:
|
||||
if ns_pkg is None:
|
||||
continue
|
||||
ns_pkg_path = path_obj / ns_pkg.replace(".", os.sep)
|
||||
if ns_pkg_path.is_dir():
|
||||
warnings.warn(
|
||||
MONKEYPATCH_LEGACY_NAMESPACE_PACKAGES, stacklevel=2
|
||||
)
|
||||
break
|
||||
|
||||
fixup_namespace_packages(str(path))
|
||||
|
||||
# A call to syspathinsert() usually means that the caller wants to
|
||||
@@ -381,7 +367,7 @@ class MonkeyPatch:
|
||||
|
||||
invalidate_caches()
|
||||
|
||||
def chdir(self, path: str | os.PathLike[str]) -> None:
|
||||
def chdir(self, path: Union[str, "os.PathLike[str]"]) -> None:
|
||||
"""Change the current working directory to the specified path.
|
||||
|
||||
:param path:
|
||||
|
||||
Reference in New Issue
Block a user