updates
This commit is contained in:
@@ -1,22 +1,18 @@
|
||||
from pytest_mock.plugin import AsyncMockType
|
||||
from pytest_mock.plugin import MockerFixture
|
||||
from pytest_mock.plugin import MockType
|
||||
from pytest_mock.plugin import PytestMockWarning
|
||||
from pytest_mock.plugin import class_mocker
|
||||
from pytest_mock.plugin import mocker
|
||||
from pytest_mock.plugin import MockerFixture
|
||||
from pytest_mock.plugin import module_mocker
|
||||
from pytest_mock.plugin import package_mocker
|
||||
from pytest_mock.plugin import pytest_addoption
|
||||
from pytest_mock.plugin import pytest_configure
|
||||
from pytest_mock.plugin import PytestMockWarning
|
||||
from pytest_mock.plugin import session_mocker
|
||||
|
||||
MockFixture = MockerFixture # backward-compatibility only (#204)
|
||||
|
||||
__all__ = [
|
||||
"AsyncMockType",
|
||||
"MockerFixture",
|
||||
"MockFixture",
|
||||
"MockType",
|
||||
"PytestMockWarning",
|
||||
"pytest_addoption",
|
||||
"pytest_configure",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,34 +1,16 @@
|
||||
# file generated by setuptools-scm
|
||||
# file generated by setuptools_scm
|
||||
# don't change, don't track in version control
|
||||
|
||||
__all__ = [
|
||||
"__version__",
|
||||
"__version_tuple__",
|
||||
"version",
|
||||
"version_tuple",
|
||||
"__commit_id__",
|
||||
"commit_id",
|
||||
]
|
||||
|
||||
TYPE_CHECKING = False
|
||||
if TYPE_CHECKING:
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
from typing import Tuple, Union
|
||||
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
||||
COMMIT_ID = Union[str, None]
|
||||
else:
|
||||
VERSION_TUPLE = object
|
||||
COMMIT_ID = object
|
||||
|
||||
version: str
|
||||
__version__: str
|
||||
__version_tuple__: VERSION_TUPLE
|
||||
version_tuple: VERSION_TUPLE
|
||||
commit_id: COMMIT_ID
|
||||
__commit_id__: COMMIT_ID
|
||||
|
||||
__version__ = version = '3.15.1'
|
||||
__version_tuple__ = version_tuple = (3, 15, 1)
|
||||
|
||||
__commit_id__ = commit_id = None
|
||||
__version__ = version = '3.12.0'
|
||||
__version_tuple__ = version_tuple = (3, 12, 0)
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
import asyncio
|
||||
import builtins
|
||||
import functools
|
||||
import inspect
|
||||
import itertools
|
||||
import sys
|
||||
import unittest.mock
|
||||
import warnings
|
||||
from collections.abc import Generator
|
||||
from collections.abc import Iterable
|
||||
from collections.abc import Iterator
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import field
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import cast
|
||||
from typing import Dict
|
||||
from typing import Generator
|
||||
from typing import Iterable
|
||||
from typing import List
|
||||
from typing import Mapping
|
||||
from typing import Optional
|
||||
from typing import overload
|
||||
from typing import Tuple
|
||||
from typing import Type
|
||||
from typing import TypeVar
|
||||
from typing import Union
|
||||
from typing import cast
|
||||
from typing import overload
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -25,58 +27,22 @@ from ._util import parse_ini_boolean
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
AsyncMockType = unittest.mock.AsyncMock
|
||||
MockType = Union[
|
||||
unittest.mock.MagicMock,
|
||||
unittest.mock.AsyncMock,
|
||||
unittest.mock.NonCallableMagicMock,
|
||||
]
|
||||
if sys.version_info >= (3, 8):
|
||||
AsyncMockType = unittest.mock.AsyncMock
|
||||
MockType = Union[
|
||||
unittest.mock.MagicMock,
|
||||
unittest.mock.AsyncMock,
|
||||
unittest.mock.NonCallableMagicMock,
|
||||
]
|
||||
else:
|
||||
AsyncMockType = Any
|
||||
MockType = Union[unittest.mock.MagicMock, unittest.mock.NonCallableMagicMock]
|
||||
|
||||
|
||||
class PytestMockWarning(UserWarning):
|
||||
"""Base class for all warnings emitted by pytest-mock."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class MockCacheItem:
|
||||
mock: MockType
|
||||
patch: Optional[Any] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class MockCache:
|
||||
"""
|
||||
Cache MagicMock and Patcher instances so we can undo them later.
|
||||
"""
|
||||
|
||||
cache: list[MockCacheItem] = field(default_factory=list)
|
||||
|
||||
def _find(self, mock: MockType) -> MockCacheItem:
|
||||
for mock_item in self.cache:
|
||||
if mock_item.mock is mock:
|
||||
return mock_item
|
||||
raise ValueError("This mock object is not registered")
|
||||
|
||||
def add(self, mock: MockType, **kwargs: Any) -> MockCacheItem:
|
||||
self.cache.append(MockCacheItem(mock=mock, **kwargs))
|
||||
return self.cache[-1]
|
||||
|
||||
def remove(self, mock: MockType) -> None:
|
||||
mock_item = self._find(mock)
|
||||
if mock_item.patch:
|
||||
mock_item.patch.stop()
|
||||
self.cache.remove(mock_item)
|
||||
|
||||
def clear(self) -> None:
|
||||
for mock_item in reversed(self.cache):
|
||||
if mock_item.patch is not None:
|
||||
mock_item.patch.stop()
|
||||
self.cache.clear()
|
||||
|
||||
def __iter__(self) -> Iterator[MockCacheItem]:
|
||||
return iter(self.cache)
|
||||
|
||||
|
||||
class MockerFixture:
|
||||
"""
|
||||
Fixture that provides the same interface to functions in the mock module,
|
||||
@@ -84,9 +50,11 @@ class MockerFixture:
|
||||
"""
|
||||
|
||||
def __init__(self, config: Any) -> None:
|
||||
self._mock_cache: MockCache = MockCache()
|
||||
self._patches_and_mocks: List[Tuple[Any, unittest.mock.MagicMock]] = []
|
||||
self.mock_module = mock_module = get_mock_module(config)
|
||||
self.patch = self._Patcher(self._mock_cache, mock_module) # type: MockerFixture._Patcher
|
||||
self.patch = self._Patcher(
|
||||
self._patches_and_mocks, mock_module
|
||||
) # type: MockerFixture._Patcher
|
||||
# aliases for convenience
|
||||
self.Mock = mock_module.Mock
|
||||
self.MagicMock = mock_module.MagicMock
|
||||
@@ -109,7 +77,7 @@ class MockerFixture:
|
||||
m: MockType = self.mock_module.create_autospec(
|
||||
spec, spec_set, instance, **kwargs
|
||||
)
|
||||
self._mock_cache.add(m)
|
||||
self._patches_and_mocks.append((None, m))
|
||||
return m
|
||||
|
||||
def resetall(
|
||||
@@ -121,55 +89,62 @@ class MockerFixture:
|
||||
:param bool return_value: Reset the return_value of mocks.
|
||||
:param bool side_effect: Reset the side_effect of mocks.
|
||||
"""
|
||||
supports_reset_mock_with_args: tuple[type[Any], ...]
|
||||
supports_reset_mock_with_args: Tuple[Type[Any], ...]
|
||||
if hasattr(self, "AsyncMock"):
|
||||
supports_reset_mock_with_args = (self.Mock, self.AsyncMock)
|
||||
else:
|
||||
supports_reset_mock_with_args = (self.Mock,)
|
||||
|
||||
for mock_item in self._mock_cache:
|
||||
for p, m in self._patches_and_mocks:
|
||||
# See issue #237.
|
||||
if not hasattr(mock_item.mock, "reset_mock"):
|
||||
if not hasattr(m, "reset_mock"):
|
||||
continue
|
||||
# NOTE: The mock may be a dictionary
|
||||
if hasattr(mock_item.mock, "spy_return_list"):
|
||||
mock_item.mock.spy_return_list = []
|
||||
if hasattr(mock_item.mock, "spy_return_iter"):
|
||||
mock_item.mock.spy_return_iter = None
|
||||
if isinstance(mock_item.mock, supports_reset_mock_with_args):
|
||||
mock_item.mock.reset_mock(
|
||||
return_value=return_value, side_effect=side_effect
|
||||
)
|
||||
if isinstance(m, supports_reset_mock_with_args):
|
||||
m.reset_mock(return_value=return_value, side_effect=side_effect)
|
||||
else:
|
||||
mock_item.mock.reset_mock()
|
||||
m.reset_mock()
|
||||
|
||||
def stopall(self) -> None:
|
||||
"""
|
||||
Stop all patchers started by this fixture. Can be safely called multiple
|
||||
times.
|
||||
"""
|
||||
self._mock_cache.clear()
|
||||
for p, m in reversed(self._patches_and_mocks):
|
||||
if p is not None:
|
||||
p.stop()
|
||||
self._patches_and_mocks.clear()
|
||||
|
||||
def stop(self, mock: unittest.mock.MagicMock) -> None:
|
||||
"""
|
||||
Stops a previous patch or spy call by passing the ``MagicMock`` object
|
||||
returned by it.
|
||||
"""
|
||||
self._mock_cache.remove(mock)
|
||||
for index, (p, m) in enumerate(self._patches_and_mocks):
|
||||
if mock is m:
|
||||
p.stop()
|
||||
del self._patches_and_mocks[index]
|
||||
break
|
||||
else:
|
||||
raise ValueError("This mock object is not registered")
|
||||
|
||||
def spy(
|
||||
self, obj: object, name: str, duplicate_iterators: bool = False
|
||||
) -> MockType:
|
||||
def spy(self, obj: object, name: str) -> MockType:
|
||||
"""
|
||||
Create a spy of method. It will run method normally, but it is now
|
||||
possible to use `mock` call features with it, like call count.
|
||||
|
||||
:param obj: An object.
|
||||
:param name: A method in object.
|
||||
:param duplicate_iterators: Whether to keep a copy of the returned iterator in `spy_return_iter`.
|
||||
:return: Spy object.
|
||||
"""
|
||||
method = getattr(obj, name)
|
||||
if inspect.isclass(obj) and isinstance(
|
||||
inspect.getattr_static(obj, name), (classmethod, staticmethod)
|
||||
):
|
||||
# Can't use autospec classmethod or staticmethod objects before 3.7
|
||||
# see: https://bugs.python.org/issue23078
|
||||
autospec = False
|
||||
else:
|
||||
autospec = inspect.ismethod(method) or inspect.isfunction(method)
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
spy_obj.spy_return = None
|
||||
@@ -180,14 +155,7 @@ class MockerFixture:
|
||||
spy_obj.spy_exception = e
|
||||
raise
|
||||
else:
|
||||
if duplicate_iterators and isinstance(r, Iterator):
|
||||
r, duplicated_iterator = itertools.tee(r, 2)
|
||||
spy_obj.spy_return_iter = duplicated_iterator
|
||||
else:
|
||||
spy_obj.spy_return_iter = None
|
||||
|
||||
spy_obj.spy_return = r
|
||||
spy_obj.spy_return_list.append(r)
|
||||
return r
|
||||
|
||||
async def async_wrapper(*args, **kwargs):
|
||||
@@ -200,20 +168,15 @@ class MockerFixture:
|
||||
raise
|
||||
else:
|
||||
spy_obj.spy_return = r
|
||||
spy_obj.spy_return_list.append(r)
|
||||
return r
|
||||
|
||||
if inspect.iscoroutinefunction(method):
|
||||
if asyncio.iscoroutinefunction(method):
|
||||
wrapped = functools.update_wrapper(async_wrapper, method)
|
||||
else:
|
||||
wrapped = functools.update_wrapper(wrapper, method)
|
||||
|
||||
autospec = inspect.ismethod(method) or inspect.isfunction(method)
|
||||
|
||||
spy_obj = self.patch.object(obj, name, side_effect=wrapped, autospec=autospec)
|
||||
spy_obj.spy_return = None
|
||||
spy_obj.spy_return_iter = None
|
||||
spy_obj.spy_return_list = []
|
||||
spy_obj.spy_exception = None
|
||||
return spy_obj
|
||||
|
||||
@@ -251,8 +214,8 @@ class MockerFixture:
|
||||
|
||||
DEFAULT = object()
|
||||
|
||||
def __init__(self, mock_cache, mock_module):
|
||||
self.__mock_cache = mock_cache
|
||||
def __init__(self, patches_and_mocks, mock_module):
|
||||
self.__patches_and_mocks = patches_and_mocks
|
||||
self.mock_module = mock_module
|
||||
|
||||
def _start_patch(
|
||||
@@ -264,18 +227,22 @@ class MockerFixture:
|
||||
"""
|
||||
p = mock_func(*args, **kwargs)
|
||||
mocked: MockType = p.start()
|
||||
self.__mock_cache.add(mock=mocked, patch=p)
|
||||
self.__patches_and_mocks.append((p, mocked))
|
||||
if hasattr(mocked, "reset_mock"):
|
||||
# check if `mocked` is actually a mock object, as depending on autospec or target
|
||||
# parameters `mocked` can be anything
|
||||
if hasattr(mocked, "__enter__") and warn_on_mock_enter:
|
||||
if sys.version_info >= (3, 8):
|
||||
depth = 5
|
||||
else:
|
||||
depth = 4
|
||||
mocked.__enter__.side_effect = lambda: warnings.warn(
|
||||
"Mocks returned by pytest-mock do not need to be used as context managers. "
|
||||
"The mocker fixture automatically undoes mocking at the end of a test. "
|
||||
"This warning can be ignored if it was triggered by mocking a context manager. "
|
||||
"https://pytest-mock.readthedocs.io/en/latest/usage.html#usage-as-context-manager",
|
||||
"https://pytest-mock.readthedocs.io/en/latest/remarks.html#usage-as-context-manager",
|
||||
PytestMockWarning,
|
||||
stacklevel=5,
|
||||
stacklevel=depth,
|
||||
)
|
||||
return mocked
|
||||
|
||||
@@ -289,7 +256,7 @@ class MockerFixture:
|
||||
spec_set: Optional[object] = None,
|
||||
autospec: Optional[object] = None,
|
||||
new_callable: object = None,
|
||||
**kwargs: Any,
|
||||
**kwargs: Any
|
||||
) -> MockType:
|
||||
"""API to mock.patch.object"""
|
||||
if new is self.DEFAULT:
|
||||
@@ -305,7 +272,7 @@ class MockerFixture:
|
||||
spec_set=spec_set,
|
||||
autospec=autospec,
|
||||
new_callable=new_callable,
|
||||
**kwargs,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def context_manager(
|
||||
@@ -318,7 +285,7 @@ class MockerFixture:
|
||||
spec_set: Optional[builtins.object] = None,
|
||||
autospec: Optional[builtins.object] = None,
|
||||
new_callable: builtins.object = None,
|
||||
**kwargs: Any,
|
||||
**kwargs: Any
|
||||
) -> MockType:
|
||||
"""This is equivalent to mock.patch.object except that the returned mock
|
||||
does not issue a warning when used as a context manager."""
|
||||
@@ -335,7 +302,7 @@ class MockerFixture:
|
||||
spec_set=spec_set,
|
||||
autospec=autospec,
|
||||
new_callable=new_callable,
|
||||
**kwargs,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def multiple(
|
||||
@@ -346,8 +313,8 @@ class MockerFixture:
|
||||
spec_set: Optional[builtins.object] = None,
|
||||
autospec: Optional[builtins.object] = None,
|
||||
new_callable: Optional[builtins.object] = None,
|
||||
**kwargs: Any,
|
||||
) -> dict[str, MockType]:
|
||||
**kwargs: Any
|
||||
) -> Dict[str, MockType]:
|
||||
"""API to mock.patch.multiple"""
|
||||
return self._start_patch(
|
||||
self.mock_module.patch.multiple,
|
||||
@@ -358,15 +325,15 @@ class MockerFixture:
|
||||
spec_set=spec_set,
|
||||
autospec=autospec,
|
||||
new_callable=new_callable,
|
||||
**kwargs,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def dict(
|
||||
self,
|
||||
in_dict: Union[Mapping[Any, Any], str],
|
||||
values: Union[Mapping[Any, Any], Iterable[tuple[Any, Any]]] = (),
|
||||
values: Union[Mapping[Any, Any], Iterable[Tuple[Any, Any]]] = (),
|
||||
clear: bool = False,
|
||||
**kwargs: Any,
|
||||
**kwargs: Any
|
||||
) -> Any:
|
||||
"""API to mock.patch.dict"""
|
||||
return self._start_patch(
|
||||
@@ -375,7 +342,7 @@ class MockerFixture:
|
||||
in_dict,
|
||||
values=values,
|
||||
clear=clear,
|
||||
**kwargs,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
@overload
|
||||
@@ -388,8 +355,9 @@ class MockerFixture:
|
||||
spec_set: Optional[builtins.object] = ...,
|
||||
autospec: Optional[builtins.object] = ...,
|
||||
new_callable: None = ...,
|
||||
**kwargs: Any,
|
||||
) -> MockType: ...
|
||||
**kwargs: Any
|
||||
) -> MockType:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __call__(
|
||||
@@ -401,8 +369,9 @@ class MockerFixture:
|
||||
spec_set: Optional[builtins.object] = ...,
|
||||
autospec: Optional[builtins.object] = ...,
|
||||
new_callable: None = ...,
|
||||
**kwargs: Any,
|
||||
) -> _T: ...
|
||||
**kwargs: Any
|
||||
) -> _T:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __call__(
|
||||
@@ -414,8 +383,9 @@ class MockerFixture:
|
||||
spec_set: Optional[builtins.object],
|
||||
autospec: Optional[builtins.object],
|
||||
new_callable: Callable[[], _T],
|
||||
**kwargs: Any,
|
||||
) -> _T: ...
|
||||
**kwargs: Any
|
||||
) -> _T:
|
||||
...
|
||||
|
||||
@overload
|
||||
def __call__(
|
||||
@@ -428,8 +398,9 @@ class MockerFixture:
|
||||
autospec: Optional[builtins.object] = ...,
|
||||
*,
|
||||
new_callable: Callable[[], _T],
|
||||
**kwargs: Any,
|
||||
) -> _T: ...
|
||||
**kwargs: Any
|
||||
) -> _T:
|
||||
...
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
@@ -440,7 +411,7 @@ class MockerFixture:
|
||||
spec_set: Optional[builtins.object] = None,
|
||||
autospec: Optional[builtins.object] = None,
|
||||
new_callable: Optional[Callable[[], Any]] = None,
|
||||
**kwargs: Any,
|
||||
**kwargs: Any
|
||||
) -> Any:
|
||||
"""API to mock.patch"""
|
||||
if new is self.DEFAULT:
|
||||
@@ -455,7 +426,7 @@ class MockerFixture:
|
||||
spec_set=spec_set,
|
||||
autospec=autospec,
|
||||
new_callable=new_callable,
|
||||
**kwargs,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
@@ -476,8 +447,8 @@ package_mocker = pytest.fixture(scope="package")(_mocker)
|
||||
session_mocker = pytest.fixture(scope="session")(_mocker)
|
||||
|
||||
|
||||
_mock_module_patches: list[Any] = []
|
||||
_mock_module_originals: dict[str, Any] = {}
|
||||
_mock_module_patches = [] # type: List[Any]
|
||||
_mock_module_originals = {} # type: Dict[str, Any]
|
||||
|
||||
|
||||
def assert_wrapper(
|
||||
|
||||
Reference in New Issue
Block a user