Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repos:
language: python
additional_dependencies: [pygments, restructuredtext_lint]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.21
rev: v0.16.1
hooks:
- id: ruff
args: ["--fix"]
Expand Down
10 changes: 5 additions & 5 deletions src/pytest_mock/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@

__all__ = [
"AsyncMockType",
"MockerFixture",
"MockFixture",
"MockType",
"MockerFixture",
"PytestMockWarning",
"SpyType",
"class_mocker",
"mocker",
"module_mocker",
"package_mocker",
"pytest_addoption",
"pytest_configure",
"session_mocker",
"package_mocker",
"module_mocker",
"class_mocker",
"mocker",
]
8 changes: 3 additions & 5 deletions src/pytest_mock/_util.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import Union

_mock_module = None


Expand All @@ -15,7 +13,7 @@ def get_mock_module(config):
config.getini("mock_use_standalone_module")
)
if use_standalone_module:
import mock
import mock # noqa

_mock_module = mock
else:
Expand All @@ -26,11 +24,11 @@ def get_mock_module(config):
return _mock_module


def parse_ini_boolean(value: Union[bool, str]) -> bool:
def parse_ini_boolean(value: bool | str) -> bool:
if isinstance(value, bool):
return value
if value.lower() == "true":
return True
if value.lower() == "false":
return False
raise ValueError("unknown string for bool: %r" % value)
raise ValueError(f"unknown string for bool: {value!r}")
90 changes: 44 additions & 46 deletions src/pytest_mock/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@
import itertools
import unittest.mock
import warnings
from collections.abc import Callable
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 Optional
from typing import TypeVar
from typing import Union
from typing import cast
from typing import overload

Expand All @@ -26,11 +24,11 @@
_T = TypeVar("_T")

AsyncMockType = unittest.mock.AsyncMock
MockType = Union[
unittest.mock.MagicMock,
unittest.mock.AsyncMock,
unittest.mock.NonCallableMagicMock,
]
MockType = (
unittest.mock.MagicMock
| unittest.mock.AsyncMock
| unittest.mock.NonCallableMagicMock
)


class SpyType(unittest.mock.Mock):
Expand All @@ -39,9 +37,9 @@ class SpyType(unittest.mock.Mock):
"""

spy_return: Any
spy_return_iter: Optional[Iterator[Any]]
spy_return_iter: Iterator[Any] | None
spy_return_list: list[Any]
spy_exception: Optional[BaseException]
spy_exception: BaseException | None


class PytestMockWarning(UserWarning):
Expand All @@ -51,7 +49,7 @@ class PytestMockWarning(UserWarning):
@dataclass
class MockCacheItem:
mock: MockType
patch: Optional[Any] = None
patch: Any | None = None


@dataclass
Expand Down Expand Up @@ -229,7 +227,7 @@ async def async_wrapper(*args, **kwargs):
spy_obj.spy_exception = None
return spy_obj

def stub(self, name: Optional[str] = None) -> unittest.mock.MagicMock:
def stub(self, name: str | None = None) -> unittest.mock.MagicMock:
"""
Create a stub method. It accepts any arguments. Ideal to register to
callbacks in tests.
Expand All @@ -242,7 +240,7 @@ def stub(self, name: Optional[str] = None) -> unittest.mock.MagicMock:
self.mock_module.MagicMock(spec=lambda *args, **kwargs: None, name=name),
)

