diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9f5aa1d..a5682d4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: strategy: fail-fast: false matrix: - python: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14-dev"] + python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] os: [ubuntu-latest, windows-latest] tox_env: ["py"] include: @@ -64,6 +64,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} + allow-prereleases: true - name: Install tox run: | diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fbf0c52..0cde5ad 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,9 +1,12 @@ Releases ======== -UNRELEASED ----------- +3.15.0 +------ + +*2025-09-04* +* Python 3.8 (EOL) is no longer supported. * `#524 `_: Added ``spy_return_iter`` to ``mocker.spy``, which contains a duplicate of the return value of the spied method if it is an ``Iterator``. 3.14.1 (2025-05-26) diff --git a/pyproject.toml b/pyproject.toml index f73f8ff..f00d66e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ dependencies = [ "pytest>=6.2.5", ] dynamic = ["version"] -requires-python = ">=3.8" +requires-python = ">=3.9" readme = "README.rst" license = {text = "MIT"} keywords = ["pytest", "mock"] @@ -27,7 +27,6 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", diff --git a/src/pytest_mock/_util.py b/src/pytest_mock/_util.py index d3a732a..ad830ca 100644 --- a/src/pytest_mock/_util.py +++ b/src/pytest_mock/_util.py @@ -15,7 +15,7 @@ def get_mock_module(config): config.getini("mock_use_standalone_module") ) if use_standalone_module: - import mock + from unittest import mock _mock_module = mock else: diff --git a/src/pytest_mock/plugin.py b/src/pytest_mock/plugin.py index e22abc3..f4dbfc3 100644 --- a/src/pytest_mock/plugin.py +++ b/src/pytest_mock/plugin.py @@ -4,19 +4,15 @@ import itertools 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 Dict -from typing import Generator -from typing import Iterable -from typing import Iterator -from typing import List -from typing import Mapping from typing import Optional -from typing import Tuple -from typing import Type from typing import TypeVar from typing import Union from typing import cast @@ -53,7 +49,7 @@ class MockCache: Cache MagicMock and Patcher instances so we can undo them later. """ - cache: List[MockCacheItem] = field(default_factory=list) + cache: list[MockCacheItem] = field(default_factory=list) def _find(self, mock: MockType) -> MockCacheItem: for mock_item in self.cache: @@ -125,7 +121,7 @@ def resetall( :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: @@ -348,7 +344,7 @@ def multiple( autospec: Optional[builtins.object] = None, new_callable: Optional[builtins.object] = None, **kwargs: Any, - ) -> Dict[str, MockType]: + ) -> dict[str, MockType]: """API to mock.patch.multiple""" return self._start_patch( self.mock_module.patch.multiple, @@ -365,7 +361,7 @@ def multiple( 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, ) -> Any: @@ -477,8 +473,8 @@ def _mocker(pytestconfig: Any) -> Generator[MockerFixture, None, None]: session_mocker = pytest.fixture(scope="session")(_mocker) -_mock_module_patches = [] # type: List[Any] -_mock_module_originals = {} # type: Dict[str, Any] +_mock_module_patches: list[Any] = [] +_mock_module_originals: dict[str, Any] = {} def assert_wrapper( diff --git a/tests/test_pytest_mock.py b/tests/test_pytest_mock.py index 174a362..e9b06c8 100644 --- a/tests/test_pytest_mock.py +++ b/tests/test_pytest_mock.py @@ -3,14 +3,12 @@ import re import sys import warnings +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 typing import Generator -from typing import Iterable -from typing import Iterator -from typing import Tuple -from typing import Type from unittest.mock import AsyncMock from unittest.mock import MagicMock @@ -102,15 +100,15 @@ def check(mocked_rm, mocked_ls): return check -def mock_using_patch_object(mocker: MockerFixture) -> Tuple[MagicMock, MagicMock]: +def mock_using_patch_object(mocker: MockerFixture) -> tuple[MagicMock, MagicMock]: return mocker.patch.object(os, "remove"), mocker.patch.object(os, "listdir") -def mock_using_patch(mocker: MockerFixture) -> Tuple[MagicMock, MagicMock]: +def mock_using_patch(mocker: MockerFixture) -> tuple[MagicMock, MagicMock]: return mocker.patch("os.remove"), mocker.patch("os.listdir") -def mock_using_patch_multiple(mocker: MockerFixture) -> Tuple[MagicMock, MagicMock]: +def mock_using_patch_multiple(mocker: MockerFixture) -> tuple[MagicMock, MagicMock]: r = mocker.patch.multiple("os", remove=mocker.DEFAULT, listdir=mocker.DEFAULT) return r["remove"], r["listdir"] @@ -212,10 +210,7 @@ def test_mocker_resetall(mocker: MockerFixture) -> None: assert isinstance(listdir.return_value, mocker.Mock) assert open.side_effect is None - if sys.version_info >= (3, 9): - # The reset on child mocks have been implemented in 3.9 - # https://bugs.python.org/issue38932 - assert mocked_object.run.return_value != "mocked" + assert mocked_object.run.return_value != "mocked" class TestMockerStub: @@ -291,7 +286,7 @@ def bar(self, arg): ), ) def test_instance_method_spy_exception( - exc_cls: Type[BaseException], + exc_cls: type[BaseException], mocker: MockerFixture, ) -> None: class Foo: diff --git a/tox.ini b/tox.ini index f1c0e98..92bcba7 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,10 @@ [tox] minversion = 3.5.3 -envlist = py{38,39,310,311,312,313,314}, norewrite, pytest6 +envlist = py{39,310,311,312,313,314}, norewrite, pytest6 [testenv] deps = coverage - mock pytest-asyncio pytest6: pytest==6.2.5 commands =