def async_stub(self, name: Optional[str] = None) -> AsyncMockType:
def async_stub(self, name: str | None = None) -> AsyncMockType:
"""
Create a async stub method. It accepts any arguments. Ideal to register to
callbacks in tests.
Expand Down Expand Up @@ -277,7 +275,7 @@ def _start_patch(
p = mock_func(*args, **kwargs)
mocked: MockType = p.start()
self.__mock_cache.add(mock=mocked, patch=p)
if hasattr(mocked, "reset_mock"):
if hasattr(mocked, "reset_mock"): # noqa:SIM102
# 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:
Expand All @@ -296,10 +294,10 @@ def object(
target: object,
attribute: str,
new: object = DEFAULT,
spec: Optional[object] = None,
spec: object | None = None,
create: bool = False,
spec_set: Optional[object] = None,
autospec: Optional[object] = None,
spec_set: object | None = None,
autospec: object | None = None,
new_callable: object = None,
**kwargs: Any,
) -> MockType:
Expand All @@ -325,10 +323,10 @@ def context_manager(
target: builtins.object,
attribute: str,
new: builtins.object = DEFAULT,
spec: Optional[builtins.object] = None,
spec: builtins.object | None = None,
create: bool = False,
spec_set: Optional[builtins.object] = None,
autospec: Optional[builtins.object] = None,
spec_set: builtins.object | None = None,
autospec: builtins.object | None = None,
new_callable: builtins.object = None,
**kwargs: Any,
) -> MockType:
Expand All @@ -353,11 +351,11 @@ def context_manager(
def multiple(
self,
target: builtins.object,
spec: Optional[builtins.object] = None,
spec: builtins.object | None = None,
create: bool = False,
spec_set: Optional[builtins.object] = None,
autospec: Optional[builtins.object] = None,
new_callable: Optional[builtins.object] = None,
spec_set: builtins.object | None = None,
autospec: builtins.object | None = None,
new_callable: builtins.object | None = None,
**kwargs: Any,
) -> dict[str, MockType]:
"""API to mock.patch.multiple"""
Expand All @@ -375,8 +373,8 @@ def multiple(

def dict(
self,
in_dict: Union[Mapping[Any, Any], str],
values: Union[Mapping[Any, Any], Iterable[tuple[Any, Any]]] = (),
in_dict: Mapping[Any, Any] | str,
values: Mapping[Any, Any] | Iterable[tuple[Any, Any]] = (),
clear: bool = False,
**kwargs: Any,
) -> Any:
Expand All @@ -395,10 +393,10 @@ def __call__(
self,
target: str,
new: None = ...,
spec: Optional[builtins.object] = ...,
spec: builtins.object | None = ...,
create: bool = ...,
spec_set: Optional[builtins.object] = ...,
autospec: Optional[builtins.object] = ...,
spec_set: builtins.object | None = ...,
autospec: builtins.object | None = ...,
new_callable: None = ...,
**kwargs: Any,
) -> MockType: ...
Expand All @@ -408,10 +406,10 @@ def __call__(
self,
target: str,
new: _T,
spec: Optional[builtins.object] = ...,
spec: builtins.object | None = ...,
create: bool = ...,
spec_set: Optional[builtins.object] = ...,
autospec: Optional[builtins.object] = ...,
spec_set: builtins.object | None = ...,
autospec: builtins.object | None = ...,
new_callable: None = ...,
**kwargs: Any,
) -> _T: ...
Expand All @@ -421,10 +419,10 @@ def __call__(
self,
target: str,
new: None,
spec: Optional[builtins.object],
spec: builtins.object | None,
create: bool,
spec_set: Optional[builtins.object],
autospec: Optional[builtins.object],
spec_set: builtins.object | None,
autospec: builtins.object | None,
new_callable: Callable[[], _T],
**kwargs: Any,
) -> _T: ...
Expand All @@ -434,10 +432,10 @@ def __call__(
self,
target: str,
new: None = ...,
spec: Optional[builtins.object] = ...,
spec: builtins.object | None = ...,
create: bool = ...,
spec_set: Optional[builtins.object] = ...,
autospec: Optional[builtins.object] = ...,
spec_set: builtins.object | None = ...,
autospec: builtins.object | None = ...,
*,
new_callable: Callable[[], _T],
**kwargs: Any,
Expand All @@ -447,11 +445,11 @@ def __call__(
self,
target: str,
new: builtins.object = DEFAULT,
spec: Optional[builtins.object] = None,
spec: builtins.object | None = None,
create: bool = False,
spec_set: Optional[builtins.object] = None,
autospec: Optional[builtins.object] = None,
new_callable: Optional[Callable[[], Any]] = None,
spec_set: builtins.object | None = None,
autospec: builtins.object | None = None,
new_callable: Callable[[], Any] | None = None,
**kwargs: Any,
) -> Any:
"""API to mock.patch"""
Expand Down Expand Up @@ -520,7 +518,7 @@ def assert_wrapper(
msg += "\n\npytest introspection follows:\n" + introspection
e = AssertionError(msg)
e._mock_introspection_applied = True # type:ignore[attr-defined]
raise e
raise e # noqa:TRY201


def assert_has_calls_wrapper(
Expand All @@ -547,13 +545,13 @@ def assert_has_calls_wrapper(
if actual_call is not None:
actual_args, actual_kwargs = actual_call
else:
actual_args = tuple()
actual_args = ()
actual_kwargs = {}

if expect_call is not None:
_, expect_args, expect_kwargs = expect_call
else:
expect_args = tuple()
expect_args = ()
expect_kwargs = {}

try:
Expand All @@ -568,7 +566,7 @@ def assert_has_calls_wrapper(
msg += "\n\npytest introspection follows:\n" + introspection
e = AssertionError(msg)
e._mock_introspection_applied = True # type:ignore[attr-defined]
raise e
raise e # noqa:TRY201


def wrap_assert_not_called(*args: Any, **kwargs: Any) -> None:
Expand Down
36 changes: 20 additions & 16 deletions tests/test_pytest_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import re
import sys
import warnings
from collections.abc import Callable
from collections.abc import Generator
from collections.abc import Iterable
from collections.abc import Iterator
from contextlib import contextmanager
from typing import Any
from typing import Callable
from unittest.mock import AsyncMock
from unittest.mock import MagicMock

Expand Down Expand Up @@ -50,7 +50,7 @@ def needs_assert_rewrite(pytestconfig):
if option != "rewrite":
pytest.skip(
"this test needs assertion rewrite to work but current option "
'is "{}"'.format(option)
f'is "{option}"'
)


Expand Down Expand Up @@ -631,7 +631,7 @@ def bar(self) -> Iterable[int]:

def test_spy_return_iter_resets(mocker: MockerFixture) -> None:
class Foo:
iterables: Any = [
iterables: Any = [ # noqa:RUF012
(i for i in range(3)),
99,
]
Expand Down Expand Up @@ -778,8 +778,8 @@ def test_assert_called_args_with_introspection(mocker: MockerFixture) -> None:
def test_assert_called_kwargs_with_introspection(mocker: MockerFixture) -> None:
stub = mocker.stub()

complex_kwargs = dict(foo={"bar": 1, "baz": "spam"})
wrong_kwargs = dict(foo={"goo": 1, "baz": "bran"})
complex_kwargs = {"foo": {"bar": 1, "baz": "spam"}}
wrong_kwargs = {"foo": {"goo": 1, "baz": "bran"}}

stub(**complex_kwargs)
stub.assert_called_with(**complex_kwargs)
Expand Down Expand Up @@ -1134,11 +1134,13 @@ def doIt(self):
"https://pytest-mock.readthedocs.io/en/latest/usage.html#usage-as-context-manager"
)

with pytest.warns(
PytestMockWarning, match=re.escape(expected_warning_msg)
) as warn_record:
with mocker.patch.object(a, "doIt", return_value=True):
assert a.doIt() is True
with (
pytest.warns(
PytestMockWarning, match=re.escape(expected_warning_msg)
) as warn_record,
mocker.patch.object(a, "doIt", return_value=True),
):
assert a.doIt() is True

assert warn_record[0].filename == __file__

Expand All @@ -1151,11 +1153,13 @@ def test_warn_patch_context_manager(mocker: MockerFixture) -> None:
"https://pytest-mock.readthedocs.io/en/latest/usage.html#usage-as-context-manager"
)

with pytest.warns(
PytestMockWarning, match=re.escape(expected_warning_msg)
) as warn_record:
with mocker.patch("json.loads"):
pass
with (
pytest.warns(
PytestMockWarning, match=re.escape(expected_warning_msg)
) as warn_record,
mocker.patch("json.loads"),
):
pass

assert warn_record[0].filename == __file__

Expand Down Expand Up @@ -1190,7 +1194,7 @@ def doIt(self):

a = A()

with warnings.catch_warnings(record=True) as warn_record:
with warnings.catch_warnings(record=True) as warn_record: # noqa:SIM117
with mocker.patch.context_manager(a, "doIt", return_value=True):
assert a.doIt() is True

Expand Down