From 6728ec5606dd7e2eb70ef64972f97eab061f95e2 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Thu, 31 Oct 2024 16:48:11 +0100 Subject: [PATCH 01/64] Raise error if sync test relies on async fixture, and warn if the fixture is autouse. --- src/_pytest/fixtures.py | 36 +++++++++++++++- testing/acceptance_test.py | 87 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 6b882fa3515..499d065c4a9 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -73,6 +73,7 @@ from _pytest.scope import _ScopeName from _pytest.scope import HIGH_SCOPES from _pytest.scope import Scope +from _pytest.warning_types import PytestRemovedIn9Warning if sys.version_info < (3, 11): @@ -575,6 +576,7 @@ def _get_active_fixturedef( # The are no fixtures with this name applicable for the function. if not fixturedefs: raise FixtureLookupError(argname, self) + # A fixture may override another fixture with the same name, e.g. a # fixture in a module can override a fixture in a conftest, a fixture in # a class can override a fixture in the module, and so on. @@ -593,6 +595,32 @@ def _get_active_fixturedef( raise FixtureLookupError(argname, self) fixturedef = fixturedefs[index] + if not inspect.iscoroutinefunction(self.function) and ( + inspect.iscoroutinefunction(fixturedef.func) + or inspect.isasyncgenfunction(fixturedef.func) + ): + if fixturedef._autouse: + warnings.warn( + PytestRemovedIn9Warning( + "Sync test requested an async fixture with autouse=True. " + "If you intended to use the fixture you may want to make the " + "test asynchronous. If you did not intend to use it you should " + "restructure your test setup. " + "This will turn into an error in pytest 9." + ), + stacklevel=3, + ) + else: + raise FixtureLookupError( + argname, + self, + ( + "ERROR: Sync test requested async fixture. " + "You may want to make the test asynchronous and run it with " + "a suitable async framework test plugin." + ), + ) + # Prepare a SubRequest object for calling the fixture. try: callspec = self._pyfuncitem.callspec @@ -805,7 +833,7 @@ def formatrepr(self) -> FixtureLookupErrorRepr: stack = [self.request._pyfuncitem.obj] stack.extend(map(lambda x: x.func, self.fixturestack)) msg = self.msg - if msg is not None: + if msg is not None and len(stack) > 1: # The last fixture raise an error, let's present # it at the requesting side. stack = stack[:-1] @@ -959,6 +987,8 @@ def __init__( ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None, *, _ispytest: bool = False, + # only used to emit a deprecationwarning, can be removed in pytest9 + _autouse: bool = False, ) -> None: check_ispytest(_ispytest) # The "base" node ID for the fixture. @@ -1005,6 +1035,9 @@ def __init__( self.cached_result: _FixtureCachedResult[FixtureValue] | None = None self._finalizers: Final[list[Callable[[], object]]] = [] + # only used to emit a deprecationwarning, can be removed in pytest9 + self._autouse = _autouse + @property def scope(self) -> _ScopeName: """Scope string, one of "function", "class", "module", "package", "session".""" @@ -1666,6 +1699,7 @@ def _register_fixture( params=params, ids=ids, _ispytest=True, + _autouse=autouse, ) faclist = self._arg2fixturedefs.setdefault(name, []) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 7a8d871144a..0437cae4047 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1286,6 +1286,93 @@ def test_3(): result.assert_outcomes(failed=3) +def test_error_on_sync_test_async_fixture(pytester: Pytester) -> None: + pytester.makepyfile( + test_sync=""" + import pytest + + @pytest.fixture + async def async_fixture(): + ... + + def test_foo(async_fixture): + ... + """ + ) + result = pytester.runpytest() + result.stdout.fnmatch_lines( + [ + ( + "*ERROR: Sync test requested async fixture. " + "You may want to make the test asynchronous and run it with " + "a suitable async framework test plugin.*" + ), + "*ERROR test_sync.py::test_foo*", + ] + ) + result.assert_outcomes(errors=1) + + +def test_error_on_sync_test_async_fixture_gen(pytester: Pytester) -> None: + pytester.makepyfile( + test_sync=""" + import pytest + + @pytest.fixture + async def async_fixture(): + yield + + def test_foo(async_fixture): + ... + """ + ) + result = pytester.runpytest() + result.stdout.fnmatch_lines( + [ + ( + "*ERROR: Sync test requested async fixture. " + "You may want to make the test asynchronous and run it with " + "a suitable async framework test plugin.*" + ), + "*ERROR test_sync.py::test_foo*", + ] + ) + result.assert_outcomes(errors=1) + + +def test_warning_on_sync_test_async_autouse_fixture(pytester: Pytester) -> None: + pytester.makepyfile( + test_sync=""" + import pytest + + @pytest.fixture(autouse=True) + async def async_fixture(): + ... + + # We explicitly request the fixture to be able to + # suppress the RuntimeWarning for unawaited coroutine. + def test_foo(async_fixture): + try: + async_fixture.send(None) + except StopIteration: + pass + """ + ) + result = pytester.runpytest() + result.stdout.fnmatch_lines( + [ + ( + "*Sync test requested an async fixture with autouse=True. " + "If you intended to use the fixture you may want to make the " + "test asynchronous. If you did not intend to use it you should " + "restructure your test setup. " + "This will turn into an error in pytest 9.*" + ), + ] + ) + result.assert_outcomes(passed=1) + + def test_pdb_can_be_rewritten(pytester: Pytester) -> None: pytester.makepyfile( **{ From 8e100ea2c70ea7252141902af819a7aae44a53c1 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Fri, 1 Nov 2024 11:56:59 +0100 Subject: [PATCH 02/64] fix tests --- src/_pytest/fixtures.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 499d065c4a9..da6625fb7e1 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -595,9 +595,16 @@ def _get_active_fixturedef( raise FixtureLookupError(argname, self) fixturedef = fixturedefs[index] - if not inspect.iscoroutinefunction(self.function) and ( - inspect.iscoroutinefunction(fixturedef.func) - or inspect.isasyncgenfunction(fixturedef.func) + # Check for attempted use of an async fixture by a sync test + # `self.scope` here is not the scope of the requested fixture, but the scope of + # the requester. + if ( + self.scope == "function" + and not inspect.iscoroutinefunction(self._pyfuncitem.obj) + and ( + inspect.iscoroutinefunction(fixturedef.func) + or inspect.isasyncgenfunction(fixturedef.func) + ) ): if fixturedef._autouse: warnings.warn( From 283db4ed9f803c6b1f19b8920ed8b01cfda20b59 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Fri, 1 Nov 2024 12:15:23 +0100 Subject: [PATCH 03/64] add changelog --- changelog/10839.deprecation.rst | 1 + changelog/10839.improvement.rst | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelog/10839.deprecation.rst create mode 100644 changelog/10839.improvement.rst diff --git a/changelog/10839.deprecation.rst b/changelog/10839.deprecation.rst new file mode 100644 index 00000000000..c0628191663 --- /dev/null +++ b/changelog/10839.deprecation.rst @@ -0,0 +1 @@ +Synchronous tests that request an asynchronous fixture with ``autouse=True`` will now give a DeprecationWarning. diff --git a/changelog/10839.improvement.rst b/changelog/10839.improvement.rst new file mode 100644 index 00000000000..0a382053c85 --- /dev/null +++ b/changelog/10839.improvement.rst @@ -0,0 +1 @@ +Synchronous tests that request asynchronous fixtures will now error, instead of silently accepting an unawaited coroutine object as the fixture value. From 5beab07de39dc4dd8a8db289f934288ae5be6bfc Mon Sep 17 00:00:00 2001 From: jakkdl Date: Wed, 6 Nov 2024 14:02:25 +0100 Subject: [PATCH 04/64] improve warning message. Make it warn regardless of autouse or not. Add section to deprecations.rst --- changelog/10839.deprecation.rst | 2 +- changelog/10839.improvement.rst | 1 - doc/en/deprecations.rst | 55 +++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/_pytest/fixtures.py | 19 +++++++----- testing/acceptance_test.py | 40 +++++++++++++++--------- 6 files changed, 93 insertions(+), 26 deletions(-) delete mode 100644 changelog/10839.improvement.rst diff --git a/changelog/10839.deprecation.rst b/changelog/10839.deprecation.rst index c0628191663..78ad4e118e4 100644 --- a/changelog/10839.deprecation.rst +++ b/changelog/10839.deprecation.rst @@ -1 +1 @@ -Synchronous tests that request an asynchronous fixture with ``autouse=True`` will now give a DeprecationWarning. +Synchronous tests that request an asynchronous fixture will now give a DeprecationWarning. This will introduce warnings in several pytest plugins that handle async tests/fixtures and for some users with custom setups. For guidance on how to manage this see :ref:`sync-test-async-fixture`. diff --git a/changelog/10839.improvement.rst b/changelog/10839.improvement.rst deleted file mode 100644 index 0a382053c85..00000000000 --- a/changelog/10839.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -Synchronous tests that request asynchronous fixtures will now error, instead of silently accepting an unawaited coroutine object as the fixture value. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index e55f0d71c2e..b384f01dfab 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -15,6 +15,61 @@ Below is a complete list of all pytest features which are considered deprecated. :class:`~pytest.PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters `. +.. _sync-test-async-fixture: + +sync test depending on async fixture +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 8.4 + +Pytest has for a long time given an error when encountering an asynchronous test function, prompting the user to install +a plugin that can handle it. It has not given any errors if you have an asynchronous fixture that's depended on by a +synchronous test. This is a problem even if you do have a plugin installed for handling async tests, as they may require +special decorators for async fixtures to be handled, and some may not robustly handle if a user accidentally requests an +async fixture from their sync tests. Fixture values being cached can make this even more unintuitive, where everything will +"work" if the fixture is first requested by an async test, and then requested by a synchronous test. + +Unfortunately there is no 100% reliable method of identifying when a user has made a mistake, versus when they expect an +unawaited object from their fixture that they will handle - either on their own, or by a plugin. To suppress this warning +when you in fact did intend to handle this you can wrap your async fixture in a synchronous fixture: + +.. code-block:: python + + import asyncio + import pytest + + + @pytest.fixture + async def unawaited_fixture(): + return 1 + + + def test_foo(unawaited_fixture): + assert 1 == asyncio.run(unawaited_fixture) + +should be changed to + + +.. code-block:: python + + import asyncio + import pytest + + + @pytest.fixture + def unawaited_fixture(): + async def inner_fixture(): + return 1 + + return inner_fixture() + + + def test_foo(unawaited_fixture): + assert 1 == asyncio.run(unawaited_fixture) + +If a user has an async fixture with ``autouse=True`` in their ``conftest.py``, or in a file where they also have synchronous tests, they will also get this warning. We strongly recommend against this practice, and they should restructure their testing infrastructure so the fixture is synchronous or to separate the fixture from their synchronous tests. Plugins that handle async may want to introduce helpers to make that easier in scenarios where that might be wanted, e.g. if setting up a database in an asynchronous way, or the user may opt to make their test async even though it might not strictly need to be. + + .. _import-or-skip-import-error: ``pytest.importorskip`` default behavior regarding :class:`ImportError` diff --git a/pyproject.toml b/pyproject.toml index caeb4bf7f83..b023f788563 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -308,7 +308,7 @@ disable = [ ] [tool.codespell] -ignore-words-list = "afile,asser,assertio,feld,hove,ned,noes,notin,paramete,parth,socio-economic,tesults,varius,wil" +ignore-words-list = "afile,asend,asser,assertio,feld,hove,ned,noes,notin,paramete,parth,socio-economic,tesults,varius,wil" skip = "*/plugin_list.rst" write-changes = true diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index da6625fb7e1..4b45489cd96 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -609,23 +609,26 @@ def _get_active_fixturedef( if fixturedef._autouse: warnings.warn( PytestRemovedIn9Warning( - "Sync test requested an async fixture with autouse=True. " + f"Sync test {self._pyfuncitem.name!r} requested async fixture " + f"{argname!r} with autouse=True. " "If you intended to use the fixture you may want to make the " - "test asynchronous. If you did not intend to use it you should " + "test asynchronous or the fixture synchronous. " + "If you did not intend to use it you should " "restructure your test setup. " "This will turn into an error in pytest 9." ), stacklevel=3, ) else: - raise FixtureLookupError( - argname, - self, - ( - "ERROR: Sync test requested async fixture. " + warnings.warn( + PytestRemovedIn9Warning( + f"Sync test {self._pyfuncitem.name!r} requested async fixture " + f"{argname!r}. " "You may want to make the test asynchronous and run it with " - "a suitable async framework test plugin." + "a suitable async framework test plugin, or make the fixture synchronous. " + "This will turn into an error in pytest 9." ), + stacklevel=3, ) # Prepare a SubRequest object for calling the fixture. diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 0437cae4047..699ad4797b9 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1296,21 +1296,26 @@ async def async_fixture(): ... def test_foo(async_fixture): - ... + # suppress unawaited coroutine warning + try: + async_fixture.send(None) + except StopIteration: + pass """ ) result = pytester.runpytest() result.stdout.fnmatch_lines( [ ( - "*ERROR: Sync test requested async fixture. " + "*Sync test 'test_foo' requested async fixture " + "'async_fixture'. " "You may want to make the test asynchronous and run it with " - "a suitable async framework test plugin.*" + "a suitable async framework test plugin, or make the fixture synchronous. " + "This will turn into an error in pytest 9." ), - "*ERROR test_sync.py::test_foo*", ] ) - result.assert_outcomes(errors=1) + result.assert_outcomes(passed=1, warnings=1) def test_error_on_sync_test_async_fixture_gen(pytester: Pytester) -> None: @@ -1323,21 +1328,26 @@ async def async_fixture(): yield def test_foo(async_fixture): - ... + # suppress unawaited coroutine warning + try: + async_fixture.asend(None) + except StopIteration: + pass """ ) result = pytester.runpytest() result.stdout.fnmatch_lines( [ ( - "*ERROR: Sync test requested async fixture. " + "*Sync test 'test_foo' requested async fixture " + "'async_fixture'. " "You may want to make the test asynchronous and run it with " - "a suitable async framework test plugin.*" + "a suitable async framework test plugin, or make the fixture synchronous. " + "This will turn into an error in pytest 9." ), - "*ERROR test_sync.py::test_foo*", ] ) - result.assert_outcomes(errors=1) + result.assert_outcomes(passed=1, warnings=1) def test_warning_on_sync_test_async_autouse_fixture(pytester: Pytester) -> None: @@ -1362,11 +1372,11 @@ def test_foo(async_fixture): result.stdout.fnmatch_lines( [ ( - "*Sync test requested an async fixture with autouse=True. " - "If you intended to use the fixture you may want to make the " - "test asynchronous. If you did not intend to use it you should " - "restructure your test setup. " - "This will turn into an error in pytest 9.*" + "*Sync test 'test_foo' requested async fixture " + "'async_fixture' with autouse=True. " + "You may want to make the test asynchronous and run it with " + "a suitable async framework test plugin, or make the fixture synchronous. " + "This will turn into an error in pytest 9." ), ] ) From 0de5302bfde78747ca6b4d468d7d60658f43307c Mon Sep 17 00:00:00 2001 From: jakkdl Date: Wed, 6 Nov 2024 14:12:12 +0100 Subject: [PATCH 05/64] fix test --- testing/acceptance_test.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 699ad4797b9..a7a9afedbc0 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1286,7 +1286,7 @@ def test_3(): result.assert_outcomes(failed=3) -def test_error_on_sync_test_async_fixture(pytester: Pytester) -> None: +def test_warning_on_sync_test_async_fixture(pytester: Pytester) -> None: pytester.makepyfile( test_sync=""" import pytest @@ -1318,7 +1318,7 @@ def test_foo(async_fixture): result.assert_outcomes(passed=1, warnings=1) -def test_error_on_sync_test_async_fixture_gen(pytester: Pytester) -> None: +def test_warning_on_sync_test_async_fixture_gen(pytester: Pytester) -> None: pytester.makepyfile( test_sync=""" import pytest @@ -1374,13 +1374,15 @@ def test_foo(async_fixture): ( "*Sync test 'test_foo' requested async fixture " "'async_fixture' with autouse=True. " - "You may want to make the test asynchronous and run it with " - "a suitable async framework test plugin, or make the fixture synchronous. " + "If you intended to use the fixture you may want to make the " + "test asynchronous or the fixture synchronous. " + "If you did not intend to use it you should " + "restructure your test setup. " "This will turn into an error in pytest 9." ), ] ) - result.assert_outcomes(passed=1) + result.assert_outcomes(passed=1, warnings=1) def test_pdb_can_be_rewritten(pytester: Pytester) -> None: From 1891fedbb1e37c9e61cbdcdaf2d05f5493a24d2c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 6 Nov 2024 13:30:49 -0300 Subject: [PATCH 06/64] Rename changelog entries to 'breaking' (#12942) Follow up to https://github.com/pytest-dev/pytest/pull/12346. --- changelog/{11372.improvement.rst => 11372.breaking.rst} | 0 changelog/{12346.misc.rst => 12346.breaking.rst} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename changelog/{11372.improvement.rst => 11372.breaking.rst} (100%) rename changelog/{12346.misc.rst => 12346.breaking.rst} (100%) diff --git a/changelog/11372.improvement.rst b/changelog/11372.breaking.rst similarity index 100% rename from changelog/11372.improvement.rst rename to changelog/11372.breaking.rst diff --git a/changelog/12346.misc.rst b/changelog/12346.breaking.rst similarity index 100% rename from changelog/12346.misc.rst rename to changelog/12346.breaking.rst From 6b9de2ab2e52181102bdaf9acbaf7815eb078ed2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 23:01:37 +0000 Subject: [PATCH 07/64] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.9 → v0.7.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.9...v0.7.2) - [github.com/adamchainz/blacken-docs: 1.19.0 → 1.19.1](https://github.com/adamchainz/blacken-docs/compare/1.19.0...1.19.1) - [github.com/pre-commit/mirrors-mypy: v1.11.2 → v1.13.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.2...v1.13.0) - [github.com/tox-dev/pyproject-fmt: 2.3.1 → v2.5.0](https://github.com/tox-dev/pyproject-fmt/compare/2.3.1...v2.5.0) - [github.com/asottile/pyupgrade: v3.18.0 → v3.19.0](https://github.com/asottile/pyupgrade/compare/v3.18.0...v3.19.0) [mypy] Remove useless noqa, add noqa for new false positives Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) --- .pre-commit-config.yaml | 11 ++++++----- pyproject.toml | 3 ++- src/_pytest/_io/pprint.py | 6 +++--- src/_pytest/capture.py | 6 ++++-- testing/test_runner.py | 4 ++-- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 096228acf9f..04971cf9fb2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.6.9" + rev: "v0.7.2" hooks: - id: ruff args: ["--fix"] @@ -12,7 +12,7 @@ repos: - id: end-of-file-fixer - id: check-yaml - repo: https://github.com/adamchainz/blacken-docs - rev: 1.19.0 + rev: 1.19.1 hooks: - id: blacken-docs additional_dependencies: [black==24.1.1] @@ -28,7 +28,7 @@ repos: hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.2 + rev: v1.13.0 hooks: - id: mypy files: ^(src/|testing/|scripts/) @@ -44,13 +44,13 @@ repos: # on <3.11 - exceptiongroup>=1.0.0rc8 - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.3.1" + rev: "v2.5.0" hooks: - id: pyproject-fmt # https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version additional_dependencies: ["tox>=4.9"] - repo: https://github.com/asottile/pyupgrade - rev: v3.18.0 + rev: v3.19.0 hooks: - id: pyupgrade stages: [manual] @@ -62,6 +62,7 @@ repos: language: system types: [python] args: ["-rn", "-sn", "--fail-on=I"] + require_serial: true stages: [manual] - id: rst name: rst diff --git a/pyproject.toml b/pyproject.toml index b023f788563..da47e4b045e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,7 @@ dependencies = [ "exceptiongroup>=1.0.0rc8; python_version<'3.11'", "iniconfig", "packaging", - "pluggy<2,>=1.5", + "pluggy>=1.5,<2", "tomli>=1; python_version<'3.11'", ] optional-dependencies.dev = [ @@ -275,6 +275,7 @@ disable = [ "too-many-lines", "too-many-locals", "too-many-nested-blocks", + "too-many-positional-arguments", "too-many-public-methods", "too-many-return-statements", # disabled in ruff "too-many-statements", # disabled in ruff diff --git a/src/_pytest/_io/pprint.py b/src/_pytest/_io/pprint.py index fc29989be0b..7a6433d9128 100644 --- a/src/_pytest/_io/pprint.py +++ b/src/_pytest/_io/pprint.py @@ -111,15 +111,15 @@ def _format( p(self, object, stream, indent, allowance, context, level + 1) context.remove(objid) elif ( - _dataclasses.is_dataclass(object) # type:ignore[unreachable] + _dataclasses.is_dataclass(object) and not isinstance(object, type) - and object.__dataclass_params__.repr + and object.__dataclass_params__.repr # type:ignore[attr-defined] and # Check dataclass has generated repr method. hasattr(object.__repr__, "__wrapped__") and "__create_fn__" in object.__repr__.__wrapped__.__qualname__ ): - context.add(objid) # type:ignore[unreachable] + context.add(objid) self._pprint_dataclass( object, stream, indent, allowance, context, level + 1 ) diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 506c0b3d287..2ac3b6bbc7f 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -15,6 +15,7 @@ from typing import Any from typing import AnyStr from typing import BinaryIO +from typing import cast from typing import Final from typing import final from typing import Generator @@ -177,7 +178,8 @@ def name(self) -> str: def mode(self) -> str: # TextIOWrapper doesn't expose a mode, but at least some of our # tests check it. - return self.buffer.mode.replace("b", "") + assert hasattr(self.buffer, "mode") + return cast(str, self.buffer.mode.replace("b", "")) class CaptureIO(io.TextIOWrapper): @@ -550,7 +552,7 @@ def snap(self) -> bytes: res = self.tmpfile.buffer.read() self.tmpfile.seek(0) self.tmpfile.truncate() - return res + return res # type: ignore[return-value] def writeorg(self, data: bytes) -> None: """Write to original file descriptor.""" diff --git a/testing/test_runner.py b/testing/test_runner.py index 0d9facdcd71..0245438a47d 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -137,8 +137,8 @@ def raiser(exc): ss.teardown_exact(None) mod, func = e.value.exceptions assert isinstance(mod, KeyError) - assert isinstance(func.exceptions[0], TypeError) # type: ignore - assert isinstance(func.exceptions[1], ValueError) # type: ignore + assert isinstance(func.exceptions[0], TypeError) + assert isinstance(func.exceptions[1], ValueError) def test_cached_exception_doesnt_get_longer(self, pytester: Pytester) -> None: """Regression test for #12204 (the "BTW" case).""" From 94dd15341a036b0a464992c2be0e48d4a8b9d23c Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Tue, 29 Oct 2024 10:08:27 +0100 Subject: [PATCH 08/64] Upgrade pylint version, activate all extensions --- .pre-commit-config.yaml | 2 +- pyproject.toml | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 04971cf9fb2..b8e7053e1c8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -61,7 +61,7 @@ repos: entry: pylint language: system types: [python] - args: ["-rn", "-sn", "--fail-on=I"] + args: ["-rn", "-sn", "--fail-on=I", "--enable-all-extentions"] require_serial: true stages: [manual] - id: rst diff --git a/pyproject.toml b/pyproject.toml index da47e4b045e..c968b826554 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -200,7 +200,9 @@ disable = [ "arguments-renamed", "assigning-non-slot", "attribute-defined-outside-init", + "bad-builtin", "bad-classmethod-argument", + "bad-dunder-name", "bad-mcs-method-argument", "broad-exception-caught", "broad-exception-raised", @@ -209,25 +211,41 @@ disable = [ "comparison-with-callable", "comparison-with-itself", # PLR0124 from ruff "condition-evals-to-constant", + "consider-alternative-union-syntax", + "confusing-consecutive-elif", + "consider-using-any-or-all", + "consider-using-assignment-expr", "consider-using-dict-items", "consider-using-from-import", "consider-using-f-string", "consider-using-in", + "consider-using-namedtuple-or-dataclass", "consider-using-ternary", + "consider-using-tuple", "consider-using-with", "consider-using-from-import", # not activated by default, PLR0402 disabled in ruff + "consider-ternary-expression", "cyclic-import", + "differing-param-doc", + "docstring-first-line-empty", + "deprecated-argument", + "deprecated-attribute", + "deprecated-class", + "deprecated-typing-alias", + "dict-init-mutate", "disallowed-name", # foo / bar are used often in tests "duplicate-code", "else-if-used", # not activated by default, PLR5501 disabled in ruff "empty-comment", # not activated by default, PLR2044 disabled in ruff "eval-used", + "eq-without-hash", "exec-used", "expression-not-assigned", "fixme", "global-statement", # PLW0603 disabled in ruff "import-error", "import-outside-toplevel", + "import-private-name", "inconsistent-return-statements", "invalid-bool-returned", "invalid-name", @@ -238,8 +256,12 @@ disable = [ "magic-value-comparison", # not activated by default, PLR2004 disabled in ruff "method-hidden", "missing-docstring", + "missing-param-doc", + "missing-raises-doc", "missing-timeout", + "missing-type-doc", "misplaced-bare-raise", # PLE0704 from ruff + "misplaced-comparison-constant", "multiple-statements", # multiple-statements-on-one-line-colon (E701) from ruff "no-else-break", "no-else-continue", @@ -248,6 +270,7 @@ disable = [ "no-member", "no-name-in-module", "no-self-argument", + "no-self-use", "not-an-iterable", "not-callable", "pointless-exception-statement", # https://github.com/pytest-dev/pytest/pull/12379 @@ -260,12 +283,14 @@ disable = [ "redefined-builtin", "redefined-loop-name", # PLW2901 disabled in ruff "redefined-outer-name", + "redefined-variable-type", "reimported", "simplifiable-condition", "simplifiable-if-expression", "singleton-comparison", "superfluous-parens", "super-init-not-called", + "too-complex", "too-few-public-methods", "too-many-ancestors", "too-many-arguments", # disabled in ruff @@ -279,6 +304,7 @@ disable = [ "too-many-public-methods", "too-many-return-statements", # disabled in ruff "too-many-statements", # disabled in ruff + "too-many-try-statements", "try-except-raise", "typevar-name-incorrect-variance", # PLC0105 disabled in ruff "unbalanced-tuple-unpacking", @@ -300,10 +326,12 @@ disable = [ "use-dict-literal", "use-implicit-booleaness-not-comparison", "use-implicit-booleaness-not-len", + "use-set-for-membership", "useless-else-on-loop", # PLC0414 disabled in ruff "useless-import-alias", "useless-return", "using-constant-test", + "while-used", "wrong-import-order", # handled by isort / ruff "wrong-import-position", # handled by isort / ruff ] From b19fd523e18a35dd17bc3ae61a251b73cb940551 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Tue, 29 Oct 2024 10:09:09 +0100 Subject: [PATCH 09/64] [pylint dict-init-mutate] Initialize a dict right off for speed --- pyproject.toml | 1 - src/_pytest/junitxml.py | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c968b826554..3636ee455ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -232,7 +232,6 @@ disable = [ "deprecated-attribute", "deprecated-class", "deprecated-typing-alias", - "dict-init-mutate", "disallowed-name", # foo / bar are used often in tests "duplicate-code", "else-if-used", # not activated by default, PLR5501 disabled in ruff diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index 3a2cb59a6c1..efe6f489b48 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -74,10 +74,10 @@ def merge_family(left, right) -> None: left.update(result) -families = {} -families["_base"] = {"testcase": ["classname", "name"]} -families["_base_legacy"] = {"testcase": ["file", "line", "url"]} - +families = { # pylint: disable=dict-init-mutate + "_base": {"testcase": ["classname", "name"]}, + "_base_legacy": {"testcase": ["file", "line", "url"]}, +} # xUnit 1.x inherits legacy attributes. families["xunit1"] = families["_base"].copy() merge_family(families["xunit1"], families["_base_legacy"]) From 987904c13cdd03fd49f46a88738e13f808db99bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 10 Nov 2024 08:39:36 +0000 Subject: [PATCH 10/64] [automated] Update plugin list (#12950) Co-authored-by: pytest bot --- doc/en/reference/plugin_list.rst | 78 +++++++++++++++++++------------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 50206c34150..ecd96b89967 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -27,15 +27,15 @@ please refer to `the update script =8.3 - :pypi:`logassert` Simple but powerful assertion and verification of logged lines. May 20, 2022 5 - Production/Stable N/A + :pypi:`databricks-labs-pytester` Python Testing for Databricks Nov 04, 2024 4 - Beta pytest>=8.3 + :pypi:`logassert` Simple but powerful assertion and verification of logged lines. Nov 08, 2024 5 - Production/Stable N/A :pypi:`logot` Test whether your code is logging correctly 🪵 Mar 23, 2024 5 - Production/Stable pytest<9,>=7; extra == "pytest" :pypi:`nuts` Network Unit Testing System Jul 19, 2024 N/A pytest<8,>=7 :pypi:`pytest-abq` Pytest integration for the ABQ universal test runner. Apr 07, 2023 N/A N/A @@ -157,7 +157,7 @@ This list contains 1538 plugins. :pypi:`pytest-bdd-wrappers` Feb 11, 2020 2 - Pre-Alpha N/A :pypi:`pytest-beakerlib` A pytest plugin that reports test results to the BeakerLib framework Mar 17, 2017 5 - Production/Stable pytest :pypi:`pytest-beartype` Pytest plugin to run your tests with beartype checking enabled. Oct 31, 2024 N/A pytest - :pypi:`pytest-bec-e2e` BEC pytest plugin for end-to-end tests Oct 22, 2024 3 - Alpha pytest + :pypi:`pytest-bec-e2e` BEC pytest plugin for end-to-end tests Nov 05, 2024 3 - Alpha pytest :pypi:`pytest-beds` Fixtures for testing Google Appengine (GAE) apps Jun 07, 2016 4 - Beta N/A :pypi:`pytest-beeprint` use icdiff for better error messages in pytest assertions Jul 04, 2023 4 - Beta N/A :pypi:`pytest-bench` Benchmark utility that plugs into pytest. Jul 21, 2014 3 - Alpha N/A @@ -246,7 +246,7 @@ This list contains 1538 plugins. :pypi:`pytest-cldf` Easy quality control for CLDF datasets using pytest Nov 07, 2022 N/A pytest (>=3.6) :pypi:`pytest-cleanslate` Collects and executes pytest tests separately Sep 04, 2024 N/A pytest :pypi:`pytest_cleanup` Automated, comprehensive and well-organised pytest test cases. Jan 28, 2020 N/A N/A - :pypi:`pytest-cleanuptotal` A cleanup plugin for pytest Sep 14, 2024 5 - Production/Stable N/A + :pypi:`pytest-cleanuptotal` A cleanup plugin for pytest Nov 08, 2024 5 - Production/Stable N/A :pypi:`pytest-clerk` A set of pytest fixtures to help with integration testing with Clerk. Oct 08, 2024 N/A pytest<9.0.0,>=8.0.0 :pypi:`pytest-click` Pytest plugin for Click Feb 11, 2022 5 - Production/Stable pytest (>=5.0) :pypi:`pytest-cli-fixtures` Automatically register fixtures for custom CLI arguments Jul 28, 2022 N/A pytest (~=7.0) @@ -571,7 +571,7 @@ This list contains 1538 plugins. :pypi:`pytest-fixtures` Common fixtures for pytest May 01, 2019 5 - Production/Stable N/A :pypi:`pytest-fixture-tools` Plugin for pytest which provides tools for fixtures Aug 15, 2024 6 - Mature pytest :pypi:`pytest-fixture-typecheck` A pytest plugin to assert type annotations at runtime. Aug 24, 2021 N/A pytest - :pypi:`pytest-flake8` pytest plugin to check FLAKE8 requirements Jul 21, 2024 5 - Production/Stable pytest>=7.0 + :pypi:`pytest-flake8` pytest plugin to check FLAKE8 requirements Nov 09, 2024 5 - Production/Stable pytest>=7.0 :pypi:`pytest-flake8-path` A pytest fixture for testing flake8 plugins. Oct 25, 2024 5 - Production/Stable pytest :pypi:`pytest-flake8-v2` pytest plugin to check FLAKE8 requirements Mar 01, 2022 5 - Production/Stable pytest (>=7.0) :pypi:`pytest-flakefinder` Runs tests multiple times to expose flakiness. Oct 26, 2022 4 - Beta pytest (>=2.7.1) @@ -601,7 +601,7 @@ This list contains 1538 plugins. :pypi:`pytest-frozen-uuids` Deterministically frozen UUID's for your tests Apr 17, 2022 N/A pytest (>=3.0) :pypi:`pytest-func-cov` Pytest plugin for measuring function coverage Apr 15, 2021 3 - Alpha pytest (>=5) :pypi:`pytest-funparam` An alternative way to parametrize test cases. Dec 02, 2021 4 - Beta pytest >=4.6.0 - :pypi:`pytest-fv` pytest extensions to support running functional-verification jobs Oct 11, 2024 N/A pytest + :pypi:`pytest-fv` pytest extensions to support running functional-verification jobs Nov 07, 2024 N/A pytest :pypi:`pytest-fxa` pytest plugin for Firefox Accounts Aug 28, 2018 5 - Production/Stable N/A :pypi:`pytest-fxa-mte` pytest plugin for Firefox Accounts Oct 02, 2024 5 - Production/Stable N/A :pypi:`pytest-fxtest` Oct 27, 2020 N/A N/A @@ -616,7 +616,7 @@ This list contains 1538 plugins. :pypi:`pytest-gherkin` A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) :pypi:`pytest-gh-log-group` pytest plugin for gh actions Jan 11, 2022 3 - Alpha pytest :pypi:`pytest-ghostinspector` For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A - :pypi:`pytest-girder` A set of pytest fixtures for testing Girder applications. Jul 08, 2024 N/A pytest>=3.6 + :pypi:`pytest-girder` A set of pytest fixtures for testing Girder applications. Nov 07, 2024 N/A pytest>=3.6 :pypi:`pytest-git` Git repository fixture for py.test Oct 17, 2024 5 - Production/Stable pytest :pypi:`pytest-gitconfig` Provide a Git config sandbox for testing Aug 11, 2024 4 - Beta pytest>=7.1.2 :pypi:`pytest-gitcov` Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A @@ -660,7 +660,7 @@ This list contains 1538 plugins. :pypi:`pytest-history` Pytest plugin to keep a history of your pytest runs Jan 14, 2024 N/A pytest (>=7.4.3,<8.0.0) :pypi:`pytest-home` Home directory fixtures Jul 28, 2024 5 - Production/Stable pytest :pypi:`pytest-homeassistant` A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A - :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Nov 01, 2024 3 - Alpha pytest==8.3.3 + :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Nov 09, 2024 3 - Alpha pytest==8.3.3 :pypi:`pytest-honey` A simple plugin to use with pytest Jan 07, 2022 4 - Beta pytest (>=3.5.0) :pypi:`pytest-honors` Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A :pypi:`pytest-hot-reloading` Sep 23, 2024 N/A N/A @@ -730,6 +730,7 @@ This list contains 1538 plugins. :pypi:`pytest-interface-tester` Pytest plugin for checking charm relation interface protocol compliance. Sep 25, 2024 4 - Beta pytest :pypi:`pytest-invenio` Pytest fixtures for Invenio. Jun 27, 2024 5 - Production/Stable pytest<7.2.0,>=6 :pypi:`pytest-involve` Run tests covering a specific file or changeset Feb 02, 2020 4 - Beta pytest (>=3.5.0) + :pypi:`pytest-iovis` A Pytest plugin to enable Jupyter Notebook testing with Papermill Nov 06, 2024 4 - Beta pytest>=7.1.0 :pypi:`pytest-ipdb` A py.test plug-in to enable drop to ipdb debugger on test failure. Mar 20, 2013 2 - Pre-Alpha N/A :pypi:`pytest-ipynb` THIS PROJECT IS ABANDONED Jan 29, 2019 3 - Alpha N/A :pypi:`pytest-ipywidgets` Oct 28, 2024 N/A pytest @@ -818,7 +819,7 @@ This list contains 1538 plugins. :pypi:`pytest-logger` Plugin configuring handlers for loggers from Python logging module. Mar 10, 2024 5 - Production/Stable pytest (>=3.2) :pypi:`pytest-logging` Configures logging and allows tweaking the log level with a py.test flag Nov 04, 2015 4 - Beta N/A :pypi:`pytest-logging-end-to-end-test-tool` Sep 23, 2022 N/A pytest (>=7.1.2,<8.0.0) - :pypi:`pytest-logikal` Common testing environment Oct 28, 2024 5 - Production/Stable pytest==8.3.3 + :pypi:`pytest-logikal` Common testing environment Nov 09, 2024 5 - Production/Stable pytest==8.3.3 :pypi:`pytest-log-report` Package for creating a pytest test run reprot Dec 26, 2019 N/A N/A :pypi:`pytest-logscanner` Pytest plugin for logscanner (A logger for python logging outputting to easily viewable (and filterable) html files. Good for people not grep savey, and color higlighting and quickly changing filters might even bye useful for commandline wizards.) Sep 30, 2024 4 - Beta pytest>=8.2.2 :pypi:`pytest-loguru` Pytest Loguru Mar 20, 2024 5 - Production/Stable pytest; extra == "test" @@ -859,7 +860,7 @@ This list contains 1538 plugins. :pypi:`pytest-messenger` Pytest to Slack reporting plugin Nov 24, 2022 5 - Production/Stable N/A :pypi:`pytest-metadata` pytest plugin for test session metadata Feb 12, 2024 5 - Production/Stable pytest>=7.0.0 :pypi:`pytest-metrics` Custom metrics report for pytest Apr 04, 2020 N/A pytest - :pypi:`pytest-mh` Pytest multihost plugin Sep 19, 2024 N/A pytest + :pypi:`pytest-mh` Pytest multihost plugin Nov 04, 2024 N/A pytest :pypi:`pytest-mimesis` Mimesis integration with the pytest test runner Mar 21, 2020 5 - Production/Stable pytest (>=4.2) :pypi:`pytest-minecraft` A pytest plugin for running tests against Minecraft releases Apr 06, 2022 N/A pytest (>=6.0.1) :pypi:`pytest-mini` A plugin to test mp Feb 06, 2023 N/A pytest (>=7.2.0,<8.0.0) @@ -1006,7 +1007,7 @@ This list contains 1538 plugins. :pypi:`pytest-pg` A tiny plugin for pytest which runs PostgreSQL in Docker May 21, 2024 5 - Production/Stable pytest>=6.0.0 :pypi:`pytest-pgsql` Pytest plugins and helpers for tests using a Postgres database. May 13, 2020 5 - Production/Stable pytest (>=3.0.0) :pypi:`pytest-phmdoctest` pytest plugin to test Python examples in Markdown using phmdoctest. Apr 15, 2022 4 - Beta pytest (>=5.4.3) - :pypi:`pytest-picked` Run the tests related to the changed files Jul 27, 2023 N/A pytest (>=3.7.0) + :pypi:`pytest-picked` Run the tests related to the changed files Nov 06, 2024 N/A pytest>=3.7.0 :pypi:`pytest-pigeonhole` Jun 25, 2018 5 - Production/Stable pytest (>=3.4) :pypi:`pytest-pikachu` Show surprise when tests are passing Aug 05, 2021 5 - Production/Stable pytest :pypi:`pytest-pilot` Slice in your test base thanks to powerful markers. Oct 09, 2020 5 - Production/Stable N/A @@ -1224,7 +1225,7 @@ This list contains 1538 plugins. :pypi:`pytest-sanity` Dec 07, 2020 N/A N/A :pypi:`pytest-sa-pg` May 14, 2019 N/A N/A :pypi:`pytest_sauce` pytest_sauce provides sane and helpful methods worked out in clearcode to run py.test tests with selenium/saucelabs Jul 14, 2014 3 - Alpha N/A - :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Nov 01, 2024 5 - Production/Stable N/A + :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Nov 09, 2024 5 - Production/Stable N/A :pypi:`pytest-scenario` pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A :pypi:`pytest-scenario-files` A pytest plugin that generates unit test scenarios from data files. May 19, 2024 5 - Production/Stable pytest>=7.2.0 :pypi:`pytest-schedule` Automate and customize test scheduling effortlessly on local machines. Oct 31, 2024 N/A N/A @@ -1235,7 +1236,7 @@ This list contains 1538 plugins. :pypi:`pytest-select` A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) :pypi:`pytest-selenium` pytest plugin for Selenium Feb 01, 2024 5 - Production/Stable pytest>=6.0.0 :pypi:`pytest-selenium-auto` pytest plugin to automatically capture screenshots upon selenium webdriver events Nov 07, 2023 N/A pytest >= 7.0.0 - :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Nov 01, 2024 5 - Production/Stable N/A + :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Nov 09, 2024 5 - Production/Stable N/A :pypi:`pytest-selenium-enhancer` pytest plugin for Selenium Apr 29, 2022 5 - Production/Stable N/A :pypi:`pytest-selenium-pdiff` A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A :pypi:`pytest-selfie` A pytest plugin for selfie snapshot testing. Apr 05, 2024 N/A pytest<9.0.0,>=8.0.0 @@ -1281,6 +1282,7 @@ This list contains 1538 plugins. :pypi:`pytest-smartcollect` A plugin for collecting tests that touch changed code Oct 04, 2018 N/A pytest (>=3.5.0) :pypi:`pytest-smartcov` Smart coverage plugin for pytest. Sep 30, 2017 3 - Alpha N/A :pypi:`pytest-smell` Automated bad smell detection tool for Pytest Jun 26, 2022 N/A N/A + :pypi:`pytest-smoke` pytest plugin for supporting smoke testing Nov 07, 2024 4 - Beta pytest<9,>=7.0.0 :pypi:`pytest-smtp` Send email with pytest execution result Feb 20, 2021 N/A pytest :pypi:`pytest-smtp4dev` Plugin for smtp4dev API Jun 27, 2023 5 - Production/Stable N/A :pypi:`pytest-smtpd` An SMTP server for testing built on aiosmtpd May 15, 2023 N/A pytest @@ -1358,7 +1360,7 @@ This list contains 1538 plugins. :pypi:`pytest-symbols` pytest-symbols is a pytest plugin that adds support for passing test environment symbols into pytest tests. Nov 20, 2017 3 - Alpha N/A :pypi:`pytest-system-statistics` Pytest plugin to track and report system usage statistics Feb 16, 2022 5 - Production/Stable pytest (>=6.0.0) :pypi:`pytest-system-test-plugin` Pyst - Pytest System-Test Plugin Feb 03, 2022 N/A N/A - :pypi:`pytest_tagging` a pytest plugin to tag tests Aug 31, 2024 N/A pytest<8.0.0,>=7.1.3 + :pypi:`pytest_tagging` a pytest plugin to tag tests Nov 08, 2024 N/A pytest>=7.1.3 :pypi:`pytest-takeltest` Fixtures for ansible, testinfra and molecule Sep 07, 2024 N/A N/A :pypi:`pytest-talisker` Nov 28, 2021 N/A N/A :pypi:`pytest-tally` A Pytest plugin to generate realtime summary stats, and display them in-console using a text-based dashboard. May 22, 2023 4 - Beta pytest (>=6.2.5) @@ -1578,14 +1580,14 @@ This list contains 1538 plugins. :pypi:`databricks-labs-pytester` - *last release*: Oct 11, 2024, + *last release*: Nov 04, 2024, *status*: 4 - Beta, *requires*: pytest>=8.3 Python Testing for Databricks :pypi:`logassert` - *last release*: May 20, 2022, + *last release*: Nov 08, 2024, *status*: 5 - Production/Stable, *requires*: N/A @@ -2439,7 +2441,7 @@ This list contains 1538 plugins. Pytest plugin to run your tests with beartype checking enabled. :pypi:`pytest-bec-e2e` - *last release*: Oct 22, 2024, + *last release*: Nov 05, 2024, *status*: 3 - Alpha, *requires*: pytest @@ -3062,7 +3064,7 @@ This list contains 1538 plugins. Automated, comprehensive and well-organised pytest test cases. :pypi:`pytest-cleanuptotal` - *last release*: Sep 14, 2024, + *last release*: Nov 08, 2024, *status*: 5 - Production/Stable, *requires*: N/A @@ -5337,7 +5339,7 @@ This list contains 1538 plugins. A pytest plugin to assert type annotations at runtime. :pypi:`pytest-flake8` - *last release*: Jul 21, 2024, + *last release*: Nov 09, 2024, *status*: 5 - Production/Stable, *requires*: pytest>=7.0 @@ -5547,7 +5549,7 @@ This list contains 1538 plugins. An alternative way to parametrize test cases. :pypi:`pytest-fv` - *last release*: Oct 11, 2024, + *last release*: Nov 07, 2024, *status*: N/A, *requires*: pytest @@ -5652,7 +5654,7 @@ This list contains 1538 plugins. For finding/executing Ghost Inspector tests :pypi:`pytest-girder` - *last release*: Jul 08, 2024, + *last release*: Nov 07, 2024, *status*: N/A, *requires*: pytest>=3.6 @@ -5960,7 +5962,7 @@ This list contains 1538 plugins. A pytest plugin for use with homeassistant custom components. :pypi:`pytest-homeassistant-custom-component` - *last release*: Nov 01, 2024, + *last release*: Nov 09, 2024, *status*: 3 - Alpha, *requires*: pytest==8.3.3 @@ -6449,6 +6451,13 @@ This list contains 1538 plugins. Run tests covering a specific file or changeset + :pypi:`pytest-iovis` + *last release*: Nov 06, 2024, + *status*: 4 - Beta, + *requires*: pytest>=7.1.0 + + A Pytest plugin to enable Jupyter Notebook testing with Papermill + :pypi:`pytest-ipdb` *last release*: Mar 20, 2013, *status*: 2 - Pre-Alpha, @@ -7066,7 +7075,7 @@ This list contains 1538 plugins. :pypi:`pytest-logikal` - *last release*: Oct 28, 2024, + *last release*: Nov 09, 2024, *status*: 5 - Production/Stable, *requires*: pytest==8.3.3 @@ -7353,7 +7362,7 @@ This list contains 1538 plugins. Custom metrics report for pytest :pypi:`pytest-mh` - *last release*: Sep 19, 2024, + *last release*: Nov 04, 2024, *status*: N/A, *requires*: pytest @@ -8382,9 +8391,9 @@ This list contains 1538 plugins. pytest plugin to test Python examples in Markdown using phmdoctest. :pypi:`pytest-picked` - *last release*: Jul 27, 2023, + *last release*: Nov 06, 2024, *status*: N/A, - *requires*: pytest (>=3.7.0) + *requires*: pytest>=3.7.0 Run the tests related to the changed files @@ -9908,7 +9917,7 @@ This list contains 1538 plugins. pytest_sauce provides sane and helpful methods worked out in clearcode to run py.test tests with selenium/saucelabs :pypi:`pytest-sbase` - *last release*: Nov 01, 2024, + *last release*: Nov 09, 2024, *status*: 5 - Production/Stable, *requires*: N/A @@ -9985,7 +9994,7 @@ This list contains 1538 plugins. pytest plugin to automatically capture screenshots upon selenium webdriver events :pypi:`pytest-seleniumbase` - *last release*: Nov 01, 2024, + *last release*: Nov 09, 2024, *status*: 5 - Production/Stable, *requires*: N/A @@ -10306,6 +10315,13 @@ This list contains 1538 plugins. Automated bad smell detection tool for Pytest + :pypi:`pytest-smoke` + *last release*: Nov 07, 2024, + *status*: 4 - Beta, + *requires*: pytest<9,>=7.0.0 + + pytest plugin for supporting smoke testing + :pypi:`pytest-smtp` *last release*: Feb 20, 2021, *status*: N/A, @@ -10846,9 +10862,9 @@ This list contains 1538 plugins. Pyst - Pytest System-Test Plugin :pypi:`pytest_tagging` - *last release*: Aug 31, 2024, + *last release*: Nov 08, 2024, *status*: N/A, - *requires*: pytest<8.0.0,>=7.1.3 + *requires*: pytest>=7.1.3 a pytest plugin to tag tests From 70639efe8d33cdbf2590742b70da59efe09c977f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 07:39:17 +0100 Subject: [PATCH 11/64] build(deps): Bump django in /testing/plugins_integration (#12951) Bumps [django](https://github.com/django/django) from 5.1.2 to 5.1.3. - [Commits](https://github.com/django/django/compare/5.1.2...5.1.3) --- updated-dependencies: - dependency-name: django dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index ef01747f2fd..32992dc2589 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,5 +1,5 @@ anyio[curio,trio]==4.6.2.post1 -django==5.1.2 +django==5.1.3 pytest-asyncio==0.24.0 pytest-bdd==7.3.0 pytest-cov==6.0.0 From 7256c0c226b0eea2453cc532e6786366aa2400c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 08:39:14 +0100 Subject: [PATCH 12/64] build(deps): Bump pypa/gh-action-pypi-publish from 1.10.3 to 1.12.2 (#12953) Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.10.3 to 1.12.2. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.10.3...v1.12.2) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 974fe4947db..3d3cb7534a4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -54,7 +54,7 @@ jobs: path: dist - name: Publish package to PyPI - uses: pypa/gh-action-pypi-publish@v1.10.3 + uses: pypa/gh-action-pypi-publish@v1.12.2 with: attestations: true From c98ef2bdcaf4f0478c77c38534bce788e6581967 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 11 Nov 2024 15:09:18 +0100 Subject: [PATCH 13/64] change implementation so the check happens in pytest_fixture_setup after any hooks (from async plugins) has had a chance to resolve the awaitable --- src/_pytest/compat.py | 1 + src/_pytest/fixtures.py | 57 ++++++++++++++------------------------ testing/acceptance_test.py | 40 +++++++++++++------------- 3 files changed, 43 insertions(+), 55 deletions(-) diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 614848e0dba..64240135313 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -44,6 +44,7 @@ class NotSetType(enum.Enum): def is_generator(func: object) -> bool: + # note: this also returns true for async generator functions genfunc = inspect.isgeneratorfunction(func) return genfunc and not iscoroutinefunction(func) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 4b45489cd96..0737773f047 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -595,42 +595,6 @@ def _get_active_fixturedef( raise FixtureLookupError(argname, self) fixturedef = fixturedefs[index] - # Check for attempted use of an async fixture by a sync test - # `self.scope` here is not the scope of the requested fixture, but the scope of - # the requester. - if ( - self.scope == "function" - and not inspect.iscoroutinefunction(self._pyfuncitem.obj) - and ( - inspect.iscoroutinefunction(fixturedef.func) - or inspect.isasyncgenfunction(fixturedef.func) - ) - ): - if fixturedef._autouse: - warnings.warn( - PytestRemovedIn9Warning( - f"Sync test {self._pyfuncitem.name!r} requested async fixture " - f"{argname!r} with autouse=True. " - "If you intended to use the fixture you may want to make the " - "test asynchronous or the fixture synchronous. " - "If you did not intend to use it you should " - "restructure your test setup. " - "This will turn into an error in pytest 9." - ), - stacklevel=3, - ) - else: - warnings.warn( - PytestRemovedIn9Warning( - f"Sync test {self._pyfuncitem.name!r} requested async fixture " - f"{argname!r}. " - "You may want to make the test asynchronous and run it with " - "a suitable async framework test plugin, or make the fixture synchronous. " - "This will turn into an error in pytest 9." - ), - stacklevel=3, - ) - # Prepare a SubRequest object for calling the fixture. try: callspec = self._pyfuncitem.callspec @@ -921,6 +885,8 @@ def call_fixture_func( fixturefunc: _FixtureFunc[FixtureValue], request: FixtureRequest, kwargs ) -> FixtureValue: if is_generator(fixturefunc): + # note: this also triggers on async generators, suppressing 'unawaited coroutine' + # warning. fixturefunc = cast( Callable[..., Generator[FixtureValue, None, None]], fixturefunc ) @@ -1179,6 +1145,25 @@ def pytest_fixture_setup( fixturefunc = resolve_fixture_function(fixturedef, request) my_cache_key = fixturedef.cache_key(request) + + if inspect.isasyncgenfunction(fixturefunc) or inspect.iscoroutinefunction( + fixturefunc + ): + auto_str = " with autouse=True" if fixturedef._autouse else "" + + warnings.warn( + PytestRemovedIn9Warning( + f"{request.node.name!r} requested an async fixture " + f"{request.fixturename!r}{auto_str}, with no plugin or hook that " + "handled it. This is usually an error, as pytest does not natively " + "support it. If this is intentional, consider making the fixture " + "sync and return a coroutine/asyncgen. " + "This will turn into an error in pytest 9." + ), + # no stacklevel will point at users code, so we just point here + stacklevel=1, + ) + try: result = call_fixture_func(fixturefunc, request, kwargs) except TEST_OUTCOME as e: diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index a7a9afedbc0..ffea0dbf46b 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1306,11 +1306,13 @@ def test_foo(async_fixture): result = pytester.runpytest() result.stdout.fnmatch_lines( [ + "*== warnings summary ==*", ( - "*Sync test 'test_foo' requested async fixture " - "'async_fixture'. " - "You may want to make the test asynchronous and run it with " - "a suitable async framework test plugin, or make the fixture synchronous. " + "*PytestRemovedIn9Warning: 'test_foo' requested an async " + "fixture 'async_fixture', with no plugin or hook that handled it. " + "This is usually an error, as pytest does not natively support it. " + "If this is intentional, consider making the fixture sync and return " + "a coroutine/asyncgen. " "This will turn into an error in pytest 9." ), ] @@ -1328,21 +1330,21 @@ async def async_fixture(): yield def test_foo(async_fixture): - # suppress unawaited coroutine warning - try: - async_fixture.asend(None) - except StopIteration: - pass + # we don't need to suppress RuntimeWarning for unawaited coroutine + # as pytest internals accidentally do so already for async gens + ... """ ) result = pytester.runpytest() result.stdout.fnmatch_lines( [ + "*== warnings summary ==*", ( - "*Sync test 'test_foo' requested async fixture " - "'async_fixture'. " - "You may want to make the test asynchronous and run it with " - "a suitable async framework test plugin, or make the fixture synchronous. " + "*PytestRemovedIn9Warning: 'test_foo' requested an async " + "fixture 'async_fixture', with no plugin or hook that handled it. " + "This is usually an error, as pytest does not natively support it. " + "If this is intentional, consider making the fixture sync and return " + "a coroutine/asyncgen. " "This will turn into an error in pytest 9." ), ] @@ -1371,13 +1373,13 @@ def test_foo(async_fixture): result = pytester.runpytest() result.stdout.fnmatch_lines( [ + "*== warnings summary ==*", ( - "*Sync test 'test_foo' requested async fixture " - "'async_fixture' with autouse=True. " - "If you intended to use the fixture you may want to make the " - "test asynchronous or the fixture synchronous. " - "If you did not intend to use it you should " - "restructure your test setup. " + "*PytestRemovedIn9Warning: Sync test 'test_foo' requested an async " + "fixture 'async_fixture' with autouse=True, with no plugin or hook " + "that handled it. This is usually an error, as pytest does not " + "natively support it. If this is intentional, consider making the " + "fixture sync and return a coroutine/asyncgen. " "This will turn into an error in pytest 9." ), ] From cd3eb9800b8f36c257cfe4deb40ea275bb72e4e9 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 11 Nov 2024 15:20:25 +0100 Subject: [PATCH 14/64] fix test --- testing/acceptance_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index ffea0dbf46b..ca6df943afa 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1375,7 +1375,7 @@ def test_foo(async_fixture): [ "*== warnings summary ==*", ( - "*PytestRemovedIn9Warning: Sync test 'test_foo' requested an async " + "*PytestRemovedIn9Warning: 'test_foo' requested an async " "fixture 'async_fixture' with autouse=True, with no plugin or hook " "that handled it. This is usually an error, as pytest does not " "natively support it. If this is intentional, consider making the " From 876cc2a2b184bb68b0398ce7f9b7bbb3a731e787 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 11 Nov 2024 15:37:22 +0100 Subject: [PATCH 15/64] update docs/changelog --- changelog/10839.deprecation.rst | 2 +- doc/en/deprecations.rst | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/changelog/10839.deprecation.rst b/changelog/10839.deprecation.rst index 78ad4e118e4..01464ca0ba2 100644 --- a/changelog/10839.deprecation.rst +++ b/changelog/10839.deprecation.rst @@ -1 +1 @@ -Synchronous tests that request an asynchronous fixture will now give a DeprecationWarning. This will introduce warnings in several pytest plugins that handle async tests/fixtures and for some users with custom setups. For guidance on how to manage this see :ref:`sync-test-async-fixture`. +Requesting an asynchronous fixture without a `pytest_fixture_setup` to handle it will now give a DeprecationWarning. This most commonly happens if a sync test requests an async fixture. This should have no effect on a majority of users with async tests or fixtures, but may affect non-standard hook setups or ``autouse=True``. For guidance on how to work around this warning see :ref:`sync-test-async-fixture`. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index b384f01dfab..494bd88d282 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -24,13 +24,14 @@ sync test depending on async fixture Pytest has for a long time given an error when encountering an asynchronous test function, prompting the user to install a plugin that can handle it. It has not given any errors if you have an asynchronous fixture that's depended on by a -synchronous test. This is a problem even if you do have a plugin installed for handling async tests, as they may require +synchronous test. If the fixture was an async function you did get an "unawaited coroutine" warning, but for async yield fixtures you didn't even get that. +This is a problem even if you do have a plugin installed for handling async tests, as they may require special decorators for async fixtures to be handled, and some may not robustly handle if a user accidentally requests an async fixture from their sync tests. Fixture values being cached can make this even more unintuitive, where everything will "work" if the fixture is first requested by an async test, and then requested by a synchronous test. Unfortunately there is no 100% reliable method of identifying when a user has made a mistake, versus when they expect an -unawaited object from their fixture that they will handle - either on their own, or by a plugin. To suppress this warning +unawaited object from their fixture that they will handle on their own. To suppress this warning when you in fact did intend to handle this you can wrap your async fixture in a synchronous fixture: .. code-block:: python @@ -67,7 +68,10 @@ should be changed to def test_foo(unawaited_fixture): assert 1 == asyncio.run(unawaited_fixture) -If a user has an async fixture with ``autouse=True`` in their ``conftest.py``, or in a file where they also have synchronous tests, they will also get this warning. We strongly recommend against this practice, and they should restructure their testing infrastructure so the fixture is synchronous or to separate the fixture from their synchronous tests. Plugins that handle async may want to introduce helpers to make that easier in scenarios where that might be wanted, e.g. if setting up a database in an asynchronous way, or the user may opt to make their test async even though it might not strictly need to be. + +You can also make use of `pytest_fixture_setup` to handle the coroutine/asyncgen before pytest sees it - this is the way current async pytest plugins handle it. + +If a user has an async fixture with ``autouse=True`` in their ``conftest.py``, or in a file where they also have synchronous tests, they will also get this warning. We strongly recommend against this practice, and they should restructure their testing infrastructure so the fixture is synchronous or to separate the fixture from their synchronous tests. Note that the anyio pytest plugin has some support for sync test + async fixtures currently. .. _import-or-skip-import-error: From 1a4dfbb593fa63bbb3eeed2f79e730395f86b41a Mon Sep 17 00:00:00 2001 From: jakkdl Date: Thu, 14 Nov 2024 15:13:32 +0100 Subject: [PATCH 16/64] remove incorrect comments, add link --- doc/en/deprecations.rst | 2 +- src/_pytest/compat.py | 1 - src/_pytest/fixtures.py | 2 -- testing/acceptance_test.py | 3 +-- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 494bd88d282..88cf3eccbf3 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -71,7 +71,7 @@ should be changed to You can also make use of `pytest_fixture_setup` to handle the coroutine/asyncgen before pytest sees it - this is the way current async pytest plugins handle it. -If a user has an async fixture with ``autouse=True`` in their ``conftest.py``, or in a file where they also have synchronous tests, they will also get this warning. We strongly recommend against this practice, and they should restructure their testing infrastructure so the fixture is synchronous or to separate the fixture from their synchronous tests. Note that the anyio pytest plugin has some support for sync test + async fixtures currently. +If a user has an async fixture with ``autouse=True`` in their ``conftest.py``, or in a file where they also have synchronous tests, they will also get this warning. We strongly recommend against this practice, and they should restructure their testing infrastructure so the fixture is synchronous or to separate the fixture from their synchronous tests. Note that the `anyio pytest plugin `_ has some support for sync test + async fixtures currently. .. _import-or-skip-import-error: diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 64240135313..614848e0dba 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -44,7 +44,6 @@ class NotSetType(enum.Enum): def is_generator(func: object) -> bool: - # note: this also returns true for async generator functions genfunc = inspect.isgeneratorfunction(func) return genfunc and not iscoroutinefunction(func) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 0737773f047..95e4557fc8e 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -885,8 +885,6 @@ def call_fixture_func( fixturefunc: _FixtureFunc[FixtureValue], request: FixtureRequest, kwargs ) -> FixtureValue: if is_generator(fixturefunc): - # note: this also triggers on async generators, suppressing 'unawaited coroutine' - # warning. fixturefunc = cast( Callable[..., Generator[FixtureValue, None, None]], fixturefunc ) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index ca6df943afa..624a313ca8d 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1330,8 +1330,7 @@ async def async_fixture(): yield def test_foo(async_fixture): - # we don't need to suppress RuntimeWarning for unawaited coroutine - # as pytest internals accidentally do so already for async gens + # async gens don't emit unawaited-coroutine ... """ ) From d35e4ebad03a6f542304573b12e90848d2169c77 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Thu, 14 Nov 2024 15:16:28 +0100 Subject: [PATCH 17/64] revert now unrelated fix --- src/_pytest/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 95e4557fc8e..011dc8e6e3d 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -807,7 +807,7 @@ def formatrepr(self) -> FixtureLookupErrorRepr: stack = [self.request._pyfuncitem.obj] stack.extend(map(lambda x: x.func, self.fixturestack)) msg = self.msg - if msg is not None and len(stack) > 1: + if msg is not None: # The last fixture raise an error, let's present # it at the requesting side. stack = stack[:-1] From ef096cda884d9f001916336e7e8c543badeb191f Mon Sep 17 00:00:00 2001 From: jakkdl Date: Thu, 14 Nov 2024 15:19:38 +0100 Subject: [PATCH 18/64] small wording changes --- changelog/10839.deprecation.rst | 2 +- src/_pytest/fixtures.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog/10839.deprecation.rst b/changelog/10839.deprecation.rst index 01464ca0ba2..a3e2cbf51d0 100644 --- a/changelog/10839.deprecation.rst +++ b/changelog/10839.deprecation.rst @@ -1 +1 @@ -Requesting an asynchronous fixture without a `pytest_fixture_setup` to handle it will now give a DeprecationWarning. This most commonly happens if a sync test requests an async fixture. This should have no effect on a majority of users with async tests or fixtures, but may affect non-standard hook setups or ``autouse=True``. For guidance on how to work around this warning see :ref:`sync-test-async-fixture`. +Requesting an asynchronous fixture without a `pytest_fixture_setup` hook that resolves it will now give a DeprecationWarning. This most commonly happens if a sync test requests an async fixture. This should have no effect on a majority of users with async tests or fixtures using async pytest plugins, but may affect non-standard hook setups or ``autouse=True``. For guidance on how to work around this warning see :ref:`sync-test-async-fixture`. diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 011dc8e6e3d..b5c64856e12 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -961,7 +961,7 @@ def __init__( ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None, *, _ispytest: bool = False, - # only used to emit a deprecationwarning, can be removed in pytest9 + # only used in a deprecationwarning msg, can be removed in pytest9 _autouse: bool = False, ) -> None: check_ispytest(_ispytest) From 52ee148b098a5e3f4cba6e4e5aac878c008f195c Mon Sep 17 00:00:00 2001 From: Gupta Arpit Date: Sat, 16 Nov 2024 22:26:53 +0530 Subject: [PATCH 19/64] Remove _pytest.compat.is_generator() fix #12960 --- changelog/12960.improvement.rst | 1 + src/_pytest/compat.py | 5 ----- src/_pytest/fixtures.py | 3 +-- src/_pytest/python.py | 3 +-- testing/test_compat.py | 23 ++++++++++++----------- 5 files changed, 15 insertions(+), 20 deletions(-) create mode 100644 changelog/12960.improvement.rst diff --git a/changelog/12960.improvement.rst b/changelog/12960.improvement.rst new file mode 100644 index 00000000000..11767e022db --- /dev/null +++ b/changelog/12960.improvement.rst @@ -0,0 +1 @@ +Removed _pytest.compat.is_generator() and just using inspect.isgeneratorfunction() directly \ No newline at end of file diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 614848e0dba..82aea5e635e 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -43,11 +43,6 @@ class NotSetType(enum.Enum): # fmt: on -def is_generator(func: object) -> bool: - genfunc = inspect.isgeneratorfunction(func) - return genfunc and not iscoroutinefunction(func) - - def iscoroutinefunction(func: object) -> bool: """Return True if func is a coroutine function (a function defined with async def syntax, and doesn't contain yield), or a function decorated with diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 8f275e4d622..6407ae0c48f 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -49,7 +49,6 @@ from _pytest.compat import getfuncargnames from _pytest.compat import getimfunc from _pytest.compat import getlocation -from _pytest.compat import is_generator from _pytest.compat import NOTSET from _pytest.compat import NotSetType from _pytest.compat import safe_getattr @@ -891,7 +890,7 @@ def toterminal(self, tw: TerminalWriter) -> None: def call_fixture_func( fixturefunc: _FixtureFunc[FixtureValue], request: FixtureRequest, kwargs ) -> FixtureValue: - if is_generator(fixturefunc): + if inspect.isgeneratorfunction(fixturefunc): fixturefunc = cast( Callable[..., Generator[FixtureValue, None, None]], fixturefunc ) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index d48a6c4a9fb..63d638e8a8c 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -43,7 +43,6 @@ from _pytest.compat import get_real_func from _pytest.compat import getimfunc from _pytest.compat import is_async_function -from _pytest.compat import is_generator from _pytest.compat import LEGACY_PATH from _pytest.compat import NOTSET from _pytest.compat import safe_getattr @@ -231,7 +230,7 @@ def pytest_pycollect_makeitem( lineno=lineno + 1, ) elif getattr(obj, "__test__", True): - if is_generator(obj): + if inspect.isgeneratorfunction(obj): res = Function.from_parent(collector, name=name) reason = ( f"yield tests were removed in pytest 4.0 - {name} will be ignored" diff --git a/testing/test_compat.py b/testing/test_compat.py index 2c6b0269c27..32a8b771e08 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -6,12 +6,12 @@ from functools import partial from functools import wraps import sys +import inspect from typing import TYPE_CHECKING from _pytest.compat import _PytestWrapper from _pytest.compat import assert_never from _pytest.compat import get_real_func -from _pytest.compat import is_generator from _pytest.compat import safe_getattr from _pytest.compat import safe_isclass from _pytest.outcomes import OutcomeException @@ -30,8 +30,8 @@ def zap(): def foo(): pass # pragma: no cover - assert is_generator(zap) - assert not is_generator(foo) + assert inspect.isgeneratorfunction(zap) + assert not inspect.isgeneratorfunction(foo) def test_real_func_loop_limit() -> None: @@ -99,14 +99,15 @@ def foo(x): def test_is_generator_asyncio(pytester: Pytester) -> None: pytester.makepyfile( """ - from _pytest.compat import is_generator import asyncio + import inspect + @asyncio.coroutine def baz(): yield from [1,2,3] def test_is_generator_asyncio(): - assert not is_generator(baz) + assert not inspect.isgeneratorfunction(baz) """ ) # avoid importing asyncio into pytest's own process, @@ -118,7 +119,7 @@ def test_is_generator_asyncio(): def test_is_generator_async_syntax(pytester: Pytester) -> None: pytester.makepyfile( """ - from _pytest.compat import is_generator + import inspect def test_is_generator_py35(): async def foo(): await foo() @@ -126,8 +127,8 @@ async def foo(): async def bar(): pass - assert not is_generator(foo) - assert not is_generator(bar) + assert not inspect.isgeneratorfunction(foo) + assert not inspect.isgeneratorfunction(bar) """ ) result = pytester.runpytest() @@ -137,7 +138,7 @@ async def bar(): def test_is_generator_async_gen_syntax(pytester: Pytester) -> None: pytester.makepyfile( """ - from _pytest.compat import is_generator + import inspect def test_is_generator(): async def foo(): yield @@ -146,8 +147,8 @@ async def foo(): async def bar(): yield - assert not is_generator(foo) - assert not is_generator(bar) + assert not inspect.isgeneratorfunction(foo) + assert not inspect.isgeneratorfunction(bar) """ ) result = pytester.runpytest() From 16cab96de4d1aed54cc4aefc120f7747942b6587 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 16 Nov 2024 16:57:24 +0000 Subject: [PATCH 20/64] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- changelog/12960.improvement.rst | 2 +- testing/test_compat.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog/12960.improvement.rst b/changelog/12960.improvement.rst index 11767e022db..ac9c9e431b1 100644 --- a/changelog/12960.improvement.rst +++ b/changelog/12960.improvement.rst @@ -1 +1 @@ -Removed _pytest.compat.is_generator() and just using inspect.isgeneratorfunction() directly \ No newline at end of file +Removed _pytest.compat.is_generator() and just using inspect.isgeneratorfunction() directly diff --git a/testing/test_compat.py b/testing/test_compat.py index 32a8b771e08..17f343ab5d2 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -5,8 +5,8 @@ from functools import cached_property from functools import partial from functools import wraps -import sys import inspect +import sys from typing import TYPE_CHECKING from _pytest.compat import _PytestWrapper From 71a35d4a3c8bf626cb73be0cb900ede18b1b123d Mon Sep 17 00:00:00 2001 From: Stefaan Lippens Date: Sat, 16 Nov 2024 19:57:06 +0100 Subject: [PATCH 21/64] Issue #12966 Clarify filterwarnings docs on precedence when using multiple marks (#12967) --- changelog/12966.doc.rst | 1 + doc/en/how-to/capture-warnings.rst | 33 +++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 changelog/12966.doc.rst diff --git a/changelog/12966.doc.rst b/changelog/12966.doc.rst new file mode 100644 index 00000000000..8a440c2ec0f --- /dev/null +++ b/changelog/12966.doc.rst @@ -0,0 +1 @@ +Clarify :ref:`filterwarnings` docs on filter precedence/order when using multiple :ref:`@pytest.mark.filterwarnings ` marks. diff --git a/doc/en/how-to/capture-warnings.rst b/doc/en/how-to/capture-warnings.rst index 44ed87508a3..65a43cec6e8 100644 --- a/doc/en/how-to/capture-warnings.rst +++ b/doc/en/how-to/capture-warnings.rst @@ -128,7 +128,7 @@ is performed. -You can use the ``@pytest.mark.filterwarnings`` to add warning filters to specific test items, +You can use the :ref:`@pytest.mark.filterwarnings ` mark to add warning filters to specific test items, allowing you to have finer control of which warnings should be captured at test, class or even module level: @@ -147,10 +147,30 @@ even module level: assert api_v1() == 1 +You can specify multiple filters with separate decorators: + +.. code-block:: python + + # Ignore "api v1" warnings, but fail on all other warnings + @pytest.mark.filterwarnings("ignore:api v1") + @pytest.mark.filterwarnings("error") + def test_one(): + assert api_v1() == 1 + +.. important:: + + Regarding decorator order and filter precedence: + it's important to remember that decorators are evaluated in reverse order, + so you have to list the warning filters in the reverse order + compared to traditional :py:func:`warnings.filterwarnings` and :option:`-W option ` usage. + This means in practice that filters from earlier :ref:`@pytest.mark.filterwarnings ` decorators + take precedence over filters from later decorators, as illustrated in the example above. + + Filters applied using a mark take precedence over filters passed on the command line or configured -by the ``filterwarnings`` ini option. +by the :confval:`filterwarnings` ini option. -You may apply a filter to all tests of a class by using the ``filterwarnings`` mark as a class +You may apply a filter to all tests of a class by using the :ref:`filterwarnings ` mark as a class decorator or to all tests in a module by setting the :globalvar:`pytestmark` variable: .. code-block:: python @@ -159,6 +179,13 @@ decorator or to all tests in a module by setting the :globalvar:`pytestmark` var pytestmark = pytest.mark.filterwarnings("error") +.. note:: + + If you want to apply multiple filters + (by assigning a list of :ref:`filterwarnings ` mark to :globalvar:`pytestmark`), + you must use the traditional :py:func:`warnings.filterwarnings` ordering approach (later filters take precedence), + which is the reverse of the decorator approach mentioned above. + *Credits go to Florian Schulze for the reference implementation in the* `pytest-warnings`_ *plugin.* From a44da2d1282baa8c8687cb2325d81693eee6c777 Mon Sep 17 00:00:00 2001 From: Gupta Arpit Date: Sun, 17 Nov 2024 01:33:53 +0530 Subject: [PATCH 22/64] Added suggested changes --- src/_pytest/python.py | 6 ++---- testing/test_compat.py | 39 --------------------------------------- 2 files changed, 2 insertions(+), 43 deletions(-) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 63d638e8a8c..bae770e2062 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -231,15 +231,13 @@ def pytest_pycollect_makeitem( ) elif getattr(obj, "__test__", True): if inspect.isgeneratorfunction(obj): - res = Function.from_parent(collector, name=name) reason = ( f"yield tests were removed in pytest 4.0 - {name} will be ignored" ) - res.add_marker(MARK_GEN.xfail(run=False, reason=reason)) - res.warn(PytestCollectionWarning(reason)) - return res + raise RuntimeError(reason) # Raise a hard error instead of xfail else: return list(collector._genfunctions(name, obj)) + return None return None diff --git a/testing/test_compat.py b/testing/test_compat.py index 17f343ab5d2..65c4b10ff7f 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -116,45 +116,6 @@ def test_is_generator_asyncio(): result.stdout.fnmatch_lines(["*1 passed*"]) -def test_is_generator_async_syntax(pytester: Pytester) -> None: - pytester.makepyfile( - """ - import inspect - def test_is_generator_py35(): - async def foo(): - await foo() - - async def bar(): - pass - - assert not inspect.isgeneratorfunction(foo) - assert not inspect.isgeneratorfunction(bar) - """ - ) - result = pytester.runpytest() - result.stdout.fnmatch_lines(["*1 passed*"]) - - -def test_is_generator_async_gen_syntax(pytester: Pytester) -> None: - pytester.makepyfile( - """ - import inspect - def test_is_generator(): - async def foo(): - yield - await foo() - - async def bar(): - yield - - assert not inspect.isgeneratorfunction(foo) - assert not inspect.isgeneratorfunction(bar) - """ - ) - result = pytester.runpytest() - result.stdout.fnmatch_lines(["*1 passed*"]) - - class ErrorsHelper: @property def raise_baseexception(self): From 526529a85fbee376de740c76a1397ac43f0a6ab2 Mon Sep 17 00:00:00 2001 From: Gupta Arpit Date: Sat, 16 Nov 2024 22:26:53 +0530 Subject: [PATCH 23/64] Remove _pytest.compat.is_generator() fix #12960 --- changelog/12960.improvement.rst | 1 + src/_pytest/compat.py | 5 ----- src/_pytest/fixtures.py | 3 +-- src/_pytest/python.py | 3 +-- testing/test_compat.py | 23 ++++++++++++----------- 5 files changed, 15 insertions(+), 20 deletions(-) create mode 100644 changelog/12960.improvement.rst diff --git a/changelog/12960.improvement.rst b/changelog/12960.improvement.rst new file mode 100644 index 00000000000..11767e022db --- /dev/null +++ b/changelog/12960.improvement.rst @@ -0,0 +1 @@ +Removed _pytest.compat.is_generator() and just using inspect.isgeneratorfunction() directly \ No newline at end of file diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 614848e0dba..82aea5e635e 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -43,11 +43,6 @@ class NotSetType(enum.Enum): # fmt: on -def is_generator(func: object) -> bool: - genfunc = inspect.isgeneratorfunction(func) - return genfunc and not iscoroutinefunction(func) - - def iscoroutinefunction(func: object) -> bool: """Return True if func is a coroutine function (a function defined with async def syntax, and doesn't contain yield), or a function decorated with diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 8f275e4d622..6407ae0c48f 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -49,7 +49,6 @@ from _pytest.compat import getfuncargnames from _pytest.compat import getimfunc from _pytest.compat import getlocation -from _pytest.compat import is_generator from _pytest.compat import NOTSET from _pytest.compat import NotSetType from _pytest.compat import safe_getattr @@ -891,7 +890,7 @@ def toterminal(self, tw: TerminalWriter) -> None: def call_fixture_func( fixturefunc: _FixtureFunc[FixtureValue], request: FixtureRequest, kwargs ) -> FixtureValue: - if is_generator(fixturefunc): + if inspect.isgeneratorfunction(fixturefunc): fixturefunc = cast( Callable[..., Generator[FixtureValue, None, None]], fixturefunc ) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index d48a6c4a9fb..63d638e8a8c 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -43,7 +43,6 @@ from _pytest.compat import get_real_func from _pytest.compat import getimfunc from _pytest.compat import is_async_function -from _pytest.compat import is_generator from _pytest.compat import LEGACY_PATH from _pytest.compat import NOTSET from _pytest.compat import safe_getattr @@ -231,7 +230,7 @@ def pytest_pycollect_makeitem( lineno=lineno + 1, ) elif getattr(obj, "__test__", True): - if is_generator(obj): + if inspect.isgeneratorfunction(obj): res = Function.from_parent(collector, name=name) reason = ( f"yield tests were removed in pytest 4.0 - {name} will be ignored" diff --git a/testing/test_compat.py b/testing/test_compat.py index 2c6b0269c27..32a8b771e08 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -6,12 +6,12 @@ from functools import partial from functools import wraps import sys +import inspect from typing import TYPE_CHECKING from _pytest.compat import _PytestWrapper from _pytest.compat import assert_never from _pytest.compat import get_real_func -from _pytest.compat import is_generator from _pytest.compat import safe_getattr from _pytest.compat import safe_isclass from _pytest.outcomes import OutcomeException @@ -30,8 +30,8 @@ def zap(): def foo(): pass # pragma: no cover - assert is_generator(zap) - assert not is_generator(foo) + assert inspect.isgeneratorfunction(zap) + assert not inspect.isgeneratorfunction(foo) def test_real_func_loop_limit() -> None: @@ -99,14 +99,15 @@ def foo(x): def test_is_generator_asyncio(pytester: Pytester) -> None: pytester.makepyfile( """ - from _pytest.compat import is_generator import asyncio + import inspect + @asyncio.coroutine def baz(): yield from [1,2,3] def test_is_generator_asyncio(): - assert not is_generator(baz) + assert not inspect.isgeneratorfunction(baz) """ ) # avoid importing asyncio into pytest's own process, @@ -118,7 +119,7 @@ def test_is_generator_asyncio(): def test_is_generator_async_syntax(pytester: Pytester) -> None: pytester.makepyfile( """ - from _pytest.compat import is_generator + import inspect def test_is_generator_py35(): async def foo(): await foo() @@ -126,8 +127,8 @@ async def foo(): async def bar(): pass - assert not is_generator(foo) - assert not is_generator(bar) + assert not inspect.isgeneratorfunction(foo) + assert not inspect.isgeneratorfunction(bar) """ ) result = pytester.runpytest() @@ -137,7 +138,7 @@ async def bar(): def test_is_generator_async_gen_syntax(pytester: Pytester) -> None: pytester.makepyfile( """ - from _pytest.compat import is_generator + import inspect def test_is_generator(): async def foo(): yield @@ -146,8 +147,8 @@ async def foo(): async def bar(): yield - assert not is_generator(foo) - assert not is_generator(bar) + assert not inspect.isgeneratorfunction(foo) + assert not inspect.isgeneratorfunction(bar) """ ) result = pytester.runpytest() From 42cd2965e59b110419e2f6ac91e35bcdfdb3e6a8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 16 Nov 2024 16:57:24 +0000 Subject: [PATCH 24/64] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- changelog/12960.improvement.rst | 2 +- testing/test_compat.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog/12960.improvement.rst b/changelog/12960.improvement.rst index 11767e022db..ac9c9e431b1 100644 --- a/changelog/12960.improvement.rst +++ b/changelog/12960.improvement.rst @@ -1 +1 @@ -Removed _pytest.compat.is_generator() and just using inspect.isgeneratorfunction() directly \ No newline at end of file +Removed _pytest.compat.is_generator() and just using inspect.isgeneratorfunction() directly diff --git a/testing/test_compat.py b/testing/test_compat.py index 32a8b771e08..17f343ab5d2 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -5,8 +5,8 @@ from functools import cached_property from functools import partial from functools import wraps -import sys import inspect +import sys from typing import TYPE_CHECKING from _pytest.compat import _PytestWrapper From 38a241671ccd56e6c6ff131f8e85bb5ef82fc701 Mon Sep 17 00:00:00 2001 From: Gupta Arpit Date: Sun, 17 Nov 2024 01:33:53 +0530 Subject: [PATCH 25/64] Added suggested changes --- src/_pytest/python.py | 6 ++---- testing/test_compat.py | 39 --------------------------------------- 2 files changed, 2 insertions(+), 43 deletions(-) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 63d638e8a8c..bae770e2062 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -231,15 +231,13 @@ def pytest_pycollect_makeitem( ) elif getattr(obj, "__test__", True): if inspect.isgeneratorfunction(obj): - res = Function.from_parent(collector, name=name) reason = ( f"yield tests were removed in pytest 4.0 - {name} will be ignored" ) - res.add_marker(MARK_GEN.xfail(run=False, reason=reason)) - res.warn(PytestCollectionWarning(reason)) - return res + raise RuntimeError(reason) # Raise a hard error instead of xfail else: return list(collector._genfunctions(name, obj)) + return None return None diff --git a/testing/test_compat.py b/testing/test_compat.py index 17f343ab5d2..65c4b10ff7f 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -116,45 +116,6 @@ def test_is_generator_asyncio(): result.stdout.fnmatch_lines(["*1 passed*"]) -def test_is_generator_async_syntax(pytester: Pytester) -> None: - pytester.makepyfile( - """ - import inspect - def test_is_generator_py35(): - async def foo(): - await foo() - - async def bar(): - pass - - assert not inspect.isgeneratorfunction(foo) - assert not inspect.isgeneratorfunction(bar) - """ - ) - result = pytester.runpytest() - result.stdout.fnmatch_lines(["*1 passed*"]) - - -def test_is_generator_async_gen_syntax(pytester: Pytester) -> None: - pytester.makepyfile( - """ - import inspect - def test_is_generator(): - async def foo(): - yield - await foo() - - async def bar(): - yield - - assert not inspect.isgeneratorfunction(foo) - assert not inspect.isgeneratorfunction(bar) - """ - ) - result = pytester.runpytest() - result.stdout.fnmatch_lines(["*1 passed*"]) - - class ErrorsHelper: @property def raise_baseexception(self): From ecc26e04f41d9c60f94cc80d0b2f0c6cd77117dd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 16 Nov 2024 20:04:32 +0000 Subject: [PATCH 26/64] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/python.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index bae770e2062..23b65765a53 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -56,7 +56,6 @@ from _pytest.fixtures import FuncFixtureInfo from _pytest.fixtures import get_scope_node from _pytest.main import Session -from _pytest.mark import MARK_GEN from _pytest.mark import ParameterSet from _pytest.mark.structures import get_unpacked_marks from _pytest.mark.structures import Mark From 3ab4a77606652a4aa11f2ffba8dedf84b3474d99 Mon Sep 17 00:00:00 2001 From: Arpit Gupta <113178590+arpitgupta-it@users.noreply.github.com> Date: Sun, 17 Nov 2024 01:50:30 +0530 Subject: [PATCH 27/64] Removed stdlib tests as per review --- testing/test_compat.py | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/testing/test_compat.py b/testing/test_compat.py index 65c4b10ff7f..07c28cebdd1 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -23,17 +23,6 @@ from typing_extensions import Literal -def test_is_generator() -> None: - def zap(): - yield # pragma: no cover - - def foo(): - pass # pragma: no cover - - assert inspect.isgeneratorfunction(zap) - assert not inspect.isgeneratorfunction(foo) - - def test_real_func_loop_limit() -> None: class Evil: def __init__(self): @@ -95,27 +84,6 @@ def foo(x): assert get_real_func(partial(foo)) is foo -@pytest.mark.skipif(sys.version_info >= (3, 11), reason="coroutine removed") -def test_is_generator_asyncio(pytester: Pytester) -> None: - pytester.makepyfile( - """ - import asyncio - import inspect - - @asyncio.coroutine - def baz(): - yield from [1,2,3] - - def test_is_generator_asyncio(): - assert not inspect.isgeneratorfunction(baz) - """ - ) - # avoid importing asyncio into pytest's own process, - # which in turn imports logging (#8) - result = pytester.runpytest_subprocess() - result.stdout.fnmatch_lines(["*1 passed*"]) - - class ErrorsHelper: @property def raise_baseexception(self): From 362a2179305139b3c877f0c2b2ed004a2f8ecf09 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 16 Nov 2024 20:20:49 +0000 Subject: [PATCH 28/64] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_compat.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/testing/test_compat.py b/testing/test_compat.py index 07c28cebdd1..86868858956 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -5,8 +5,6 @@ from functools import cached_property from functools import partial from functools import wraps -import inspect -import sys from typing import TYPE_CHECKING from _pytest.compat import _PytestWrapper @@ -15,7 +13,6 @@ from _pytest.compat import safe_getattr from _pytest.compat import safe_isclass from _pytest.outcomes import OutcomeException -from _pytest.pytester import Pytester import pytest From 8691ff1f368620c112983a5ef7c5ad82b46d9d34 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 17 Nov 2024 03:25:38 +0000 Subject: [PATCH 29/64] [automated] Update plugin list (#12970) Co-authored-by: pytest bot --- doc/en/reference/plugin_list.rst | 166 +++++++++++++++++++------------ 1 file changed, 103 insertions(+), 63 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index ecd96b89967..ee0981e3694 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -27,14 +27,14 @@ please refer to `the update script =8.3 + :pypi:`databricks-labs-pytester` Python Testing for Databricks Nov 15, 2024 4 - Beta pytest>=8.3 :pypi:`logassert` Simple but powerful assertion and verification of logged lines. Nov 08, 2024 5 - Production/Stable N/A :pypi:`logot` Test whether your code is logging correctly 🪵 Mar 23, 2024 5 - Production/Stable pytest<9,>=7; extra == "pytest" :pypi:`nuts` Network Unit Testing System Jul 19, 2024 N/A pytest<8,>=7 @@ -148,7 +148,7 @@ This list contains 1540 plugins. :pypi:`pytest-base-url` pytest plugin for URL based testing Jan 31, 2024 5 - Production/Stable pytest>=7.0.0 :pypi:`pytest-batch-regression` A pytest plugin to repeat the entire test suite in batches. May 08, 2024 N/A pytest>=6.0.0 :pypi:`pytest-bazel` A pytest runner with bazel support Sep 27, 2024 4 - Beta pytest - :pypi:`pytest-bdd` BDD for pytest Oct 15, 2024 6 - Mature pytest>=6.2.0 + :pypi:`pytest-bdd` BDD for pytest Nov 14, 2024 6 - Mature pytest>=7.0.0 :pypi:`pytest-bdd-html` pytest plugin to display BDD info in HTML test report Nov 22, 2022 3 - Alpha pytest (!=6.0.0,>=5.0) :pypi:`pytest-bdd-ng` BDD for pytest Oct 07, 2024 4 - Beta pytest>=5.0 :pypi:`pytest-bdd-report` A pytest-bdd plugin for generating useful and informative BDD test reports Aug 26, 2024 N/A pytest>=7.1.3 @@ -157,7 +157,7 @@ This list contains 1540 plugins. :pypi:`pytest-bdd-wrappers` Feb 11, 2020 2 - Pre-Alpha N/A :pypi:`pytest-beakerlib` A pytest plugin that reports test results to the BeakerLib framework Mar 17, 2017 5 - Production/Stable pytest :pypi:`pytest-beartype` Pytest plugin to run your tests with beartype checking enabled. Oct 31, 2024 N/A pytest - :pypi:`pytest-bec-e2e` BEC pytest plugin for end-to-end tests Nov 05, 2024 3 - Alpha pytest + :pypi:`pytest-bec-e2e` BEC pytest plugin for end-to-end tests Nov 11, 2024 3 - Alpha pytest :pypi:`pytest-beds` Fixtures for testing Google Appengine (GAE) apps Jun 07, 2016 4 - Beta N/A :pypi:`pytest-beeprint` use icdiff for better error messages in pytest assertions Jul 04, 2023 4 - Beta N/A :pypi:`pytest-bench` Benchmark utility that plugs into pytest. Jul 21, 2014 3 - Alpha N/A @@ -243,6 +243,7 @@ This list contains 1540 plugins. :pypi:`pytest-circleci-parallelized-rjp` Parallelize pytest across CircleCI workers. Jun 21, 2022 N/A pytest :pypi:`pytest-ckan` Backport of CKAN 2.9 pytest plugin and fixtures to CAKN 2.8 Apr 28, 2020 4 - Beta pytest :pypi:`pytest-clarity` A plugin providing an alternative, colourful diff output for failing assertions. Jun 11, 2021 N/A N/A + :pypi:`pytest-class-fixtures` Class as PyTest fixtures (and BDD steps) Nov 15, 2024 N/A pytest<9.0.0,>=8.3.3 :pypi:`pytest-cldf` Easy quality control for CLDF datasets using pytest Nov 07, 2022 N/A pytest (>=3.6) :pypi:`pytest-cleanslate` Collects and executes pytest tests separately Sep 04, 2024 N/A pytest :pypi:`pytest_cleanup` Automated, comprehensive and well-organised pytest test cases. Jan 28, 2020 N/A N/A @@ -352,11 +353,12 @@ This list contains 1540 plugins. :pypi:`pytest-deadfixtures` A simple plugin to list unused fixtures in pytest Jul 23, 2020 5 - Production/Stable N/A :pypi:`pytest-deduplicate` Identifies duplicate unit tests Aug 12, 2023 4 - Beta pytest :pypi:`pytest-deepcov` deepcov Mar 30, 2021 N/A N/A - :pypi:`pytest-defer` Aug 24, 2021 N/A N/A + :pypi:`pytest_defer` A 'defer' fixture for pytest Nov 13, 2024 N/A pytest>=8.3 :pypi:`pytest-demo-plugin` pytest示例插件 May 15, 2021 N/A N/A :pypi:`pytest-dependency` Manage dependencies of tests Dec 31, 2023 4 - Beta N/A :pypi:`pytest-depends` Tests that depend on other tests Apr 05, 2020 5 - Production/Stable pytest (>=3) :pypi:`pytest-deprecate` Mark tests as testing a deprecated feature with a warning note. Jul 01, 2019 N/A N/A + :pypi:`pytest-deprecator` A simple plugin to use with pytest Nov 14, 2024 4 - Beta pytest>=6.2.0 :pypi:`pytest-describe` Describe-style plugin for pytest Feb 10, 2024 5 - Production/Stable pytest <9,>=4.6 :pypi:`pytest-describe-it` plugin for rich text descriptions Jul 19, 2019 4 - Beta pytest :pypi:`pytest-deselect-if` A plugin to deselect pytests tests rather than using skipif Mar 24, 2024 4 - Beta pytest>=6.2.0 @@ -465,14 +467,15 @@ This list contains 1540 plugins. :pypi:`pytest-eliot` An eliot plugin for pytest. Aug 31, 2022 1 - Planning pytest (>=5.4.0) :pypi:`pytest-elk-reporter` A simple plugin to use with pytest Jul 25, 2024 4 - Beta pytest>=3.5.0 :pypi:`pytest-email` Send execution result email Jul 08, 2020 N/A pytest - :pypi:`pytest-embedded` A pytest plugin that designed for embedded testing. Oct 29, 2024 5 - Production/Stable pytest>=7.0 - :pypi:`pytest-embedded-arduino` Make pytest-embedded plugin work with Arduino. Oct 29, 2024 5 - Production/Stable N/A - :pypi:`pytest-embedded-idf` Make pytest-embedded plugin work with ESP-IDF. Oct 29, 2024 5 - Production/Stable N/A - :pypi:`pytest-embedded-jtag` Make pytest-embedded plugin work with JTAG. Oct 29, 2024 5 - Production/Stable N/A - :pypi:`pytest-embedded-qemu` Make pytest-embedded plugin work with QEMU. Oct 29, 2024 5 - Production/Stable N/A - :pypi:`pytest-embedded-serial` Make pytest-embedded plugin work with Serial. Oct 29, 2024 5 - Production/Stable N/A - :pypi:`pytest-embedded-serial-esp` Make pytest-embedded plugin work with Espressif target boards. Oct 29, 2024 5 - Production/Stable N/A - :pypi:`pytest-embedded-wokwi` Make pytest-embedded plugin work with the Wokwi CLI. Oct 29, 2024 5 - Production/Stable N/A + :pypi:`pytest-embedded` A pytest plugin that designed for embedded testing. Nov 14, 2024 5 - Production/Stable pytest>=7.0 + :pypi:`pytest-embedded-arduino` Make pytest-embedded plugin work with Arduino. Nov 14, 2024 5 - Production/Stable N/A + :pypi:`pytest-embedded-idf` Make pytest-embedded plugin work with ESP-IDF. Nov 14, 2024 5 - Production/Stable N/A + :pypi:`pytest-embedded-jtag` Make pytest-embedded plugin work with JTAG. Nov 14, 2024 5 - Production/Stable N/A + :pypi:`pytest-embedded-nuttx` Make pytest-embedded plugin work with NuttX. Nov 14, 2024 5 - Production/Stable N/A + :pypi:`pytest-embedded-qemu` Make pytest-embedded plugin work with QEMU. Nov 14, 2024 5 - Production/Stable N/A + :pypi:`pytest-embedded-serial` Make pytest-embedded plugin work with Serial. Nov 14, 2024 5 - Production/Stable N/A + :pypi:`pytest-embedded-serial-esp` Make pytest-embedded plugin work with Espressif target boards. Nov 14, 2024 5 - Production/Stable N/A + :pypi:`pytest-embedded-wokwi` Make pytest-embedded plugin work with the Wokwi CLI. Nov 14, 2024 5 - Production/Stable N/A :pypi:`pytest-embrace` 💝 Dataclasses-as-tests. Describe the runtime once and multiply coverage with no boilerplate. Mar 25, 2023 N/A pytest (>=7.0,<8.0) :pypi:`pytest-emoji` A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1) :pypi:`pytest-emoji-output` Pytest plugin to represent test output with emoji support Apr 09, 2023 4 - Beta pytest (==7.0.1) @@ -499,7 +502,7 @@ This list contains 1540 plugins. :pypi:`pytest-eventlet` Applies eventlet monkey-patch as a pytest plugin. Oct 04, 2021 N/A pytest ; extra == 'dev' :pypi:`pytest_evm` The testing package containing tools to test Web3-based projects Sep 23, 2024 4 - Beta pytest<9.0.0,>=8.1.1 :pypi:`pytest_exact_fixtures` Parse queries in Lucene and Elasticsearch syntaxes Feb 04, 2019 N/A N/A - :pypi:`pytest-examples` Pytest plugin for testing examples in docstrings and markdown files. Aug 13, 2024 4 - Beta pytest>=7 + :pypi:`pytest-examples` Pytest plugin for testing examples in docstrings and markdown files. Nov 15, 2024 N/A pytest>=7 :pypi:`pytest-exasol-backend` Oct 31, 2024 N/A pytest<9,>=7 :pypi:`pytest-exasol-extension` Oct 10, 2024 N/A pytest<9,>=7 :pypi:`pytest-exasol-itde` Jul 01, 2024 N/A pytest<9,>=7 @@ -601,7 +604,7 @@ This list contains 1540 plugins. :pypi:`pytest-frozen-uuids` Deterministically frozen UUID's for your tests Apr 17, 2022 N/A pytest (>=3.0) :pypi:`pytest-func-cov` Pytest plugin for measuring function coverage Apr 15, 2021 3 - Alpha pytest (>=5) :pypi:`pytest-funparam` An alternative way to parametrize test cases. Dec 02, 2021 4 - Beta pytest >=4.6.0 - :pypi:`pytest-fv` pytest extensions to support running functional-verification jobs Nov 07, 2024 N/A pytest + :pypi:`pytest-fv` pytest extensions to support running functional-verification jobs Nov 16, 2024 N/A pytest :pypi:`pytest-fxa` pytest plugin for Firefox Accounts Aug 28, 2018 5 - Production/Stable N/A :pypi:`pytest-fxa-mte` pytest plugin for Firefox Accounts Oct 02, 2024 5 - Production/Stable N/A :pypi:`pytest-fxtest` Oct 27, 2020 N/A N/A @@ -611,7 +614,7 @@ This list contains 1540 plugins. :pypi:`pytest-gc` The garbage collector plugin for py.test Feb 01, 2018 N/A N/A :pypi:`pytest-gcov` Uses gcov to measure test coverage of a C library Feb 01, 2018 3 - Alpha N/A :pypi:`pytest-gcs` GCS fixtures and fixture factories for Pytest. Mar 01, 2024 5 - Production/Stable pytest >=6.2 - :pypi:`pytest-gee` The Python plugin for your GEE based packages. Jun 30, 2024 3 - Alpha pytest + :pypi:`pytest-gee` The Python plugin for your GEE based packages. Nov 14, 2024 3 - Alpha pytest :pypi:`pytest-gevent` Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest :pypi:`pytest-gherkin` A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) :pypi:`pytest-gh-log-group` pytest plugin for gh actions Jan 11, 2022 3 - Alpha pytest @@ -660,7 +663,7 @@ This list contains 1540 plugins. :pypi:`pytest-history` Pytest plugin to keep a history of your pytest runs Jan 14, 2024 N/A pytest (>=7.4.3,<8.0.0) :pypi:`pytest-home` Home directory fixtures Jul 28, 2024 5 - Production/Stable pytest :pypi:`pytest-homeassistant` A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A - :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Nov 09, 2024 3 - Alpha pytest==8.3.3 + :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Nov 16, 2024 3 - Alpha pytest==8.3.3 :pypi:`pytest-honey` A simple plugin to use with pytest Jan 07, 2022 4 - Beta pytest (>=3.5.0) :pypi:`pytest-honors` Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A :pypi:`pytest-hot-reloading` Sep 23, 2024 N/A N/A @@ -738,7 +741,7 @@ This list contains 1540 plugins. :pypi:`pytest-isolate-mpi` pytest-isolate-mpi allows for MPI-parallel tests being executed in a segfault and MPI_Abort safe manner Sep 18, 2024 4 - Beta pytest>=5 :pypi:`pytest-isort` py.test plugin to check import ordering using isort Mar 05, 2024 5 - Production/Stable pytest (>=5.0) :pypi:`pytest-it` Pytest plugin to display test reports as a plaintext spec, inspired by Rspec: https://github.com/mattduck/pytest-it. Jan 29, 2024 4 - Beta N/A - :pypi:`pytest-item-dict` Get a hierarchical dict of session.items Oct 30, 2024 4 - Beta pytest>=8.3.0 + :pypi:`pytest-item-dict` Get a hierarchical dict of session.items Nov 14, 2024 4 - Beta pytest>=8.3.0 :pypi:`pytest-iterassert` Nicer list and iterable assertion messages for pytest May 11, 2020 3 - Alpha N/A :pypi:`pytest-iteration` Add iteration mark for tests Aug 22, 2024 N/A pytest :pypi:`pytest-iters` A contextmanager pytest fixture for handling multiple mock iters May 24, 2022 N/A N/A @@ -826,7 +829,7 @@ This list contains 1540 plugins. :pypi:`pytest-loop` pytest plugin for looping tests Oct 17, 2024 5 - Production/Stable pytest :pypi:`pytest-lsp` A pytest plugin for end-to-end testing of language servers Sep 04, 2024 3 - Alpha pytest :pypi:`pytest-manual-marker` pytest marker for marking manual tests Aug 04, 2022 3 - Alpha pytest>=7 - :pypi:`pytest-mark-count` Get a count of the number of tests marked, unmarked, and unique tests if tests have multiple markers Oct 18, 2024 4 - Beta pytest>=8.0.0 + :pypi:`pytest-mark-count` Get a count of the number of tests marked, unmarked, and unique tests if tests have multiple markers Nov 13, 2024 4 - Beta pytest>=8.0.0 :pypi:`pytest-markdoctest` A pytest plugin to doctest your markdown files Jul 22, 2022 4 - Beta pytest (>=6) :pypi:`pytest-markdown` Test your markdown docs with pytest Jan 15, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) :pypi:`pytest-markdown-docs` Run markdown code fences through pytest Oct 22, 2024 N/A pytest>=7.0.0 @@ -867,7 +870,7 @@ This list contains 1540 plugins. :pypi:`pytest-minio-mock` A pytest plugin for mocking Minio S3 interactions Aug 27, 2024 N/A pytest>=5.0.0 :pypi:`pytest-missing-fixtures` Pytest plugin that creates missing fixtures Oct 14, 2020 4 - Beta pytest (>=3.5.0) :pypi:`pytest-missing-modules` Pytest plugin to easily fake missing modules Sep 03, 2024 N/A pytest>=8.3.2 - :pypi:`pytest-mitmproxy` pytest plugin for mitmproxy tests Sep 12, 2024 N/A pytest>=7.0 + :pypi:`pytest-mitmproxy` pytest plugin for mitmproxy tests Nov 13, 2024 N/A pytest>=7.0 :pypi:`pytest-ml` Test your machine learning! May 04, 2019 4 - Beta N/A :pypi:`pytest-mocha` pytest plugin to display test execution output like a mochajs Apr 02, 2020 4 - Beta pytest (>=5.4.0) :pypi:`pytest-mock` Thin-wrapper around the mock package for easier use with pytest Mar 21, 2024 5 - Production/Stable pytest>=6.2.5 @@ -979,7 +982,7 @@ This list contains 1540 plugins. :pypi:`pytest-param` pytest plugin to test all, first, last or random params Sep 11, 2016 4 - Beta pytest (>=2.6.0) :pypi:`pytest-paramark` Configure pytest fixtures using a combination of"parametrize" and markers Jan 10, 2020 4 - Beta pytest (>=4.5.0) :pypi:`pytest-parametrization` Simpler PyTest parametrization May 22, 2022 5 - Production/Stable N/A - :pypi:`pytest-parametrize` pytest decorator for parametrizing test cases in a dict-way Oct 30, 2024 5 - Production/Stable pytest<9.0.0,>=8.3.0 + :pypi:`pytest-parametrize` pytest decorator for parametrizing test cases in a dict-way Nov 10, 2024 5 - Production/Stable pytest<9.0.0,>=8.3.0 :pypi:`pytest-parametrize-cases` A more user-friendly way to write parametrized tests. Mar 13, 2022 N/A pytest (>=6.1.2) :pypi:`pytest-parametrized` Pytest decorator for parametrizing tests with default iterables. Oct 22, 2024 5 - Production/Stable pytest :pypi:`pytest-parametrize-suite` A simple pytest extension for creating a named test suite. Jan 19, 2023 5 - Production/Stable pytest @@ -1098,7 +1101,7 @@ This list contains 1540 plugins. :pypi:`pytest-pyvenv` A package for create venv in tests Feb 27, 2024 N/A pytest ; extra == 'test' :pypi:`pytest-pyvista` Pytest-pyvista package Sep 29, 2023 4 - Beta pytest>=3.5.0 :pypi:`pytest-qanova` A pytest plugin to collect test information Sep 05, 2024 3 - Alpha pytest - :pypi:`pytest-qaseio` Pytest plugin for Qase.io integration Oct 28, 2024 5 - Production/Stable pytest<9.0.0,>=7.2.2 + :pypi:`pytest-qaseio` Pytest plugin for Qase.io integration Nov 15, 2024 5 - Production/Stable pytest<9.0.0,>=7.2.2 :pypi:`pytest-qasync` Pytest support for qasync. Jul 12, 2021 4 - Beta pytest (>=5.4.0) :pypi:`pytest-qatouch` Pytest plugin for uploading test results to your QA Touch Testrun. Feb 14, 2023 4 - Beta pytest (>=6.2.0) :pypi:`pytest-qgis` A pytest plugin for testing QGIS python plugins Jun 14, 2024 5 - Production/Stable pytest>=6.0 @@ -1137,7 +1140,7 @@ This list contains 1540 plugins. :pypi:`pytest-regex` Select pytest tests with regular expressions May 29, 2023 4 - Beta pytest (>=3.5.0) :pypi:`pytest-regex-dependency` Management of Pytest dependencies via regex patterns Jun 12, 2022 N/A pytest :pypi:`pytest-regressions` Easy to use fixtures to write regression tests. Aug 31, 2023 5 - Production/Stable pytest >=6.2.0 - :pypi:`pytest-regtest` pytest plugin for snapshot regression testing Oct 17, 2024 N/A pytest>7.2 + :pypi:`pytest-regtest` pytest plugin for snapshot regression testing Nov 12, 2024 N/A pytest>7.2 :pypi:`pytest-relative-order` a pytest plugin that sorts tests using "before" and "after" markers May 17, 2021 4 - Beta N/A :pypi:`pytest-relative-path` Handle relative path in pytest options or ini configs Aug 30, 2024 N/A pytest :pypi:`pytest-relaxed` Relaxed test discovery/organization for pytest Mar 29, 2024 5 - Production/Stable pytest>=7 @@ -1209,7 +1212,7 @@ This list contains 1540 plugins. :pypi:`pytest-ruff` pytest plugin to check ruff requirements. Jul 21, 2024 4 - Beta pytest>=5 :pypi:`pytest-run-changed` Pytest plugin that runs changed tests only Apr 02, 2021 3 - Alpha pytest :pypi:`pytest-runfailed` implement a --failed option for pytest Mar 24, 2016 N/A N/A - :pypi:`pytest-run-parallel` A simple pytest plugin to run tests concurrently Sep 30, 2024 4 - Beta pytest>=6.2.0 + :pypi:`pytest-run-parallel` A simple pytest plugin to run tests concurrently Nov 13, 2024 4 - Beta pytest>=6.2.0 :pypi:`pytest-run-subprocess` Pytest Plugin for running and testing subprocesses. Nov 12, 2022 5 - Production/Stable pytest :pypi:`pytest-runtime-types` Checks type annotations on runtime while running tests. Feb 09, 2023 N/A pytest :pypi:`pytest-runtime-xfail` Call runtime_xfail() to mark running test as xfail. Aug 26, 2021 N/A pytest>=5.0.0 @@ -1225,7 +1228,7 @@ This list contains 1540 plugins. :pypi:`pytest-sanity` Dec 07, 2020 N/A N/A :pypi:`pytest-sa-pg` May 14, 2019 N/A N/A :pypi:`pytest_sauce` pytest_sauce provides sane and helpful methods worked out in clearcode to run py.test tests with selenium/saucelabs Jul 14, 2014 3 - Alpha N/A - :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Nov 09, 2024 5 - Production/Stable N/A + :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Nov 15, 2024 5 - Production/Stable N/A :pypi:`pytest-scenario` pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A :pypi:`pytest-scenario-files` A pytest plugin that generates unit test scenarios from data files. May 19, 2024 5 - Production/Stable pytest>=7.2.0 :pypi:`pytest-schedule` Automate and customize test scheduling effortlessly on local machines. Oct 31, 2024 N/A N/A @@ -1236,7 +1239,7 @@ This list contains 1540 plugins. :pypi:`pytest-select` A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) :pypi:`pytest-selenium` pytest plugin for Selenium Feb 01, 2024 5 - Production/Stable pytest>=6.0.0 :pypi:`pytest-selenium-auto` pytest plugin to automatically capture screenshots upon selenium webdriver events Nov 07, 2023 N/A pytest >= 7.0.0 - :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Nov 09, 2024 5 - Production/Stable N/A + :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Nov 15, 2024 5 - Production/Stable N/A :pypi:`pytest-selenium-enhancer` pytest plugin for Selenium Apr 29, 2022 5 - Production/Stable N/A :pypi:`pytest-selenium-pdiff` A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A :pypi:`pytest-selfie` A pytest plugin for selfie snapshot testing. Apr 05, 2024 N/A pytest<9.0.0,>=8.0.0 @@ -1246,7 +1249,7 @@ This list contains 1540 plugins. :pypi:`pytest-server` test server exec cmd Sep 09, 2024 N/A N/A :pypi:`pytest-server-fixtures` Extensible server fixtures for py.test Oct 17, 2024 5 - Production/Stable pytest :pypi:`pytest-serverless` Automatically mocks resources from serverless.yml in pytest using moto. May 09, 2022 4 - Beta N/A - :pypi:`pytest-servers` pytest servers Sep 17, 2024 3 - Alpha pytest>=6.2 + :pypi:`pytest-servers` pytest servers Nov 14, 2024 3 - Alpha pytest>=6.2 :pypi:`pytest-service` Aug 06, 2024 5 - Production/Stable pytest>=6.0.0 :pypi:`pytest-services` Services plugin for pytest testing framework Oct 30, 2020 6 - Mature N/A :pypi:`pytest-session2file` pytest-session2file (aka: pytest-session_to_file for v0.1.0 - v0.1.2) is a py.test plugin for capturing and saving to file the stdout of py.test. Jan 26, 2021 3 - Alpha pytest @@ -1289,6 +1292,7 @@ This list contains 1540 plugins. :pypi:`pytest-smtp-test-server` pytest plugin for using \`smtp-test-server\` as a fixture Dec 03, 2023 2 - Pre-Alpha pytest (>=7.4.3,<8.0.0) :pypi:`pytest-snail` Plugin for adding a marker to slow running tests. 🐌 Nov 04, 2019 3 - Alpha pytest (>=5.0.1) :pypi:`pytest-snapci` py.test plugin for Snap-CI Nov 12, 2015 N/A N/A + :pypi:`pytest-snapmock` Snapshots for your mocks. Nov 15, 2024 N/A N/A :pypi:`pytest-snapshot` A plugin for snapshot testing with pytest. Apr 23, 2022 4 - Beta pytest (>=3.0.0) :pypi:`pytest-snapshot-with-message-generator` A plugin for snapshot testing with pytest. Jul 25, 2023 4 - Beta pytest (>=3.0.0) :pypi:`pytest-snmpserver` May 12, 2021 N/A N/A @@ -1383,7 +1387,7 @@ This list contains 1540 plugins. :pypi:`pytest-testdirectory` A py.test plugin providing temporary directories in unit tests. May 02, 2023 5 - Production/Stable pytest :pypi:`pytest-testdox` A testdox format reporter for pytest Jul 22, 2023 5 - Production/Stable pytest (>=4.6.0) :pypi:`pytest-test-grouping` A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Feb 01, 2023 5 - Production/Stable pytest (>=2.5) - :pypi:`pytest-test-groups` A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Oct 25, 2016 5 - Production/Stable N/A + :pypi:`pytest-test-groups` A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Nov 14, 2024 5 - Production/Stable pytest>=7.0.0 :pypi:`pytest-testinfra` Test infrastructures May 26, 2024 5 - Production/Stable pytest>=6 :pypi:`pytest-testinfra-jpic` Test infrastructures Sep 21, 2023 5 - Production/Stable N/A :pypi:`pytest-testinfra-winrm-transport` Test infrastructures Sep 21, 2023 5 - Production/Stable N/A @@ -1412,7 +1416,7 @@ This list contains 1540 plugins. :pypi:`pytest-test-tracer-for-pytest` A plugin that allows coll test data for use on Test Tracer Jun 28, 2024 4 - Beta pytest>=6.2.0 :pypi:`pytest-test-tracer-for-pytest-bdd` A plugin that allows coll test data for use on Test Tracer Aug 20, 2024 4 - Beta pytest>=6.2.0 :pypi:`pytest-test-utils` Feb 08, 2024 N/A pytest >=3.9 - :pypi:`pytest-tesults` Tesults plugin for pytest Sep 09, 2024 5 - Production/Stable pytest>=3.5.0 + :pypi:`pytest-tesults` Tesults plugin for pytest Nov 12, 2024 5 - Production/Stable pytest>=3.5.0 :pypi:`pytest-textual-snapshot` Snapshot testing for Textual apps Jul 22, 2024 4 - Beta pytest>=8.0.0 :pypi:`pytest-tezos` pytest-ligo Jan 16, 2020 4 - Beta N/A :pypi:`pytest-tf` Test your OpenTofu and Terraform config using a PyTest plugin May 29, 2024 N/A pytest<9.0.0,>=8.2.1 @@ -1542,6 +1546,7 @@ This list contains 1540 plugins. :pypi:`pytest-xiuyu` This is a pytest plugin Jul 25, 2023 5 - Production/Stable N/A :pypi:`pytest-xlog` Extended logging for test and decorators May 31, 2020 4 - Beta N/A :pypi:`pytest-xlsx` pytest plugin for generating test cases by xlsx(excel) Aug 07, 2024 N/A pytest~=8.2.2 + :pypi:`pytest-xml` Create simple XML results for parsing Nov 14, 2024 4 - Beta pytest>=8.0.0 :pypi:`pytest-xpara` An extended parametrizing plugin of pytest. Aug 07, 2024 3 - Alpha pytest :pypi:`pytest-xprocess` A pytest plugin for managing processes across test runs. May 19, 2024 4 - Beta pytest>=2.8 :pypi:`pytest-xray` May 30, 2019 3 - Alpha N/A @@ -1580,7 +1585,7 @@ This list contains 1540 plugins. :pypi:`databricks-labs-pytester` - *last release*: Nov 04, 2024, + *last release*: Nov 15, 2024, *status*: 4 - Beta, *requires*: pytest>=8.3 @@ -2378,9 +2383,9 @@ This list contains 1540 plugins. A pytest runner with bazel support :pypi:`pytest-bdd` - *last release*: Oct 15, 2024, + *last release*: Nov 14, 2024, *status*: 6 - Mature, - *requires*: pytest>=6.2.0 + *requires*: pytest>=7.0.0 BDD for pytest @@ -2441,7 +2446,7 @@ This list contains 1540 plugins. Pytest plugin to run your tests with beartype checking enabled. :pypi:`pytest-bec-e2e` - *last release*: Nov 05, 2024, + *last release*: Nov 11, 2024, *status*: 3 - Alpha, *requires*: pytest @@ -3042,6 +3047,13 @@ This list contains 1540 plugins. A plugin providing an alternative, colourful diff output for failing assertions. + :pypi:`pytest-class-fixtures` + *last release*: Nov 15, 2024, + *status*: N/A, + *requires*: pytest<9.0.0,>=8.3.3 + + Class as PyTest fixtures (and BDD steps) + :pypi:`pytest-cldf` *last release*: Nov 07, 2022, *status*: N/A, @@ -3805,12 +3817,12 @@ This list contains 1540 plugins. deepcov - :pypi:`pytest-defer` - *last release*: Aug 24, 2021, + :pypi:`pytest_defer` + *last release*: Nov 13, 2024, *status*: N/A, - *requires*: N/A - + *requires*: pytest>=8.3 + A 'defer' fixture for pytest :pypi:`pytest-demo-plugin` *last release*: May 15, 2021, @@ -3840,6 +3852,13 @@ This list contains 1540 plugins. Mark tests as testing a deprecated feature with a warning note. + :pypi:`pytest-deprecator` + *last release*: Nov 14, 2024, + *status*: 4 - Beta, + *requires*: pytest>=6.2.0 + + A simple plugin to use with pytest + :pypi:`pytest-describe` *last release*: Feb 10, 2024, *status*: 5 - Production/Stable, @@ -4597,56 +4616,63 @@ This list contains 1540 plugins. Send execution result email :pypi:`pytest-embedded` - *last release*: Oct 29, 2024, + *last release*: Nov 14, 2024, *status*: 5 - Production/Stable, *requires*: pytest>=7.0 A pytest plugin that designed for embedded testing. :pypi:`pytest-embedded-arduino` - *last release*: Oct 29, 2024, + *last release*: Nov 14, 2024, *status*: 5 - Production/Stable, *requires*: N/A Make pytest-embedded plugin work with Arduino. :pypi:`pytest-embedded-idf` - *last release*: Oct 29, 2024, + *last release*: Nov 14, 2024, *status*: 5 - Production/Stable, *requires*: N/A Make pytest-embedded plugin work with ESP-IDF. :pypi:`pytest-embedded-jtag` - *last release*: Oct 29, 2024, + *last release*: Nov 14, 2024, *status*: 5 - Production/Stable, *requires*: N/A Make pytest-embedded plugin work with JTAG. + :pypi:`pytest-embedded-nuttx` + *last release*: Nov 14, 2024, + *status*: 5 - Production/Stable, + *requires*: N/A + + Make pytest-embedded plugin work with NuttX. + :pypi:`pytest-embedded-qemu` - *last release*: Oct 29, 2024, + *last release*: Nov 14, 2024, *status*: 5 - Production/Stable, *requires*: N/A Make pytest-embedded plugin work with QEMU. :pypi:`pytest-embedded-serial` - *last release*: Oct 29, 2024, + *last release*: Nov 14, 2024, *status*: 5 - Production/Stable, *requires*: N/A Make pytest-embedded plugin work with Serial. :pypi:`pytest-embedded-serial-esp` - *last release*: Oct 29, 2024, + *last release*: Nov 14, 2024, *status*: 5 - Production/Stable, *requires*: N/A Make pytest-embedded plugin work with Espressif target boards. :pypi:`pytest-embedded-wokwi` - *last release*: Oct 29, 2024, + *last release*: Nov 14, 2024, *status*: 5 - Production/Stable, *requires*: N/A @@ -4835,8 +4861,8 @@ This list contains 1540 plugins. Parse queries in Lucene and Elasticsearch syntaxes :pypi:`pytest-examples` - *last release*: Aug 13, 2024, - *status*: 4 - Beta, + *last release*: Nov 15, 2024, + *status*: N/A, *requires*: pytest>=7 Pytest plugin for testing examples in docstrings and markdown files. @@ -5549,7 +5575,7 @@ This list contains 1540 plugins. An alternative way to parametrize test cases. :pypi:`pytest-fv` - *last release*: Nov 07, 2024, + *last release*: Nov 16, 2024, *status*: N/A, *requires*: pytest @@ -5619,7 +5645,7 @@ This list contains 1540 plugins. GCS fixtures and fixture factories for Pytest. :pypi:`pytest-gee` - *last release*: Jun 30, 2024, + *last release*: Nov 14, 2024, *status*: 3 - Alpha, *requires*: pytest @@ -5962,7 +5988,7 @@ This list contains 1540 plugins. A pytest plugin for use with homeassistant custom components. :pypi:`pytest-homeassistant-custom-component` - *last release*: Nov 09, 2024, + *last release*: Nov 16, 2024, *status*: 3 - Alpha, *requires*: pytest==8.3.3 @@ -6508,7 +6534,7 @@ This list contains 1540 plugins. Pytest plugin to display test reports as a plaintext spec, inspired by Rspec: https://github.com/mattduck/pytest-it. :pypi:`pytest-item-dict` - *last release*: Oct 30, 2024, + *last release*: Nov 14, 2024, *status*: 4 - Beta, *requires*: pytest>=8.3.0 @@ -7124,7 +7150,7 @@ This list contains 1540 plugins. pytest marker for marking manual tests :pypi:`pytest-mark-count` - *last release*: Oct 18, 2024, + *last release*: Nov 13, 2024, *status*: 4 - Beta, *requires*: pytest>=8.0.0 @@ -7411,7 +7437,7 @@ This list contains 1540 plugins. Pytest plugin to easily fake missing modules :pypi:`pytest-mitmproxy` - *last release*: Sep 12, 2024, + *last release*: Nov 13, 2024, *status*: N/A, *requires*: pytest>=7.0 @@ -8195,7 +8221,7 @@ This list contains 1540 plugins. Simpler PyTest parametrization :pypi:`pytest-parametrize` - *last release*: Oct 30, 2024, + *last release*: Nov 10, 2024, *status*: 5 - Production/Stable, *requires*: pytest<9.0.0,>=8.3.0 @@ -9028,7 +9054,7 @@ This list contains 1540 plugins. A pytest plugin to collect test information :pypi:`pytest-qaseio` - *last release*: Oct 28, 2024, + *last release*: Nov 15, 2024, *status*: 5 - Production/Stable, *requires*: pytest<9.0.0,>=7.2.2 @@ -9301,7 +9327,7 @@ This list contains 1540 plugins. Easy to use fixtures to write regression tests. :pypi:`pytest-regtest` - *last release*: Oct 17, 2024, + *last release*: Nov 12, 2024, *status*: N/A, *requires*: pytest>7.2 @@ -9805,7 +9831,7 @@ This list contains 1540 plugins. implement a --failed option for pytest :pypi:`pytest-run-parallel` - *last release*: Sep 30, 2024, + *last release*: Nov 13, 2024, *status*: 4 - Beta, *requires*: pytest>=6.2.0 @@ -9917,7 +9943,7 @@ This list contains 1540 plugins. pytest_sauce provides sane and helpful methods worked out in clearcode to run py.test tests with selenium/saucelabs :pypi:`pytest-sbase` - *last release*: Nov 09, 2024, + *last release*: Nov 15, 2024, *status*: 5 - Production/Stable, *requires*: N/A @@ -9994,7 +10020,7 @@ This list contains 1540 plugins. pytest plugin to automatically capture screenshots upon selenium webdriver events :pypi:`pytest-seleniumbase` - *last release*: Nov 09, 2024, + *last release*: Nov 15, 2024, *status*: 5 - Production/Stable, *requires*: N/A @@ -10064,7 +10090,7 @@ This list contains 1540 plugins. Automatically mocks resources from serverless.yml in pytest using moto. :pypi:`pytest-servers` - *last release*: Sep 17, 2024, + *last release*: Nov 14, 2024, *status*: 3 - Alpha, *requires*: pytest>=6.2 @@ -10364,6 +10390,13 @@ This list contains 1540 plugins. py.test plugin for Snap-CI + :pypi:`pytest-snapmock` + *last release*: Nov 15, 2024, + *status*: N/A, + *requires*: N/A + + Snapshots for your mocks. + :pypi:`pytest-snapshot` *last release*: Apr 23, 2022, *status*: 4 - Beta, @@ -11023,9 +11056,9 @@ This list contains 1540 plugins. A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. :pypi:`pytest-test-groups` - *last release*: Oct 25, 2016, + *last release*: Nov 14, 2024, *status*: 5 - Production/Stable, - *requires*: N/A + *requires*: pytest>=7.0.0 A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. @@ -11226,7 +11259,7 @@ This list contains 1540 plugins. :pypi:`pytest-tesults` - *last release*: Sep 09, 2024, + *last release*: Nov 12, 2024, *status*: 5 - Production/Stable, *requires*: pytest>=3.5.0 @@ -12135,6 +12168,13 @@ This list contains 1540 plugins. pytest plugin for generating test cases by xlsx(excel) + :pypi:`pytest-xml` + *last release*: Nov 14, 2024, + *status*: 4 - Beta, + *requires*: pytest>=8.0.0 + + Create simple XML results for parsing + :pypi:`pytest-xpara` *last release*: Aug 07, 2024, *status*: 3 - Alpha, From 78017f41313f5135f0afe00d9ad57937b4e41045 Mon Sep 17 00:00:00 2001 From: Gupta Arpit Date: Sun, 17 Nov 2024 13:45:51 +0530 Subject: [PATCH 30/64] Pushed review changes --- changelog/12960.improvement.rst | 2 +- src/_pytest/python.py | 8 ++------ testing/test_terminal.py | 5 ----- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/changelog/12960.improvement.rst b/changelog/12960.improvement.rst index ac9c9e431b1..e935f90704b 100644 --- a/changelog/12960.improvement.rst +++ b/changelog/12960.improvement.rst @@ -1 +1 @@ -Removed _pytest.compat.is_generator() and just using inspect.isgeneratorfunction() directly +Test functions containing a yield now cause an explicit error. They have not been run since Pytest 4.0, and were previously marked as an expected failure and deprecation warning. \ No newline at end of file diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 23b65765a53..d1e8d369e7c 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -230,12 +230,8 @@ def pytest_pycollect_makeitem( ) elif getattr(obj, "__test__", True): if inspect.isgeneratorfunction(obj): - reason = ( - f"yield tests were removed in pytest 4.0 - {name} will be ignored" - ) - raise RuntimeError(reason) # Raise a hard error instead of xfail - else: - return list(collector._genfunctions(name, obj)) + raise RuntimeError("'yield' keyword is allowed in fixtures, but not in tests ({name})") + return list(collector._genfunctions(name, obj)) return None return None diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 14c152d6123..872703900cd 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1042,10 +1042,6 @@ def test_pass(): class TestClass(object): def test_skip(self): pytest.skip("hello") - def test_gen(): - def check(x): - assert x == 1 - yield check, 0 """ ) @@ -1058,7 +1054,6 @@ def test_verbose_reporting(self, verbose_testfile, pytester: Pytester) -> None: "*test_verbose_reporting.py::test_fail *FAIL*", "*test_verbose_reporting.py::test_pass *PASS*", "*test_verbose_reporting.py::TestClass::test_skip *SKIP*", - "*test_verbose_reporting.py::test_gen *XFAIL*", ] ) assert result.ret == 1 From a074630394daa767ea2bc700f3626a2533288268 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 17 Nov 2024 08:16:14 +0000 Subject: [PATCH 31/64] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- changelog/12960.improvement.rst | 2 +- src/_pytest/python.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/changelog/12960.improvement.rst b/changelog/12960.improvement.rst index e935f90704b..43e981dc648 100644 --- a/changelog/12960.improvement.rst +++ b/changelog/12960.improvement.rst @@ -1 +1 @@ -Test functions containing a yield now cause an explicit error. They have not been run since Pytest 4.0, and were previously marked as an expected failure and deprecation warning. \ No newline at end of file +Test functions containing a yield now cause an explicit error. They have not been run since Pytest 4.0, and were previously marked as an expected failure and deprecation warning. diff --git a/src/_pytest/python.py b/src/_pytest/python.py index d1e8d369e7c..d117fa58d01 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -230,7 +230,9 @@ def pytest_pycollect_makeitem( ) elif getattr(obj, "__test__", True): if inspect.isgeneratorfunction(obj): - raise RuntimeError("'yield' keyword is allowed in fixtures, but not in tests ({name})") + raise RuntimeError( + "'yield' keyword is allowed in fixtures, but not in tests ({name})" + ) return list(collector._genfunctions(name, obj)) return None return None From 49c140d286155f91969ce9e4cf6e3b15aee621d2 Mon Sep 17 00:00:00 2001 From: John Litborn <11260241+jakkdl@users.noreply.github.com> Date: Sun, 17 Nov 2024 11:29:02 +0100 Subject: [PATCH 32/64] Apply suggestions from code review Co-authored-by: Bruno Oliveira --- src/_pytest/fixtures.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index b5c64856e12..8122d7fe2a8 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -1154,9 +1154,9 @@ def pytest_fixture_setup( f"{request.node.name!r} requested an async fixture " f"{request.fixturename!r}{auto_str}, with no plugin or hook that " "handled it. This is usually an error, as pytest does not natively " - "support it. If this is intentional, consider making the fixture " - "sync and return a coroutine/asyncgen. " - "This will turn into an error in pytest 9." + "support it. " + "This will turn into an error in pytest 9.\n" + "See: https://docs.pytest.org/en/stable/deprecations.html#sync-test-depending-on-async-fixture" ), # no stacklevel will point at users code, so we just point here stacklevel=1, From f434c278e45092e2091cd9358251c58ca9e0bce8 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Sun, 17 Nov 2024 11:44:07 +0100 Subject: [PATCH 33/64] be more explicit about the downside of async autouse fixtures --- doc/en/deprecations.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 88cf3eccbf3..59f9d83451b 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -71,7 +71,18 @@ should be changed to You can also make use of `pytest_fixture_setup` to handle the coroutine/asyncgen before pytest sees it - this is the way current async pytest plugins handle it. -If a user has an async fixture with ``autouse=True`` in their ``conftest.py``, or in a file where they also have synchronous tests, they will also get this warning. We strongly recommend against this practice, and they should restructure their testing infrastructure so the fixture is synchronous or to separate the fixture from their synchronous tests. Note that the `anyio pytest plugin `_ has some support for sync test + async fixtures currently. +If a user has an async fixture with ``autouse=True`` in their ``conftest.py``, or in a file +containing both synchronous tests and the fixture, they will receive this warning. +Unless you're using a plugin that specifically handles async fixtures +with synchronous tests, we strongly recommend against this practice. +It can lead to unpredictable behavior (with larger scopes, it may appear to "work" if an async +test is the first to request the fixture, due to value caching) and will generate +unawaited-coroutine runtime warnings (but only for non-yield fixtures). +Additionally, it creates ambiguity for other developers about whether the fixture is intended to perform +setup for synchronous tests. + +The `anyio pytest plugin `_ supports +synchronous tests with async fixtures, though certain limitations apply. .. _import-or-skip-import-error: From 708494008b3976feefeb54f141eeb0ca2e6738a3 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Sun, 17 Nov 2024 11:51:37 +0100 Subject: [PATCH 34/64] fix tests after message change --- testing/acceptance_test.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 624a313ca8d..ba1f86f02d9 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1311,10 +1311,9 @@ def test_foo(async_fixture): "*PytestRemovedIn9Warning: 'test_foo' requested an async " "fixture 'async_fixture', with no plugin or hook that handled it. " "This is usually an error, as pytest does not natively support it. " - "If this is intentional, consider making the fixture sync and return " - "a coroutine/asyncgen. " "This will turn into an error in pytest 9." ), + " See: https://docs.pytest.org/en/stable/deprecations.html#sync-test-depending-on-async-fixture", ] ) result.assert_outcomes(passed=1, warnings=1) @@ -1342,10 +1341,9 @@ def test_foo(async_fixture): "*PytestRemovedIn9Warning: 'test_foo' requested an async " "fixture 'async_fixture', with no plugin or hook that handled it. " "This is usually an error, as pytest does not natively support it. " - "If this is intentional, consider making the fixture sync and return " - "a coroutine/asyncgen. " "This will turn into an error in pytest 9." ), + " See: https://docs.pytest.org/en/stable/deprecations.html#sync-test-depending-on-async-fixture", ] ) result.assert_outcomes(passed=1, warnings=1) @@ -1376,11 +1374,11 @@ def test_foo(async_fixture): ( "*PytestRemovedIn9Warning: 'test_foo' requested an async " "fixture 'async_fixture' with autouse=True, with no plugin or hook " - "that handled it. This is usually an error, as pytest does not " - "natively support it. If this is intentional, consider making the " - "fixture sync and return a coroutine/asyncgen. " + "that handled it. " + "This is usually an error, as pytest does not natively support it. " "This will turn into an error in pytest 9." ), + " See: https://docs.pytest.org/en/stable/deprecations.html#sync-test-depending-on-async-fixture", ] ) result.assert_outcomes(passed=1, warnings=1) From 5398d7c15aeb1de7595e5ed4fad0d787feb3d0f2 Mon Sep 17 00:00:00 2001 From: Gupta Arpit Date: Sun, 17 Nov 2024 17:26:00 +0530 Subject: [PATCH 35/64] Add test for RuntimeError on 'yield' in tests --- testing/test_collection.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/testing/test_collection.py b/testing/test_collection.py index aba8f8ea48d..80c250027c6 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1878,3 +1878,21 @@ def test_respect_system_exceptions( result.stdout.fnmatch_lines([f"*{head}*"]) result.stdout.fnmatch_lines([msg]) result.stdout.no_fnmatch_line(f"*{tail}*") + + +def test_yield_disallowed_in_tests(pytester: Pytester): + """Ensure generator test functions with 'yield' raise a RuntimeError.""" + pytester.makepyfile( + """ + def test_with_yield(): + yield 1 + """ + ) + result = pytester.runpytest() + assert result.ret == 2 + result.stdout.fnmatch_lines( + ["*RuntimeError: 'yield' keyword is allowed in fixtures, but not in tests (*)*"] + ) + result.stdout.fnmatch_lines( + ["*collected 0 items*"] + ) \ No newline at end of file From bad14eec09c40370270d7fb428deb929c6e2718f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 17 Nov 2024 11:56:25 +0000 Subject: [PATCH 36/64] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_collection.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/testing/test_collection.py b/testing/test_collection.py index 80c250027c6..53463d81866 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1893,6 +1893,4 @@ def test_with_yield(): result.stdout.fnmatch_lines( ["*RuntimeError: 'yield' keyword is allowed in fixtures, but not in tests (*)*"] ) - result.stdout.fnmatch_lines( - ["*collected 0 items*"] - ) \ No newline at end of file + result.stdout.fnmatch_lines(["*collected 0 items*"]) From 1bcaf2c58d44e4426dd1ef6989206e9790710285 Mon Sep 17 00:00:00 2001 From: Gupta Arpit Date: Mon, 18 Nov 2024 00:37:00 +0530 Subject: [PATCH 37/64] Rename changelog/12960.breaking.rst --- changelog/{12960.improvement.rst => 12960.breaking.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog/{12960.improvement.rst => 12960.breaking.rst} (100%) diff --git a/changelog/12960.improvement.rst b/changelog/12960.breaking.rst similarity index 100% rename from changelog/12960.improvement.rst rename to changelog/12960.breaking.rst From 6cba4e6c1af24fd5940cc0ebdddabdbb1a0b8906 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 09:03:41 +0100 Subject: [PATCH 38/64] build(deps): Bump pytest-bdd in /testing/plugins_integration (#12971) Bumps [pytest-bdd](https://github.com/pytest-dev/pytest-bdd) from 7.3.0 to 8.0.0. - [Changelog](https://github.com/pytest-dev/pytest-bdd/blob/master/CHANGES.rst) - [Commits](https://github.com/pytest-dev/pytest-bdd/compare/7.3.0...8.0.0) --- updated-dependencies: - dependency-name: pytest-bdd dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 32992dc2589..9ca5d1cfde0 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,7 +1,7 @@ anyio[curio,trio]==4.6.2.post1 django==5.1.3 pytest-asyncio==0.24.0 -pytest-bdd==7.3.0 +pytest-bdd==8.0.0 pytest-cov==6.0.0 pytest-django==4.9.0 pytest-flakes==4.0.5 From 3de3d0d79ed2efe23a6e73d386a54b75a712fcf1 Mon Sep 17 00:00:00 2001 From: Gupta Arpit Date: Mon, 18 Nov 2024 15:54:42 +0530 Subject: [PATCH 39/64] Added final review requested changes --- changelog/12960.breaking.rst | 2 +- doc/en/deprecations.rst | 60 ++++++++++++++++++------------------ src/_pytest/python.py | 5 +-- testing/test_collection.py | 7 +++-- 4 files changed, 38 insertions(+), 36 deletions(-) diff --git a/changelog/12960.breaking.rst b/changelog/12960.breaking.rst index 43e981dc648..942348d8245 100644 --- a/changelog/12960.breaking.rst +++ b/changelog/12960.breaking.rst @@ -1 +1 @@ -Test functions containing a yield now cause an explicit error. They have not been run since Pytest 4.0, and were previously marked as an expected failure and deprecation warning. +Test functions containing a yield now cause an explicit error. They have not been run since pytest 4.0, and were previously marked as an expected failure and deprecation warning. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 59f9d83451b..689ba96b615 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -374,6 +374,36 @@ an appropriate period of deprecation has passed. Some breaking changes which could not be deprecated are also listed. +.. _yield tests deprecated: + +``yield`` tests +~~~~~~~~~~~~~~~ + +.. versionremoved:: 8.4 + +pytest no longer supports ``yield``-style tests, where a test function actually ``yield`` functions and values +that are then turned into proper test methods. Example: + +.. code-block:: python + + def check(x, y): + assert x**x == y + + + def test_squared(): + yield check, 2, 4 + yield check, 3, 9 + +This would result in two actual test functions being generated. + +This form of test function doesn't support fixtures properly, and users should switch to ``pytest.mark.parametrize``: + +.. code-block:: python + + @pytest.mark.parametrize("x, y", [(2, 4), (3, 9)]) + def test_squared(x, y): + assert x**x == y + .. _nose-deprecation: Support for tests written for nose @@ -1270,36 +1300,6 @@ with the ``name`` parameter: return cell() -.. _yield tests deprecated: - -``yield`` tests -~~~~~~~~~~~~~~~ - -.. versionremoved:: 4.0 - -pytest supported ``yield``-style tests, where a test function actually ``yield`` functions and values -that are then turned into proper test methods. Example: - -.. code-block:: python - - def check(x, y): - assert x**x == y - - - def test_squared(): - yield check, 2, 4 - yield check, 3, 9 - -This would result into two actual test functions being generated. - -This form of test function doesn't support fixtures properly, and users should switch to ``pytest.mark.parametrize``: - -.. code-block:: python - - @pytest.mark.parametrize("x, y", [(2, 4), (3, 9)]) - def test_squared(x, y): - assert x**x == y - .. _internal classes accessed through node deprecated: Internal classes accessed through ``Node`` diff --git a/src/_pytest/python.py b/src/_pytest/python.py index d117fa58d01..1456b5212d4 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -230,8 +230,9 @@ def pytest_pycollect_makeitem( ) elif getattr(obj, "__test__", True): if inspect.isgeneratorfunction(obj): - raise RuntimeError( - "'yield' keyword is allowed in fixtures, but not in tests ({name})" + fail( + f"'yield' keyword is allowed in fixtures, but not in tests ({name})", + pytrace=False, ) return list(collector._genfunctions(name, obj)) return None diff --git a/testing/test_collection.py b/testing/test_collection.py index 53463d81866..aed0c34091d 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1881,7 +1881,7 @@ def test_respect_system_exceptions( def test_yield_disallowed_in_tests(pytester: Pytester): - """Ensure generator test functions with 'yield' raise a RuntimeError.""" + """Ensure generator test functions with 'yield' fail collection (#12960).""" pytester.makepyfile( """ def test_with_yield(): @@ -1891,6 +1891,7 @@ def test_with_yield(): result = pytester.runpytest() assert result.ret == 2 result.stdout.fnmatch_lines( - ["*RuntimeError: 'yield' keyword is allowed in fixtures, but not in tests (*)*"] + ["*'yield' keyword is allowed in fixtures, but not in tests (test_with_yield)*"] ) - result.stdout.fnmatch_lines(["*collected 0 items*"]) + # Assert that no tests were collected + result.stdout.fnmatch_lines(["*collected 0 items*"]) \ No newline at end of file From 7c24a0ba1919c43e0f4ac8c48bd2126c8a4d64b2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:26:40 +0000 Subject: [PATCH 40/64] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- doc/en/deprecations.rst | 2 +- testing/test_collection.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 689ba96b615..a6b7ed4beab 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -403,7 +403,7 @@ This form of test function doesn't support fixtures properly, and users should s @pytest.mark.parametrize("x, y", [(2, 4), (3, 9)]) def test_squared(x, y): assert x**x == y - + .. _nose-deprecation: Support for tests written for nose diff --git a/testing/test_collection.py b/testing/test_collection.py index aed0c34091d..7d28610e015 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1894,4 +1894,4 @@ def test_with_yield(): ["*'yield' keyword is allowed in fixtures, but not in tests (test_with_yield)*"] ) # Assert that no tests were collected - result.stdout.fnmatch_lines(["*collected 0 items*"]) \ No newline at end of file + result.stdout.fnmatch_lines(["*collected 0 items*"]) From 1a5dbcb04d2677f46175d5b03dfd63f0debbf43e Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 18 Nov 2024 09:30:53 -0300 Subject: [PATCH 41/64] Update changelog/12960.breaking.rst --- changelog/12960.breaking.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog/12960.breaking.rst b/changelog/12960.breaking.rst index 942348d8245..3ab87e6fe23 100644 --- a/changelog/12960.breaking.rst +++ b/changelog/12960.breaking.rst @@ -1 +1,3 @@ Test functions containing a yield now cause an explicit error. They have not been run since pytest 4.0, and were previously marked as an expected failure and deprecation warning. + +See :ref:`the docs ` for more information. From 831d061a84c86c2d5ba99a7b3efde65862d8ea58 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Mon, 18 Nov 2024 13:33:56 +0100 Subject: [PATCH 42/64] Docs: be more precise about TestReport outcome in example (#12535) * Testing only for "failed" should not be reported as "or skipped" * Test for "skipped" explicitly instead Signed-off-by: Yann Dirson --- changelog/12535.doc.rst | 4 ++++ doc/en/example/simple.rst | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 changelog/12535.doc.rst diff --git a/changelog/12535.doc.rst b/changelog/12535.doc.rst new file mode 100644 index 00000000000..d43c1c822ea --- /dev/null +++ b/changelog/12535.doc.rst @@ -0,0 +1,4 @@ +`This +example` +showed ``print`` statements that do not exactly reflect what the +different branches actually do. The fix makes the example more precise. diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index a14c34c19c3..b50fbfed397 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -904,7 +904,9 @@ here is a little example implemented via a local plugin: # "function" scope report = request.node.stash[phase_report_key] if report["setup"].failed: - print("setting up a test failed or skipped", request.node.nodeid) + print("setting up a test failed", request.node.nodeid) + elif report["setup"].skipped: + print("setting up a test skipped", request.node.nodeid) elif ("call" not in report) or report["call"].failed: print("executing test failed or skipped", request.node.nodeid) From fe60ceb06d996965de140ccfc61cede4afe2a389 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 09:39:04 -0300 Subject: [PATCH 43/64] build(deps): Bump codecov/codecov-action from 4 to 5 (#12972) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4...v5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 52faca6ddef..c1ea62f6c8b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -246,7 +246,7 @@ jobs: - name: Upload coverage to Codecov if: "matrix.use_coverage" - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: fail_ci_if_error: false files: ./coverage.xml From 47996bdc3870f9eed82951a15e07f430eb27a665 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Mon, 18 Nov 2024 15:05:04 +0100 Subject: [PATCH 44/64] display single contained exception in excgroups in short test summary info --- changelog/12943.improvement.rst | 1 + src/_pytest/_code/code.py | 25 ++++++++++++++- testing/code/test_excinfo.py | 57 +++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 changelog/12943.improvement.rst diff --git a/changelog/12943.improvement.rst b/changelog/12943.improvement.rst new file mode 100644 index 00000000000..eb8ac63650a --- /dev/null +++ b/changelog/12943.improvement.rst @@ -0,0 +1 @@ +If a test fails with an exceptiongroup with a single exception, the contained exception will now be displayed in the short test summary info. diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 8fac39ea298..79adc196aea 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -1033,6 +1033,24 @@ def _truncate_recursive_traceback( def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainRepr: repr_chain: list[tuple[ReprTraceback, ReprFileLocation | None, str | None]] = [] + + def _get_single_subexc( + eg: BaseExceptionGroup[BaseException], + ) -> BaseException | None: + res: BaseException | None = None + for subexc in eg.exceptions: + if res is not None: + return None + + if isinstance(subexc, BaseExceptionGroup): + res = _get_single_subexc(subexc) + if res is None: + # there were multiple exceptions in the subgroup + return None + else: + res = subexc + return res + e: BaseException | None = excinfo.value excinfo_: ExceptionInfo[BaseException] | None = excinfo descr = None @@ -1041,6 +1059,7 @@ def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainR seen.add(id(e)) if excinfo_: + reprcrash = excinfo_._getreprcrash() # Fall back to native traceback as a temporary workaround until # full support for exception groups added to ExceptionInfo. # See https://github.com/pytest-dev/pytest/issues/9159 @@ -1054,9 +1073,13 @@ def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainR ) ) ) + if ( + reprcrash is not None + and (subexc := _get_single_subexc(e)) is not None + ): + reprcrash.message = f"[in {type(e).__name__}] {subexc!r}" else: reprtraceback = self.repr_traceback(excinfo_) - reprcrash = excinfo_._getreprcrash() else: # Fallback to native repr if the exception doesn't have a traceback: # ExceptionInfo objects require a full traceback to work. diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index fc60ae9ac99..5bb65bf30af 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -1703,6 +1703,63 @@ def test_exceptiongroup(pytester: Pytester, outer_chain, inner_chain) -> None: _exceptiongroup_common(pytester, outer_chain, inner_chain, native=False) +def test_exceptiongroup_short_summary_info(pytester: Pytester): + pytester.makepyfile( + """ + import sys + + if sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup, ExceptionGroup + + def test_base() -> None: + raise BaseExceptionGroup("NOT IN SUMMARY", [SystemExit("a" * 10)]) + + def test_nonbase() -> None: + raise ExceptionGroup("NOT IN SUMMARY", [ValueError("a" * 10)]) + + def test_nested() -> None: + raise ExceptionGroup( + "NOT DISPLAYED", [ + ExceptionGroup("NOT IN SUMMARY", [ValueError("a" * 10)]) + ] + ) + + def test_multiple() -> None: + raise ExceptionGroup( + "b" * 10, + [ + ValueError("NOT IN SUMMARY"), + TypeError("NOT IN SUMMARY"), + ] + ) + """ + ) + result = pytester.runpytest("-vv") + assert result.ret == 1 + result.stdout.fnmatch_lines( + [ + "*= short test summary info =*", + ( + "FAILED test_exceptiongroup_short_summary_info.py::test_base - " + "[in BaseExceptionGroup] SystemExit('aaaaaaaaaa')" + ), + ( + "FAILED test_exceptiongroup_short_summary_info.py::test_nonbase - " + "[in ExceptionGroup] ValueError('aaaaaaaaaa')" + ), + ( + "FAILED test_exceptiongroup_short_summary_info.py::test_nested - " + "[in ExceptionGroup] ValueError('aaaaaaaaaa')" + ), + ( + "FAILED test_exceptiongroup_short_summary_info.py::test_multiple - " + "ExceptionGroup: bbbbbbbbbb (2 sub-exceptions)" + ), + "*= 4 failed in *", + ] + ) + + @pytest.mark.parametrize("tbstyle", ("long", "short", "auto", "line", "native")) def test_all_entries_hidden(pytester: Pytester, tbstyle: str) -> None: """Regression test for #10903.""" From 70e41cb2de00d19761033537574b318c9728fb45 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 18 Nov 2024 15:30:34 -0300 Subject: [PATCH 45/64] Update doc/en/deprecations.rst --- doc/en/deprecations.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index a6b7ed4beab..18df64c9204 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -379,8 +379,14 @@ Some breaking changes which could not be deprecated are also listed. ``yield`` tests ~~~~~~~~~~~~~~~ +.. versionremoved:: 4.0 + + ``yield`` tests ``xfail``. + .. versionremoved:: 8.4 + ``yield`` tests raise a collection error. + pytest no longer supports ``yield``-style tests, where a test function actually ``yield`` functions and values that are then turned into proper test methods. Example: From 72f17d10d76e5260906c17f279c5fa6bd29b5a8d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 07:31:40 +0100 Subject: [PATCH 46/64] [pre-commit.ci] pre-commit autoupdate (#12976) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.3 → v0.7.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.3...v0.7.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 218a69e2cbc..9e7be8cf6db 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.7.3" + rev: "v0.7.4" hooks: - id: ruff args: ["--fix"] From f59a8d18c5e7071e4af06b7f0571cfa22c1b1544 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Tue, 19 Nov 2024 17:03:34 +0100 Subject: [PATCH 47/64] fix test --- testing/code/test_excinfo.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 5bb65bf30af..3418c433541 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -1736,6 +1736,7 @@ def test_multiple() -> None: ) result = pytester.runpytest("-vv") assert result.ret == 1 + backport_str = "exceptiongroup." if sys.version_info < (3, 11) else "" result.stdout.fnmatch_lines( [ "*= short test summary info =*", @@ -1753,7 +1754,7 @@ def test_multiple() -> None: ), ( "FAILED test_exceptiongroup_short_summary_info.py::test_multiple - " - "ExceptionGroup: bbbbbbbbbb (2 sub-exceptions)" + f"{backport_str}ExceptionGroup: bbbbbbbbbb (2 sub-exceptions)" ), "*= 4 failed in *", ] From 3311c180c75b99ab58d4b0b114953db2e80c1ff4 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Tue, 19 Nov 2024 17:16:29 +0100 Subject: [PATCH 48/64] move logic to ExceptionInfo --- src/_pytest/_code/code.py | 50 ++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 79adc196aea..0d635db132b 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -589,6 +589,31 @@ def exconly(self, tryshort: bool = False) -> str: representation is returned (so 'AssertionError: ' is removed from the beginning). """ + + def _get_single_subexc( + eg: BaseExceptionGroup[BaseException], + ) -> BaseException | None: + res: BaseException | None = None + for subexc in eg.exceptions: + if res is not None: + return None + + if isinstance(subexc, BaseExceptionGroup): + res = _get_single_subexc(subexc) + if res is None: + # there were multiple exceptions in the subgroup + return None + else: + res = subexc + return res + + if ( + tryshort + and isinstance(self.value, BaseExceptionGroup) + and (subexc := _get_single_subexc(self.value)) is not None + ): + return f"[in {type(self.value).__name__}] {subexc!r}" + lines = format_exception_only(self.type, self.value) text = "".join(lines) text = text.rstrip() @@ -1033,24 +1058,6 @@ def _truncate_recursive_traceback( def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainRepr: repr_chain: list[tuple[ReprTraceback, ReprFileLocation | None, str | None]] = [] - - def _get_single_subexc( - eg: BaseExceptionGroup[BaseException], - ) -> BaseException | None: - res: BaseException | None = None - for subexc in eg.exceptions: - if res is not None: - return None - - if isinstance(subexc, BaseExceptionGroup): - res = _get_single_subexc(subexc) - if res is None: - # there were multiple exceptions in the subgroup - return None - else: - res = subexc - return res - e: BaseException | None = excinfo.value excinfo_: ExceptionInfo[BaseException] | None = excinfo descr = None @@ -1059,7 +1066,6 @@ def _get_single_subexc( seen.add(id(e)) if excinfo_: - reprcrash = excinfo_._getreprcrash() # Fall back to native traceback as a temporary workaround until # full support for exception groups added to ExceptionInfo. # See https://github.com/pytest-dev/pytest/issues/9159 @@ -1073,13 +1079,9 @@ def _get_single_subexc( ) ) ) - if ( - reprcrash is not None - and (subexc := _get_single_subexc(e)) is not None - ): - reprcrash.message = f"[in {type(e).__name__}] {subexc!r}" else: reprtraceback = self.repr_traceback(excinfo_) + reprcrash = excinfo_._getreprcrash() else: # Fallback to native repr if the exception doesn't have a traceback: # ExceptionInfo objects require a full traceback to work. From 033120b6546955b8b430a1f547879dd50a32775c Mon Sep 17 00:00:00 2001 From: jakkdl Date: Tue, 19 Nov 2024 17:25:07 +0100 Subject: [PATCH 49/64] add test case for codecov --- testing/code/test_excinfo.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 3418c433541..d23382c898c 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -1732,8 +1732,23 @@ def test_multiple() -> None: TypeError("NOT IN SUMMARY"), ] ) + + def test_nested_multiple() -> None: + raise ExceptionGroup( + "b" * 10, + [ + ExceptionGroup( + "c" * 10, + [ + ValueError("NOT IN SUMMARY"), + TypeError("NOT IN SUMMARY"), + ] + ) + ] + ) """ ) + # run with -vv to not truncate summary info, default width in tests is very low result = pytester.runpytest("-vv") assert result.ret == 1 backport_str = "exceptiongroup." if sys.version_info < (3, 11) else "" @@ -1756,7 +1771,11 @@ def test_multiple() -> None: "FAILED test_exceptiongroup_short_summary_info.py::test_multiple - " f"{backport_str}ExceptionGroup: bbbbbbbbbb (2 sub-exceptions)" ), - "*= 4 failed in *", + ( + "FAILED test_exceptiongroup_short_summary_info.py::test_nested_multiple - " + f"{backport_str}ExceptionGroup: bbbbbbbbbb (1 sub-exception)" + ), + "*= 5 failed in *", ] ) From 6f61360a2b195ad15aba0e09354d662647865846 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Fri, 22 Nov 2024 15:50:12 +0100 Subject: [PATCH 50/64] fixes after review --- src/_pytest/_code/code.py | 20 ++++++-------------- testing/code/test_excinfo.py | 6 +++--- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 0d635db132b..85ed3145e66 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -593,26 +593,18 @@ def exconly(self, tryshort: bool = False) -> str: def _get_single_subexc( eg: BaseExceptionGroup[BaseException], ) -> BaseException | None: - res: BaseException | None = None - for subexc in eg.exceptions: - if res is not None: - return None - - if isinstance(subexc, BaseExceptionGroup): - res = _get_single_subexc(subexc) - if res is None: - # there were multiple exceptions in the subgroup - return None - else: - res = subexc - return res + if len(eg.exceptions) != 1: + return None + if isinstance(e := eg.exceptions[0], BaseExceptionGroup): + return _get_single_subexc(e) + return e if ( tryshort and isinstance(self.value, BaseExceptionGroup) and (subexc := _get_single_subexc(self.value)) is not None ): - return f"[in {type(self.value).__name__}] {subexc!r}" + return f"{subexc!r} [single exception in {type(self.value).__name__}]" lines = format_exception_only(self.type, self.value) text = "".join(lines) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index d23382c898c..b049e0cf188 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -1757,15 +1757,15 @@ def test_nested_multiple() -> None: "*= short test summary info =*", ( "FAILED test_exceptiongroup_short_summary_info.py::test_base - " - "[in BaseExceptionGroup] SystemExit('aaaaaaaaaa')" + "SystemExit('aaaaaaaaaa') [single exception in BaseExceptionGroup]" ), ( "FAILED test_exceptiongroup_short_summary_info.py::test_nonbase - " - "[in ExceptionGroup] ValueError('aaaaaaaaaa')" + "ValueError('aaaaaaaaaa') [single exception in ExceptionGroup]" ), ( "FAILED test_exceptiongroup_short_summary_info.py::test_nested - " - "[in ExceptionGroup] ValueError('aaaaaaaaaa')" + "ValueError('aaaaaaaaaa') [single exception in ExceptionGroup]" ), ( "FAILED test_exceptiongroup_short_summary_info.py::test_multiple - " From 4f777ff5c83273deba23832dd7e3768ba122fe39 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 24 Nov 2024 00:34:59 +0000 Subject: [PATCH 51/64] Prevent Config.add_cleanup callbacks preventing other cleanups running (#12982) Ensure all callbacks registered via Config.add_cleanup will be called, regardless if any of them raises an error. --------- Co-authored-by: Bruno Oliveira --- changelog/12981.bugfix.rst | 1 + src/_pytest/config/__init__.py | 28 +++++++++++++++++----------- testing/test_config.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 11 deletions(-) create mode 100644 changelog/12981.bugfix.rst diff --git a/changelog/12981.bugfix.rst b/changelog/12981.bugfix.rst new file mode 100644 index 00000000000..5fc8e29656f --- /dev/null +++ b/changelog/12981.bugfix.rst @@ -0,0 +1 @@ +Prevent exceptions in :func:`pytest.Config.add_cleanup` callbacks preventing further cleanups. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 35ab622de31..92cf565e85f 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -5,6 +5,7 @@ import argparse import collections.abc +import contextlib import copy import dataclasses import enum @@ -73,7 +74,6 @@ from _pytest.cacheprovider import Cache from _pytest.terminal import TerminalReporter - _PluggyPlugin = object """A type to represent plugin objects. @@ -1077,7 +1077,7 @@ def __init__( self._inicache: dict[str, Any] = {} self._override_ini: Sequence[str] = () self._opt2dest: dict[str, str] = {} - self._cleanup: list[Callable[[], None]] = [] + self._cleanup_stack = contextlib.ExitStack() self.pluginmanager.register(self, "pytestconfig") self._configured = False self.hook.pytest_addoption.call_historic( @@ -1106,8 +1106,9 @@ def inipath(self) -> pathlib.Path | None: def add_cleanup(self, func: Callable[[], None]) -> None: """Add a function to be called when the config object gets out of - use (usually coinciding with pytest_unconfigure).""" - self._cleanup.append(func) + use (usually coinciding with pytest_unconfigure). + """ + self._cleanup_stack.callback(func) def _do_configure(self) -> None: assert not self._configured @@ -1117,13 +1118,18 @@ def _do_configure(self) -> None: self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) def _ensure_unconfigure(self) -> None: - if self._configured: - self._configured = False - self.hook.pytest_unconfigure(config=self) - self.hook.pytest_configure._call_history = [] - while self._cleanup: - fin = self._cleanup.pop() - fin() + try: + if self._configured: + self._configured = False + try: + self.hook.pytest_unconfigure(config=self) + finally: + self.hook.pytest_configure._call_history = [] + finally: + try: + self._cleanup_stack.close() + finally: + self._cleanup_stack = contextlib.ExitStack() def get_terminal_writer(self) -> TerminalWriter: terminalreporter: TerminalReporter | None = self.pluginmanager.get_plugin( diff --git a/testing/test_config.py b/testing/test_config.py index 13ba66e8f9d..b2825678b46 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -983,6 +983,37 @@ def test_confcutdir_check_isdir(self, pytester: Pytester) -> None: def test_iter_rewritable_modules(self, names, expected) -> None: assert list(_iter_rewritable_modules(names)) == expected + def test_add_cleanup(self, pytester: Pytester) -> None: + config = Config.fromdictargs({}, []) + config._do_configure() + report = [] + + class MyError(BaseException): + pass + + @config.add_cleanup + def cleanup_last(): + report.append("cleanup_last") + + @config.add_cleanup + def raise_2(): + report.append("raise_2") + raise MyError("raise_2") + + @config.add_cleanup + def raise_1(): + report.append("raise_1") + raise MyError("raise_1") + + @config.add_cleanup + def cleanup_first(): + report.append("cleanup_first") + + with pytest.raises(MyError, match=r"raise_2"): + config._ensure_unconfigure() + + assert report == ["cleanup_first", "raise_1", "raise_2", "cleanup_last"] + class TestConfigFromdictargs: def test_basic_behavior(self, _sys_snapshot) -> None: From 258f76eccb4101717f03747e3384d0c97e4c8138 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 24 Nov 2024 08:09:29 +0000 Subject: [PATCH 52/64] [automated] Update plugin list (#12986) Co-authored-by: pytest bot --- doc/en/reference/plugin_list.rst | 86 +++++++++++++++++--------------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index ee0981e3694..3ac9b0b4c13 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -27,7 +27,7 @@ please refer to `the update script =8.3.3 :pypi:`pytest-cases` Separate test code from test cases in pytest. Sep 26, 2024 5 - Production/Stable N/A :pypi:`pytest-cassandra` Cassandra CCM Test Fixtures for pytest Nov 04, 2017 1 - Planning N/A :pypi:`pytest-catchlog` py.test plugin to catch log messages. This is a fork of pytest-capturelog. Jan 24, 2016 4 - Beta pytest (>=2.6) @@ -459,7 +460,7 @@ This list contains 1545 plugins. :pypi:`pytest-ebics-sandbox` A pytest plugin for testing against an EBICS sandbox server. Requires docker. Aug 15, 2022 N/A N/A :pypi:`pytest-ec2` Pytest execution on EC2 instance Oct 22, 2019 3 - Alpha N/A :pypi:`pytest-echo` pytest plugin with mechanisms for echoing environment variables, package version and generic attributes Dec 05, 2023 5 - Production/Stable pytest >=2.2 - :pypi:`pytest-edit` Edit the source code of a failed test with \`pytest --edit\`. Jun 09, 2024 N/A pytest + :pypi:`pytest-edit` Edit the source code of a failed test with \`pytest --edit\`. Nov 17, 2024 N/A pytest :pypi:`pytest-ekstazi` Pytest plugin to select test using Ekstazi algorithm Sep 10, 2022 N/A pytest :pypi:`pytest-elasticsearch` Elasticsearch fixtures and fixture factories for Pytest. Mar 15, 2024 5 - Production/Stable pytest >=7.0 :pypi:`pytest-elasticsearch-test` Elasticsearch fixtures and fixture factories for Pytest. Aug 21, 2024 5 - Production/Stable pytest>=7.0 @@ -502,11 +503,11 @@ This list contains 1545 plugins. :pypi:`pytest-eventlet` Applies eventlet monkey-patch as a pytest plugin. Oct 04, 2021 N/A pytest ; extra == 'dev' :pypi:`pytest_evm` The testing package containing tools to test Web3-based projects Sep 23, 2024 4 - Beta pytest<9.0.0,>=8.1.1 :pypi:`pytest_exact_fixtures` Parse queries in Lucene and Elasticsearch syntaxes Feb 04, 2019 N/A N/A - :pypi:`pytest-examples` Pytest plugin for testing examples in docstrings and markdown files. Nov 15, 2024 N/A pytest>=7 - :pypi:`pytest-exasol-backend` Oct 31, 2024 N/A pytest<9,>=7 + :pypi:`pytest-examples` Pytest plugin for testing examples in docstrings and markdown files. Nov 20, 2024 N/A pytest>=7 + :pypi:`pytest-exasol-backend` Nov 22, 2024 N/A pytest<9,>=7 :pypi:`pytest-exasol-extension` Oct 10, 2024 N/A pytest<9,>=7 :pypi:`pytest-exasol-itde` Jul 01, 2024 N/A pytest<9,>=7 - :pypi:`pytest-exasol-saas` Aug 06, 2024 N/A pytest<9,>=7 + :pypi:`pytest-exasol-saas` Nov 22, 2024 N/A pytest<9,>=7 :pypi:`pytest-exasol-slc` Nov 01, 2024 N/A pytest<9,>=7 :pypi:`pytest-excel` pytest plugin for generating excel reports Jun 18, 2024 5 - Production/Stable pytest>3.6 :pypi:`pytest-exceptional` Better exceptions Mar 16, 2017 4 - Beta N/A @@ -619,7 +620,7 @@ This list contains 1545 plugins. :pypi:`pytest-gherkin` A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) :pypi:`pytest-gh-log-group` pytest plugin for gh actions Jan 11, 2022 3 - Alpha pytest :pypi:`pytest-ghostinspector` For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A - :pypi:`pytest-girder` A set of pytest fixtures for testing Girder applications. Nov 07, 2024 N/A pytest>=3.6 + :pypi:`pytest-girder` A set of pytest fixtures for testing Girder applications. Nov 19, 2024 N/A pytest>=3.6 :pypi:`pytest-git` Git repository fixture for py.test Oct 17, 2024 5 - Production/Stable pytest :pypi:`pytest-gitconfig` Provide a Git config sandbox for testing Aug 11, 2024 4 - Beta pytest>=7.1.2 :pypi:`pytest-gitcov` Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A @@ -688,7 +689,7 @@ This list contains 1545 plugins. :pypi:`pytest-httpretty` A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A :pypi:`pytest_httpserver` pytest-httpserver is a httpserver for pytest Aug 11, 2024 3 - Alpha N/A :pypi:`pytest-httptesting` http_testing framework on top of pytest Aug 10, 2024 N/A pytest<9.0.0,>=8.2.0 - :pypi:`pytest-httpx` Send responses to httpx. Oct 28, 2024 5 - Production/Stable pytest==8.* + :pypi:`pytest-httpx` Send responses to httpx. Nov 18, 2024 5 - Production/Stable pytest==8.* :pypi:`pytest-httpx-blockage` Disable httpx requests during a test run Feb 16, 2023 N/A pytest (>=7.2.1) :pypi:`pytest-httpx-recorder` Recorder feature based on pytest_httpx, like recorder feature in responses. Jan 04, 2024 5 - Production/Stable pytest :pypi:`pytest-hue` Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A @@ -730,7 +731,7 @@ This list contains 1545 plugins. :pypi:`pytest-integration-mark` Automatic integration test marking and excluding plugin for pytest May 22, 2023 N/A pytest (>=5.2) :pypi:`pytest-interactive` A pytest plugin for console based interactive test selection just after the collection phase Nov 30, 2017 3 - Alpha N/A :pypi:`pytest-intercept-remote` Pytest plugin for intercepting outgoing connection requests during pytest run. May 24, 2021 4 - Beta pytest (>=4.6) - :pypi:`pytest-interface-tester` Pytest plugin for checking charm relation interface protocol compliance. Sep 25, 2024 4 - Beta pytest + :pypi:`pytest-interface-tester` Pytest plugin for checking charm relation interface protocol compliance. Nov 19, 2024 4 - Beta pytest :pypi:`pytest-invenio` Pytest fixtures for Invenio. Jun 27, 2024 5 - Production/Stable pytest<7.2.0,>=6 :pypi:`pytest-involve` Run tests covering a specific file or changeset Feb 02, 2020 4 - Beta pytest (>=3.5.0) :pypi:`pytest-iovis` A Pytest plugin to enable Jupyter Notebook testing with Papermill Nov 06, 2024 4 - Beta pytest>=7.1.0 @@ -1092,7 +1093,7 @@ This list contains 1545 plugins. :pypi:`pytest-pyreport` PyReport is a lightweight reporting plugin for Pytest that provides concise HTML report May 05, 2024 N/A pytest :pypi:`pytest-pyright` Pytest plugin for type checking code with Pyright Jan 26, 2024 4 - Beta pytest >=7.0.0 :pypi:`pytest-pyspec` A plugin that transforms the pytest output into a result similar to the RSpec. It enables the use of docstrings to display results and also enables the use of the prefixes "describe", "with" and "it". Aug 17, 2024 N/A pytest<9.0.0,>=8.3.2 - :pypi:`pytest-pystack` Plugin to run pystack after a timeout for a test suite. Jan 04, 2024 N/A pytest >=3.5.0 + :pypi:`pytest-pystack` Plugin to run pystack after a timeout for a test suite. Nov 16, 2024 N/A pytest>=3.5.0 :pypi:`pytest-pytestrail` Pytest plugin for interaction with TestRail Aug 27, 2020 4 - Beta pytest (>=3.8.0) :pypi:`pytest-pythonhashseed` Pytest plugin to set PYTHONHASHSEED env var. Feb 25, 2024 4 - Beta pytest>=3.0.0 :pypi:`pytest-pythonpath` pytest plugin for adding to the PYTHONPATH from command line or configs. Feb 10, 2022 5 - Production/Stable pytest (<7,>=2.5.2) @@ -1101,7 +1102,7 @@ This list contains 1545 plugins. :pypi:`pytest-pyvenv` A package for create venv in tests Feb 27, 2024 N/A pytest ; extra == 'test' :pypi:`pytest-pyvista` Pytest-pyvista package Sep 29, 2023 4 - Beta pytest>=3.5.0 :pypi:`pytest-qanova` A pytest plugin to collect test information Sep 05, 2024 3 - Alpha pytest - :pypi:`pytest-qaseio` Pytest plugin for Qase.io integration Nov 15, 2024 5 - Production/Stable pytest<9.0.0,>=7.2.2 + :pypi:`pytest-qaseio` Pytest plugin for Qase.io integration Nov 18, 2024 5 - Production/Stable pytest<9.0.0,>=7.2.2 :pypi:`pytest-qasync` Pytest support for qasync. Jul 12, 2021 4 - Beta pytest (>=5.4.0) :pypi:`pytest-qatouch` Pytest plugin for uploading test results to your QA Touch Testrun. Feb 14, 2023 4 - Beta pytest (>=6.2.0) :pypi:`pytest-qgis` A pytest plugin for testing QGIS python plugins Jun 14, 2024 5 - Production/Stable pytest>=6.0 @@ -1175,7 +1176,7 @@ This list contains 1545 plugins. :pypi:`pytest-rerun` Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) :pypi:`pytest-rerun-all` Rerun testsuite for a certain time or iterations Nov 16, 2023 3 - Alpha pytest (>=7.0.0) :pypi:`pytest-rerunclassfailures` pytest rerun class failures plugin Apr 24, 2024 5 - Production/Stable pytest>=7.2 - :pypi:`pytest-rerunfailures` pytest plugin to re-run tests to eliminate flaky failures Mar 13, 2024 5 - Production/Stable pytest >=7.2 + :pypi:`pytest-rerunfailures` pytest plugin to re-run tests to eliminate flaky failures Nov 20, 2024 5 - Production/Stable pytest!=8.2.2,>=7.4 :pypi:`pytest-rerunfailures-all-logs` pytest plugin to re-run tests to eliminate flaky failures Mar 07, 2022 5 - Production/Stable N/A :pypi:`pytest-reserial` Pytest fixture for recording and replaying serial port traffic. Jul 23, 2024 4 - Beta pytest :pypi:`pytest-resilient-circuits` Resilient Circuits fixtures for PyTest Jul 16, 2024 N/A pytest~=7.0 @@ -1228,9 +1229,9 @@ This list contains 1545 plugins. :pypi:`pytest-sanity` Dec 07, 2020 N/A N/A :pypi:`pytest-sa-pg` May 14, 2019 N/A N/A :pypi:`pytest_sauce` pytest_sauce provides sane and helpful methods worked out in clearcode to run py.test tests with selenium/saucelabs Jul 14, 2014 3 - Alpha N/A - :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Nov 15, 2024 5 - Production/Stable N/A + :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Nov 19, 2024 5 - Production/Stable N/A :pypi:`pytest-scenario` pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A - :pypi:`pytest-scenario-files` A pytest plugin that generates unit test scenarios from data files. May 19, 2024 5 - Production/Stable pytest>=7.2.0 + :pypi:`pytest-scenario-files` A pytest plugin that generates unit test scenarios from data files. Nov 21, 2024 5 - Production/Stable pytest>=7.0 :pypi:`pytest-schedule` Automate and customize test scheduling effortlessly on local machines. Oct 31, 2024 N/A N/A :pypi:`pytest-schema` 👍 Validate return values against a schema-like object in testing Feb 16, 2024 5 - Production/Stable pytest >=3.5.0 :pypi:`pytest-screenshot-on-failure` Saves a screenshot when a test case from a pytest execution fails Jul 21, 2023 4 - Beta N/A @@ -1239,7 +1240,7 @@ This list contains 1545 plugins. :pypi:`pytest-select` A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) :pypi:`pytest-selenium` pytest plugin for Selenium Feb 01, 2024 5 - Production/Stable pytest>=6.0.0 :pypi:`pytest-selenium-auto` pytest plugin to automatically capture screenshots upon selenium webdriver events Nov 07, 2023 N/A pytest >= 7.0.0 - :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Nov 15, 2024 5 - Production/Stable N/A + :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Nov 19, 2024 5 - Production/Stable N/A :pypi:`pytest-selenium-enhancer` pytest plugin for Selenium Apr 29, 2022 5 - Production/Stable N/A :pypi:`pytest-selenium-pdiff` A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A :pypi:`pytest-selfie` A pytest plugin for selfie snapshot testing. Apr 05, 2024 N/A pytest<9.0.0,>=8.0.0 @@ -1512,11 +1513,11 @@ This list contains 1545 plugins. :pypi:`pytest-vscode-pycharm-cls` A PyTest helper to enable start remote debugger on test start or failure or when pytest.set_trace is used. Feb 01, 2023 N/A pytest :pypi:`pytest-vtestify` A pytest plugin for visual assertion using SSIM and image comparison. Oct 10, 2024 N/A pytest :pypi:`pytest-vts` pytest plugin for automatic recording of http stubbed tests Jun 05, 2019 N/A pytest (>=2.3) - :pypi:`pytest-vulture` A pytest plugin to checks dead code with vulture Jun 01, 2023 N/A pytest (>=7.0.0) + :pypi:`pytest-vulture` A pytest plugin to checks dead code with vulture Nov 22, 2024 N/A pytest>=7.0.0 :pypi:`pytest-vw` pytest-vw makes your failing test cases succeed under CI tools scrutiny Oct 07, 2015 4 - Beta N/A :pypi:`pytest-vyper` Plugin for the vyper smart contract language. May 28, 2020 2 - Pre-Alpha N/A :pypi:`pytest-wa-e2e-plugin` Pytest plugin for testing whatsapp bots with end to end tests Feb 18, 2020 4 - Beta pytest (>=3.5.0) - :pypi:`pytest-wake` Mar 20, 2024 N/A pytest + :pypi:`pytest-wake` Nov 19, 2024 N/A pytest :pypi:`pytest-watch` Local continuous test runner with pytest and watchdog. May 20, 2018 N/A N/A :pypi:`pytest-watcher` Automatically rerun your tests on file modifications Aug 28, 2024 4 - Beta N/A :pypi:`pytest-watch-plugin` Placeholder for internal package Sep 12, 2024 N/A N/A @@ -1557,7 +1558,7 @@ This list contains 1545 plugins. :pypi:`pytest-xvfb` A pytest plugin to run Xvfb (or Xephyr/Xvnc) for tests. May 29, 2023 4 - Beta pytest (>=2.8.1) :pypi:`pytest-xvirt` A pytest plugin to virtualize test. For example to transparently running them on a remote box. Oct 01, 2024 4 - Beta pytest>=7.2.2 :pypi:`pytest-yaml` This plugin is used to load yaml output to your test using pytest framework. Oct 05, 2018 N/A pytest - :pypi:`pytest-yaml-sanmu` Pytest plugin for generating test cases with YAML. In test cases, you can use markers, fixtures, variables, and even call Python functions. Aug 16, 2024 N/A pytest~=8.2.2 + :pypi:`pytest-yaml-sanmu` Pytest plugin for generating test cases with YAML. In test cases, you can use markers, fixtures, variables, and even call Python functions. Nov 20, 2024 N/A pytest~=8.2.2 :pypi:`pytest-yamltree` Create or check file/directory trees described by YAML Mar 02, 2020 4 - Beta pytest (>=3.1.1) :pypi:`pytest-yamlwsgi` Run tests against wsgi apps defined in yaml May 11, 2010 N/A N/A :pypi:`pytest-yaml-yoyo` http/https API run by yaml Jun 19, 2023 N/A pytest (>=7.2.0) @@ -2446,7 +2447,7 @@ This list contains 1545 plugins. Pytest plugin to run your tests with beartype checking enabled. :pypi:`pytest-bec-e2e` - *last release*: Nov 11, 2024, + *last release*: Nov 18, 2024, *status*: 3 - Alpha, *requires*: pytest @@ -2823,6 +2824,13 @@ This list contains 1545 plugins. pytest plugin to capture all warnings and put them in one file of your choice + :pypi:`pytest-case` + *last release*: Nov 22, 2024, + *status*: N/A, + *requires*: pytest<9.0.0,>=8.3.3 + + A clean, modern, wrapper for pytest.mark.parametrize + :pypi:`pytest-cases` *last release*: Sep 26, 2024, *status*: 5 - Production/Stable, @@ -4560,7 +4568,7 @@ This list contains 1545 plugins. pytest plugin with mechanisms for echoing environment variables, package version and generic attributes :pypi:`pytest-edit` - *last release*: Jun 09, 2024, + *last release*: Nov 17, 2024, *status*: N/A, *requires*: pytest @@ -4861,14 +4869,14 @@ This list contains 1545 plugins. Parse queries in Lucene and Elasticsearch syntaxes :pypi:`pytest-examples` - *last release*: Nov 15, 2024, + *last release*: Nov 20, 2024, *status*: N/A, *requires*: pytest>=7 Pytest plugin for testing examples in docstrings and markdown files. :pypi:`pytest-exasol-backend` - *last release*: Oct 31, 2024, + *last release*: Nov 22, 2024, *status*: N/A, *requires*: pytest<9,>=7 @@ -4889,7 +4897,7 @@ This list contains 1545 plugins. :pypi:`pytest-exasol-saas` - *last release*: Aug 06, 2024, + *last release*: Nov 22, 2024, *status*: N/A, *requires*: pytest<9,>=7 @@ -5680,7 +5688,7 @@ This list contains 1545 plugins. For finding/executing Ghost Inspector tests :pypi:`pytest-girder` - *last release*: Nov 07, 2024, + *last release*: Nov 19, 2024, *status*: N/A, *requires*: pytest>=3.6 @@ -6163,7 +6171,7 @@ This list contains 1545 plugins. http_testing framework on top of pytest :pypi:`pytest-httpx` - *last release*: Oct 28, 2024, + *last release*: Nov 18, 2024, *status*: 5 - Production/Stable, *requires*: pytest==8.* @@ -6457,7 +6465,7 @@ This list contains 1545 plugins. Pytest plugin for intercepting outgoing connection requests during pytest run. :pypi:`pytest-interface-tester` - *last release*: Sep 25, 2024, + *last release*: Nov 19, 2024, *status*: 4 - Beta, *requires*: pytest @@ -8991,9 +8999,9 @@ This list contains 1545 plugins. A plugin that transforms the pytest output into a result similar to the RSpec. It enables the use of docstrings to display results and also enables the use of the prefixes "describe", "with" and "it". :pypi:`pytest-pystack` - *last release*: Jan 04, 2024, + *last release*: Nov 16, 2024, *status*: N/A, - *requires*: pytest >=3.5.0 + *requires*: pytest>=3.5.0 Plugin to run pystack after a timeout for a test suite. @@ -9054,7 +9062,7 @@ This list contains 1545 plugins. A pytest plugin to collect test information :pypi:`pytest-qaseio` - *last release*: Nov 15, 2024, + *last release*: Nov 18, 2024, *status*: 5 - Production/Stable, *requires*: pytest<9.0.0,>=7.2.2 @@ -9572,9 +9580,9 @@ This list contains 1545 plugins. pytest rerun class failures plugin :pypi:`pytest-rerunfailures` - *last release*: Mar 13, 2024, + *last release*: Nov 20, 2024, *status*: 5 - Production/Stable, - *requires*: pytest >=7.2 + *requires*: pytest!=8.2.2,>=7.4 pytest plugin to re-run tests to eliminate flaky failures @@ -9943,7 +9951,7 @@ This list contains 1545 plugins. pytest_sauce provides sane and helpful methods worked out in clearcode to run py.test tests with selenium/saucelabs :pypi:`pytest-sbase` - *last release*: Nov 15, 2024, + *last release*: Nov 19, 2024, *status*: 5 - Production/Stable, *requires*: N/A @@ -9957,9 +9965,9 @@ This list contains 1545 plugins. pytest plugin for test scenarios :pypi:`pytest-scenario-files` - *last release*: May 19, 2024, + *last release*: Nov 21, 2024, *status*: 5 - Production/Stable, - *requires*: pytest>=7.2.0 + *requires*: pytest>=7.0 A pytest plugin that generates unit test scenarios from data files. @@ -10020,7 +10028,7 @@ This list contains 1545 plugins. pytest plugin to automatically capture screenshots upon selenium webdriver events :pypi:`pytest-seleniumbase` - *last release*: Nov 15, 2024, + *last release*: Nov 19, 2024, *status*: 5 - Production/Stable, *requires*: N/A @@ -11931,9 +11939,9 @@ This list contains 1545 plugins. pytest plugin for automatic recording of http stubbed tests :pypi:`pytest-vulture` - *last release*: Jun 01, 2023, + *last release*: Nov 22, 2024, *status*: N/A, - *requires*: pytest (>=7.0.0) + *requires*: pytest>=7.0.0 A pytest plugin to checks dead code with vulture @@ -11959,7 +11967,7 @@ This list contains 1545 plugins. Pytest plugin for testing whatsapp bots with end to end tests :pypi:`pytest-wake` - *last release*: Mar 20, 2024, + *last release*: Nov 19, 2024, *status*: N/A, *requires*: pytest @@ -12246,7 +12254,7 @@ This list contains 1545 plugins. This plugin is used to load yaml output to your test using pytest framework. :pypi:`pytest-yaml-sanmu` - *last release*: Aug 16, 2024, + *last release*: Nov 20, 2024, *status*: N/A, *requires*: pytest~=8.2.2 From 3b230e06dbcc6a3472fc7cfecdc33e420b7cdb11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 07:02:43 +0100 Subject: [PATCH 53/64] build(deps): Bump pytest-rerunfailures in /testing/plugins_integration (#12988) Bumps [pytest-rerunfailures](https://github.com/pytest-dev/pytest-rerunfailures) from 14.0 to 15.0. - [Changelog](https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst) - [Commits](https://github.com/pytest-dev/pytest-rerunfailures/compare/14.0...15.0) --- updated-dependencies: - dependency-name: pytest-rerunfailures dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 9ca5d1cfde0..ec6b9418c93 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -7,7 +7,7 @@ pytest-django==4.9.0 pytest-flakes==4.0.5 pytest-html==4.1.1 pytest-mock==3.14.0 -pytest-rerunfailures==14.0 +pytest-rerunfailures==15.0 pytest-sugar==1.0.0 pytest-trio==0.8.0 pytest-twisted==1.14.3 From 76e044477010dcc0e31d4a736b7130e044a01a7e Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Mon, 25 Nov 2024 19:25:50 +0700 Subject: [PATCH 54/64] pytest.fail: fix ANSI escape codes for colored output (#12959) - When `ReprEntry.style == "value"` (happens when calling `pytest.fail(..., pytrace=False)`, the message should not be printed to terminal using `TerminalWriter._write_source` because then it'll try to highlight the message as source code - The message should be printed to terminal directly using `TerminalWriter.line` or `TerminalWriter.write`, I went with the later for testing purposes https://github.com/pytest-dev/pytest/pull/12959#discussion_r1842574618 Closes #12849 --- AUTHORS | 1 + changelog/12849.bugfix.rst | 1 + src/_pytest/_code/code.py | 16 +++++++++++----- testing/code/test_excinfo.py | 17 +++++++++++++++++ testing/conftest.py | 4 ++-- 5 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 changelog/12849.bugfix.rst diff --git a/AUTHORS b/AUTHORS index c38f74d9980..303d04133cb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -247,6 +247,7 @@ Kristoffer Nordström Kyle Altendorf Lawrence Mitchell Lee Kamentsky +Leonardus Chen Lev Maximov Levon Saldamli Lewis Cowles diff --git a/changelog/12849.bugfix.rst b/changelog/12849.bugfix.rst new file mode 100644 index 00000000000..fb72263aadd --- /dev/null +++ b/changelog/12849.bugfix.rst @@ -0,0 +1 @@ +ANSI escape codes for colored output now handled correctly in :func:`pytest.fail` with `pytrace=False`. diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 8fac39ea298..fec627b3a36 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -1221,6 +1221,15 @@ def _write_entry_lines(self, tw: TerminalWriter) -> None: if not self.lines: return + if self.style == "value": + # Using tw.write instead of tw.line for testing purposes due to TWMock implementation; + # lines written with TWMock.line and TWMock._write_source cannot be distinguished + # from each other, whereas lines written with TWMock.write are marked with TWMock.WRITE + for line in self.lines: + tw.write(line) + tw.write("\n") + return + # separate indents and source lines that are not failures: we want to # highlight the code but not the indentation, which may contain markers # such as "> assert 0" @@ -1236,11 +1245,8 @@ def _write_entry_lines(self, tw: TerminalWriter) -> None: failure_lines.extend(self.lines[index:]) break else: - if self.style == "value": - source_lines.append(line) - else: - indents.append(line[:indent_size]) - source_lines.append(line[indent_size:]) + indents.append(line[:indent_size]) + source_lines.append(line[indent_size:]) tw._write_source(source_lines, indents) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index fc60ae9ac99..97c207e9795 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -1194,6 +1194,23 @@ def f(): line = tw_mock.lines[-1] assert line == ":3: ValueError" + def test_toterminal_value(self, importasmod, tw_mock): + mod = importasmod( + """ + def g(x): + raise ValueError(x) + def f(): + g('some_value') + """ + ) + excinfo = pytest.raises(ValueError, mod.f) + excinfo.traceback = excinfo.traceback.filter(excinfo) + repr = excinfo.getrepr(style="value") + repr.toterminal(tw_mock) + + assert tw_mock.get_write_msg(0) == "some_value" + assert tw_mock.get_write_msg(1) == "\n" + @pytest.mark.parametrize( "reproptions", [ diff --git a/testing/conftest.py b/testing/conftest.py index 046bb77a109..69af03324d6 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -119,8 +119,8 @@ def markup(self, text, **kw): return text def get_write_msg(self, idx): - flag, msg = self.lines[idx] - assert flag == TWMock.WRITE + assert self.lines[idx][0] == TWMock.WRITE + msg = self.lines[idx][1] return msg fullwidth = 80 From 2157caf87960d904c8547c9168c94a7d535f21e0 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 25 Nov 2024 12:29:01 +0000 Subject: [PATCH 55/64] docs: Fix wrong statement about sys.modules with importlib import mode (#12985) Follow-up to #7870, see #12983. --- doc/en/explanation/goodpractices.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/explanation/goodpractices.rst b/doc/en/explanation/goodpractices.rst index 1390ba4e8fe..51c0b960aed 100644 --- a/doc/en/explanation/goodpractices.rst +++ b/doc/en/explanation/goodpractices.rst @@ -210,8 +210,8 @@ Note that this layout also works in conjunction with the ``src`` layout mentione to avoid surprises such as a test module getting imported twice. With ``--import-mode=importlib`` things are less convoluted because - pytest doesn't need to change ``sys.path`` or ``sys.modules``, making things - much less surprising. + pytest doesn't need to change ``sys.path``, making things much less + surprising. .. _which-import-mode: From a4cb74e8646dc3cfaa1c4d5b36e0d21f177332e6 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 10 Nov 2024 19:25:05 +0100 Subject: [PATCH 56/64] Upgrade doc and CI after dropping python 3.8 --- .github/workflows/prepare-release-pr.yml | 2 +- .github/workflows/test.yml | 103 +++++++++++++---------- .pre-commit-config.yaml | 4 + CONTRIBUTING.rst | 2 +- README.rst | 2 +- changelog/12874.breaking.rst | 1 + doc/en/backwards-compatibility.rst | 1 + pyproject.toml | 7 +- scripts/generate-gh-release-notes.py | 2 +- scripts/update-plugin-list.py | 4 +- src/_pytest/_code/code.py | 15 ++-- src/_pytest/_code/source.py | 4 +- src/_pytest/_io/pprint.py | 2 +- src/_pytest/_io/terminalwriter.py | 2 +- src/_pytest/assertion/__init__.py | 2 +- src/_pytest/assertion/rewrite.py | 6 +- src/_pytest/assertion/util.py | 6 +- src/_pytest/cacheprovider.py | 4 +- src/_pytest/capture.py | 6 +- src/_pytest/config/__init__.py | 10 +-- src/_pytest/config/argparsing.py | 7 +- src/_pytest/config/compat.py | 2 +- src/_pytest/config/findpaths.py | 4 +- src/_pytest/debugging.py | 2 +- src/_pytest/doctest.py | 8 +- src/_pytest/faulthandler.py | 2 +- src/_pytest/fixtures.py | 24 +++--- src/_pytest/freeze_support.py | 2 +- src/_pytest/helpconfig.py | 2 +- src/_pytest/hookspec.py | 4 +- src/_pytest/junitxml.py | 2 +- src/_pytest/logging.py | 25 +++--- src/_pytest/main.py | 9 +- src/_pytest/mark/__init__.py | 4 +- src/_pytest/mark/expression.py | 6 +- src/_pytest/mark/structures.py | 12 +-- src/_pytest/monkeypatch.py | 6 +- src/_pytest/nodes.py | 6 +- src/_pytest/outcomes.py | 3 +- src/_pytest/pathlib.py | 4 +- src/_pytest/pytester.py | 6 +- src/_pytest/pytester_assertions.py | 2 +- src/_pytest/python.py | 15 ++-- src/_pytest/python_api.py | 10 +-- src/_pytest/recwarn.py | 6 +- src/_pytest/reports.py | 8 +- src/_pytest/setuponly.py | 2 +- src/_pytest/skipping.py | 2 +- src/_pytest/terminal.py | 6 +- src/_pytest/threadexception.py | 2 +- src/_pytest/tmpdir.py | 5 +- src/_pytest/unittest.py | 10 +-- src/_pytest/unraisableexception.py | 2 +- src/_pytest/warnings.py | 2 +- testing/code/test_excinfo.py | 4 +- testing/code/test_source.py | 2 +- testing/conftest.py | 2 +- testing/io/test_terminalwriter.py | 2 +- testing/logging/test_fixture.py | 2 +- testing/python/metafunc.py | 7 +- testing/test_assertion.py | 2 +- testing/test_assertrewrite.py | 4 +- testing/test_cacheprovider.py | 4 +- testing/test_capture.py | 2 +- testing/test_config.py | 2 +- testing/test_conftest.py | 7 +- testing/test_monkeypatch.py | 2 +- testing/test_pathlib.py | 6 +- testing/test_reports.py | 2 +- tox.ini | 9 +- 70 files changed, 230 insertions(+), 226 deletions(-) create mode 100644 changelog/12874.breaking.rst diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml index 1bb23fab844..f46b5cecda5 100644 --- a/.github/workflows/prepare-release-pr.yml +++ b/.github/workflows/prepare-release-pr.yml @@ -34,7 +34,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: "3.x" - name: Install dependencies run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c1ea62f6c8b..30536706afb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -54,25 +54,24 @@ jobs: fail-fast: false matrix: name: [ - "windows-py38", - "windows-py38-pluggy", - "windows-py39", + "windows-py39-unittestextras", + "windows-py39-pluggy", + "windows-py39-xdist", "windows-py310", "windows-py311", "windows-py312", "windows-py313", - "ubuntu-py38", - "ubuntu-py38-pluggy", - "ubuntu-py38-freeze", - "ubuntu-py39", - "ubuntu-py310", + "ubuntu-py39-lsof-numpy-pexpect", + "ubuntu-py39-pluggy", + "ubuntu-py39-freeze", + "ubuntu-py39-xdist", + "ubuntu-py310-xdist", "ubuntu-py311", "ubuntu-py312", - "ubuntu-py313", - "ubuntu-pypy3", + "ubuntu-py313-pexpect", + "ubuntu-pypy3-xdist", - "macos-py38", "macos-py39", "macos-py310", "macos-py312", @@ -83,106 +82,122 @@ jobs: ] include: - - name: "windows-py38" - python: "3.8" + - name: "windows-py39-unittestextras" + python: "3.9" os: windows-latest - tox_env: "py38-unittestextras" + tox_env: "py39-unittestextras" use_coverage: true - - name: "windows-py38-pluggy" - python: "3.8" + + - name: "windows-py39-pluggy" + python: "3.9" os: windows-latest - tox_env: "py38-pluggymain-pylib-xdist" - - name: "windows-py39" + tox_env: "py39-pluggymain-pylib-xdist" + + - name: "windows-py39-xdist" python: "3.9" os: windows-latest tox_env: "py39-xdist" + - name: "windows-py310" python: "3.10" os: windows-latest tox_env: "py310-xdist" + - name: "windows-py311" python: "3.11" os: windows-latest tox_env: "py311" + - name: "windows-py312" python: "3.12" os: windows-latest tox_env: "py312" + - name: "windows-py313" - python: "3.13-dev" + python: "3.13" os: windows-latest tox_env: "py313" - - name: "ubuntu-py38" - python: "3.8" + + - name: "ubuntu-py39-lsof-numpy-pexpect" + python: "3.9" os: ubuntu-latest - tox_env: "py38-lsof-numpy-pexpect" - use_coverage: true - - name: "ubuntu-py38-pluggy" - python: "3.8" + tox_env: "py39-lsof-numpy-pexpect" + + - name: "ubuntu-py39-pluggy" + python: "3.9" os: ubuntu-latest - tox_env: "py38-pluggymain-pylib-xdist" - - name: "ubuntu-py38-freeze" - python: "3.8" + tox_env: "py39-pluggymain-pylib-xdist" + + - name: "ubuntu-py39-freeze" + python: "3.9" os: ubuntu-latest - tox_env: "py38-freeze" - - name: "ubuntu-py39" + tox_env: "py39-freeze" + + - name: "ubuntu-py39-xdist" python: "3.9" os: ubuntu-latest tox_env: "py39-xdist" - - name: "ubuntu-py310" + + - name: "ubuntu-py310-xdist" python: "3.10" os: ubuntu-latest tox_env: "py310-xdist" + - name: "ubuntu-py311" python: "3.11" os: ubuntu-latest tox_env: "py311" use_coverage: true + - name: "ubuntu-py312" python: "3.12" os: ubuntu-latest tox_env: "py312" use_coverage: true - - name: "ubuntu-py313" - python: "3.13-dev" + + - name: "ubuntu-py313-pexpect" + python: "3.13" os: ubuntu-latest tox_env: "py313-pexpect" use_coverage: true - - name: "ubuntu-pypy3" + + - name: "ubuntu-pypy3-xdist" python: "pypy-3.9" os: ubuntu-latest tox_env: "pypy3-xdist" - - name: "macos-py38" - python: "3.8" - os: macos-latest - tox_env: "py38-xdist" + - name: "macos-py39" python: "3.9" os: macos-latest tox_env: "py39-xdist" use_coverage: true + - name: "macos-py310" python: "3.10" os: macos-latest tox_env: "py310-xdist" + - name: "macos-py312" python: "3.12" os: macos-latest tox_env: "py312-xdist" + - name: "macos-py313" - python: "3.13-dev" + python: "3.13" os: macos-latest tox_env: "py313-xdist" + - name: "plugins" python: "3.12" os: ubuntu-latest tox_env: "plugins" + - name: "doctesting" - python: "3.8" + python: "3.9" os: ubuntu-latest tox_env: "doctesting" use_coverage: true @@ -192,12 +207,12 @@ jobs: contains( fromJSON( '[ - "windows-py38-pluggy", + "windows-py39-pluggy", "windows-py313", - "ubuntu-py38-pluggy", - "ubuntu-py38-freeze", + "ubuntu-py39-pluggy", + "ubuntu-py39-freeze", "ubuntu-py313", - "macos-py38", + "macos-py39", "macos-py313" ]' ), diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9e7be8cf6db..cb02fd0f00f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -53,6 +53,10 @@ repos: rev: v3.19.0 hooks: - id: pyupgrade + args: + - "--py39-plus" + # Manual because ruff does what pyupgrade does and the two are not out of sync + # often enough to make launching pyupgrade everytime worth it stages: [manual] - repo: local hooks: diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index d615e5fb113..56824a43ff4 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -270,7 +270,7 @@ Here is a simple overview, with pytest-specific bits: #. Run all the tests - You need to have Python 3.8 or later available in your system. Now + You need to have Python 3.9 or later available in your system. Now running tests is as simple as issuing this command:: $ tox -e linting,py39 diff --git a/README.rst b/README.rst index a81e082cdd7..091afc363da 100644 --- a/README.rst +++ b/README.rst @@ -97,7 +97,7 @@ Features - Can run `unittest `_ (or trial) test suites out of the box -- Python 3.8+ or PyPy3 +- Python 3.9+ or PyPy3 - Rich plugin architecture, with over 1300+ `external plugins `_ and thriving community diff --git a/changelog/12874.breaking.rst b/changelog/12874.breaking.rst new file mode 100644 index 00000000000..a442586eeb5 --- /dev/null +++ b/changelog/12874.breaking.rst @@ -0,0 +1 @@ +We dropped support for Python 3.8 following its end of life (2024-10-07). diff --git a/doc/en/backwards-compatibility.rst b/doc/en/backwards-compatibility.rst index c0feb833ce1..82f678b4dea 100644 --- a/doc/en/backwards-compatibility.rst +++ b/doc/en/backwards-compatibility.rst @@ -83,6 +83,7 @@ Released pytest versions support all Python versions that are actively maintaine ============== =================== pytest version min. Python version ============== =================== +8.4+ 3.9+ 8.0+ 3.8+ 7.1+ 3.7+ 6.2 - 7.0 3.6+ diff --git a/pyproject.toml b/pyproject.toml index 50fcfc65353..0541da9906e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ authors = [ { name = "Florian Bruhin" }, { name = "Others (See AUTHORS)" }, ] -requires-python = ">=3.8" +requires-python = ">=3.9" classifiers = [ "Development Status :: 6 - Mature", "Intended Audience :: Developers", @@ -33,7 +33,6 @@ classifiers = [ "Operating System :: POSIX", "Operating System :: Unix", "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", @@ -85,7 +84,7 @@ write_to = "src/_pytest/_version.py" [tool.black] target-version = [ - 'py38', + 'py39', ] [tool.ruff] @@ -508,7 +507,7 @@ files = [ mypy_path = [ "src", ] -python_version = "3.8" +python_version = "3.9" check_untyped_defs = true disallow_any_generics = true disallow_untyped_defs = true diff --git a/scripts/generate-gh-release-notes.py b/scripts/generate-gh-release-notes.py index 7f195ba1e0a..b6d92d085e1 100644 --- a/scripts/generate-gh-release-notes.py +++ b/scripts/generate-gh-release-notes.py @@ -11,10 +11,10 @@ from __future__ import annotations +from collections.abc import Sequence from pathlib import Path import re import sys -from typing import Sequence import pypandoc diff --git a/scripts/update-plugin-list.py b/scripts/update-plugin-list.py index 556004d9e98..693a25a00cc 100644 --- a/scripts/update-plugin-list.py +++ b/scripts/update-plugin-list.py @@ -1,14 +1,14 @@ # mypy: disallow-untyped-defs from __future__ import annotations +from collections.abc import Iterable +from collections.abc import Iterator import datetime import pathlib import re from textwrap import dedent from textwrap import indent from typing import Any -from typing import Iterable -from typing import Iterator from typing import TypedDict import packaging.version diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index dd3fd7addb6..ea7415be23e 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -2,6 +2,9 @@ from __future__ import annotations import ast +from collections.abc import Iterable +from collections.abc import Mapping +from collections.abc import Sequence import dataclasses import inspect from inspect import CO_VARARGS @@ -10,6 +13,7 @@ import os from pathlib import Path import re +from re import Pattern import sys import traceback from traceback import format_exception_only @@ -22,16 +26,9 @@ from typing import Final from typing import final from typing import Generic -from typing import Iterable -from typing import List from typing import Literal -from typing import Mapping from typing import overload -from typing import Pattern -from typing import Sequence from typing import SupportsIndex -from typing import Tuple -from typing import Type from typing import TypeVar from typing import Union @@ -56,7 +53,7 @@ TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"] -EXCEPTION_OR_MORE = Union[Type[BaseException], Tuple[Type[BaseException], ...]] +EXCEPTION_OR_MORE = Union[type[BaseException], tuple[type[BaseException], ...]] class Code: @@ -320,7 +317,7 @@ def name(self) -> str: return self.frame.code.raw.co_name -class Traceback(List[TracebackEntry]): +class Traceback(list[TracebackEntry]): """Traceback objects encapsulate and offer higher level access to Traceback entries.""" def __init__( diff --git a/src/_pytest/_code/source.py b/src/_pytest/_code/source.py index 604aff8ba19..e24ee3a260e 100644 --- a/src/_pytest/_code/source.py +++ b/src/_pytest/_code/source.py @@ -3,12 +3,12 @@ import ast from bisect import bisect_right +from collections.abc import Iterable +from collections.abc import Iterator import inspect import textwrap import tokenize import types -from typing import Iterable -from typing import Iterator from typing import overload import warnings diff --git a/src/_pytest/_io/pprint.py b/src/_pytest/_io/pprint.py index 7a6433d9128..d212b773166 100644 --- a/src/_pytest/_io/pprint.py +++ b/src/_pytest/_io/pprint.py @@ -16,6 +16,7 @@ from __future__ import annotations import collections as _collections +from collections.abc import Iterator import dataclasses as _dataclasses from io import StringIO as _StringIO import re @@ -23,7 +24,6 @@ from typing import Any from typing import Callable from typing import IO -from typing import Iterator class _safe_key: diff --git a/src/_pytest/_io/terminalwriter.py b/src/_pytest/_io/terminalwriter.py index 70ebd3d061b..50ce463f6b2 100644 --- a/src/_pytest/_io/terminalwriter.py +++ b/src/_pytest/_io/terminalwriter.py @@ -2,12 +2,12 @@ from __future__ import annotations +from collections.abc import Sequence import os import shutil import sys from typing import final from typing import Literal -from typing import Sequence from typing import TextIO from typing import TYPE_CHECKING diff --git a/src/_pytest/assertion/__init__.py b/src/_pytest/assertion/__init__.py index cbdf9fa0298..532b96fe431 100644 --- a/src/_pytest/assertion/__init__.py +++ b/src/_pytest/assertion/__init__.py @@ -3,9 +3,9 @@ from __future__ import annotations +from collections.abc import Generator import sys from typing import Any -from typing import Generator from typing import TYPE_CHECKING from _pytest.assertion import rewrite diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 37c09b03467..05934797a14 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -4,6 +4,9 @@ import ast from collections import defaultdict +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Sequence import errno import functools import importlib.abc @@ -21,9 +24,6 @@ import types from typing import Callable from typing import IO -from typing import Iterable -from typing import Iterator -from typing import Sequence from typing import TYPE_CHECKING from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 4dc1af4af03..3593f0ce036 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -4,16 +4,16 @@ from __future__ import annotations import collections.abc +from collections.abc import Iterable +from collections.abc import Mapping +from collections.abc import Sequence import os import pprint from typing import AbstractSet from typing import Any from typing import Callable -from typing import Iterable from typing import Literal -from typing import Mapping from typing import Protocol -from typing import Sequence from unicodedata import normalize from _pytest import outcomes diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 1b236efdc9b..bf643d6f4dc 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -5,6 +5,8 @@ # pytest-cache version. from __future__ import annotations +from collections.abc import Generator +from collections.abc import Iterable import dataclasses import errno import json @@ -12,8 +14,6 @@ from pathlib import Path import tempfile from typing import final -from typing import Generator -from typing import Iterable from .pathlib import resolve_from_str from .pathlib import rm_rf diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 2ac3b6bbc7f..93a3b04182e 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -5,6 +5,9 @@ import abc import collections +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator import contextlib import io from io import UnsupportedOperation @@ -18,10 +21,7 @@ from typing import cast from typing import Final from typing import final -from typing import Generator from typing import Generic -from typing import Iterable -from typing import Iterator from typing import Literal from typing import NamedTuple from typing import TextIO diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 92cf565e85f..5ec4d7b52ea 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -5,6 +5,10 @@ import argparse import collections.abc +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Sequence import contextlib import copy import dataclasses @@ -26,11 +30,7 @@ from typing import cast from typing import Final from typing import final -from typing import Generator from typing import IO -from typing import Iterable -from typing import Iterator -from typing import Sequence from typing import TextIO from typing import Type from typing import TYPE_CHECKING @@ -1978,7 +1978,7 @@ def _resolve_warning_category(category: str) -> type[Warning]: cat = getattr(m, klass) if not issubclass(cat, Warning): raise UsageError(f"{cat} is not a Warning subclass") - return cast(Type[Warning], cat) + return cast(type[Warning], cat) def apply_warning_filters( diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index 85aa4632702..455d2d8db26 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -2,6 +2,8 @@ from __future__ import annotations import argparse +from collections.abc import Mapping +from collections.abc import Sequence from gettext import gettext import os import sys @@ -9,11 +11,8 @@ from typing import Callable from typing import cast from typing import final -from typing import List from typing import Literal -from typing import Mapping from typing import NoReturn -from typing import Sequence import _pytest._io from _pytest.config.exceptions import UsageError @@ -144,7 +143,7 @@ def parse_setoption( parsedoption = self.parse(args, namespace=namespace) for name, value in parsedoption.__dict__.items(): setattr(option, name, value) - return cast(List[str], getattr(parsedoption, FILE_OR_DIR)) + return cast(list[str], getattr(parsedoption, FILE_OR_DIR)) def parse_known_args( self, diff --git a/src/_pytest/config/compat.py b/src/_pytest/config/compat.py index 2856d85d195..21eab4c7e47 100644 --- a/src/_pytest/config/compat.py +++ b/src/_pytest/config/compat.py @@ -1,9 +1,9 @@ from __future__ import annotations +from collections.abc import Mapping import functools from pathlib import Path from typing import Any -from typing import Mapping import warnings import pluggy diff --git a/src/_pytest/config/findpaths.py b/src/_pytest/config/findpaths.py index ce4c990b810..a7f45bf593e 100644 --- a/src/_pytest/config/findpaths.py +++ b/src/_pytest/config/findpaths.py @@ -1,10 +1,10 @@ from __future__ import annotations +from collections.abc import Iterable +from collections.abc import Sequence import os from pathlib import Path import sys -from typing import Iterable -from typing import Sequence import iniconfig diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index 763606cd60e..665d8536262 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -5,12 +5,12 @@ from __future__ import annotations import argparse +from collections.abc import Generator import functools import sys import types from typing import Any from typing import Callable -from typing import Generator import unittest from _pytest import outcomes diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 384dea976ad..a1f680570d8 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -4,21 +4,21 @@ from __future__ import annotations import bdb +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Sequence from contextlib import contextmanager import functools import inspect import os from pathlib import Path import platform +from re import Pattern import sys import traceback import types from typing import Any from typing import Callable -from typing import Generator -from typing import Iterable -from typing import Pattern -from typing import Sequence from typing import TYPE_CHECKING import warnings diff --git a/src/_pytest/faulthandler.py b/src/_pytest/faulthandler.py index d16aea1eb88..79efc1d1704 100644 --- a/src/_pytest/faulthandler.py +++ b/src/_pytest/faulthandler.py @@ -1,8 +1,8 @@ from __future__ import annotations +from collections.abc import Generator import os import sys -from typing import Generator from _pytest.config import Config from _pytest.config.argparsing import Parser diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 5817e88f47d..c4a8a09f84a 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -4,6 +4,13 @@ import abc from collections import defaultdict from collections import deque +from collections import OrderedDict +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import MutableMapping +from collections.abc import Sequence import dataclasses import functools import inspect @@ -15,21 +22,12 @@ from typing import Any from typing import Callable from typing import cast -from typing import Dict from typing import Final from typing import final -from typing import Generator from typing import Generic -from typing import Iterable -from typing import Iterator -from typing import Mapping -from typing import MutableMapping from typing import NoReturn from typing import Optional -from typing import OrderedDict from typing import overload -from typing import Sequence -from typing import Tuple from typing import TYPE_CHECKING from typing import TypeVar from typing import Union @@ -95,19 +93,19 @@ ] # The type of FixtureDef.cached_result (type alias generic in fixture value). _FixtureCachedResult = Union[ - Tuple[ + tuple[ # The result. FixtureValue, # Cache key. object, None, ], - Tuple[ + tuple[ None, # Cache key. object, # The exception and the original traceback. - Tuple[BaseException, Optional[types.TracebackType]], + tuple[BaseException, Optional[types.TracebackType]], ], ] @@ -177,7 +175,7 @@ class FixtureArgKey: _V = TypeVar("_V") -OrderedSet = Dict[_V, None] +OrderedSet = dict[_V, None] def get_parametrized_fixture_argkeys( diff --git a/src/_pytest/freeze_support.py b/src/_pytest/freeze_support.py index 2ba6f9b8bcc..959ff071d86 100644 --- a/src/_pytest/freeze_support.py +++ b/src/_pytest/freeze_support.py @@ -3,8 +3,8 @@ from __future__ import annotations +from collections.abc import Iterator import types -from typing import Iterator def freeze_includes() -> list[str]: diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py index 1886d5c9342..3d607189dbe 100644 --- a/src/_pytest/helpconfig.py +++ b/src/_pytest/helpconfig.py @@ -4,9 +4,9 @@ from __future__ import annotations from argparse import Action +from collections.abc import Generator import os import sys -from typing import Generator from _pytest.config import Config from _pytest.config import ExitCode diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 0a41b0aca47..8b20061d6f0 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -5,10 +5,10 @@ from __future__ import annotations +from collections.abc import Mapping +from collections.abc import Sequence from pathlib import Path from typing import Any -from typing import Mapping -from typing import Sequence from typing import TYPE_CHECKING from pluggy import HookspecMarker diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index efe6f489b48..46c456a5b6e 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -16,8 +16,8 @@ import os import platform import re +from re import Match from typing import Callable -from typing import Match import xml.etree.ElementTree as ET from _pytest import nodes diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 08c826ff6d4..00645dae2da 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -3,6 +3,8 @@ from __future__ import annotations +from collections.abc import Generator +from collections.abc import Mapping from contextlib import contextmanager from contextlib import nullcontext from datetime import datetime @@ -17,13 +19,9 @@ import re from types import TracebackType from typing import AbstractSet -from typing import Dict from typing import final -from typing import Generator from typing import Generic -from typing import List from typing import Literal -from typing import Mapping from typing import TYPE_CHECKING from typing import TypeVar @@ -53,7 +51,7 @@ DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S" _ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]+m") caplog_handler_key = StashKey["LogCaptureHandler"]() -caplog_records_key = StashKey[Dict[str, List[logging.LogRecord]]]() +caplog_records_key = StashKey[dict[str, list[logging.LogRecord]]]() def _remove_ansi_escape_sequences(text: str) -> str: @@ -813,13 +811,16 @@ def pytest_runtest_logreport(self) -> None: def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None]: """Implement the internals of the pytest_runtest_xxx() hooks.""" - with catching_logs( - self.caplog_handler, - level=self.log_level, - ) as caplog_handler, catching_logs( - self.report_handler, - level=self.log_level, - ) as report_handler: + with ( + catching_logs( + self.caplog_handler, + level=self.log_level, + ) as caplog_handler, + catching_logs( + self.report_handler, + level=self.log_level, + ) as report_handler, + ): caplog_handler.reset() report_handler.reset() item.stash[caplog_records_key][when] = caplog_handler.records diff --git a/src/_pytest/main.py b/src/_pytest/main.py index e5534e98d69..1e18a60141d 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -3,6 +3,9 @@ from __future__ import annotations import argparse +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Sequence import dataclasses import fnmatch import functools @@ -13,13 +16,9 @@ import sys from typing import AbstractSet from typing import Callable -from typing import Dict from typing import final -from typing import Iterable -from typing import Iterator from typing import Literal from typing import overload -from typing import Sequence from typing import TYPE_CHECKING import warnings @@ -476,7 +475,7 @@ class Failed(Exception): @dataclasses.dataclass -class _bestrelpath_cache(Dict[Path, str]): +class _bestrelpath_cache(dict[Path, str]): __slots__ = ("path",) path: Path diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py index a6f0155751a..c8c51d19aa0 100644 --- a/src/_pytest/mark/__init__.py +++ b/src/_pytest/mark/__init__.py @@ -3,10 +3,10 @@ from __future__ import annotations import collections +from collections.abc import Collection +from collections.abc import Iterable import dataclasses from typing import AbstractSet -from typing import Collection -from typing import Iterable from typing import Optional from typing import TYPE_CHECKING diff --git a/src/_pytest/mark/expression.py b/src/_pytest/mark/expression.py index 89cc0e94d3b..d0ab190e3b6 100644 --- a/src/_pytest/mark/expression.py +++ b/src/_pytest/mark/expression.py @@ -23,18 +23,18 @@ from __future__ import annotations import ast +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import Sequence import dataclasses import enum import keyword import re import types -from typing import Iterator from typing import Literal -from typing import Mapping from typing import NoReturn from typing import overload from typing import Protocol -from typing import Sequence __all__ = [ diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 14650a64759..09ef7d02cef 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -2,19 +2,19 @@ from __future__ import annotations import collections.abc +from collections.abc import Collection +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import MutableMapping +from collections.abc import Sequence import dataclasses import inspect from typing import Any from typing import Callable -from typing import Collection from typing import final -from typing import Iterable -from typing import Iterator -from typing import Mapping -from typing import MutableMapping from typing import NamedTuple from typing import overload -from typing import Sequence from typing import TYPE_CHECKING from typing import TypeVar from typing import Union diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index 46eb1724e35..1285e571551 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -3,15 +3,15 @@ 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 import re import sys from typing import Any from typing import final -from typing import Generator -from typing import Mapping -from typing import MutableMapping from typing import overload from typing import TypeVar import warnings diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 51bc5174628..e84fb37c9d0 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -2,6 +2,9 @@ from __future__ import annotations import abc +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import MutableMapping from functools import cached_property from inspect import signature import os @@ -10,9 +13,6 @@ from typing import Any from typing import Callable from typing import cast -from typing import Iterable -from typing import Iterator -from typing import MutableMapping from typing import NoReturn from typing import overload from typing import TYPE_CHECKING diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index 5b20803e586..ab39bac61b9 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -9,7 +9,6 @@ from typing import cast from typing import NoReturn from typing import Protocol -from typing import Type from typing import TypeVar from .warning_types import PytestDeprecationWarning @@ -82,7 +81,7 @@ def __init__( # Ideally would just be `exit.Exception = Exit` etc. _F = TypeVar("_F", bound=Callable[..., object]) -_ET = TypeVar("_ET", bound=Type[BaseException]) +_ET = TypeVar("_ET", bound=type[BaseException]) class _WithException(Protocol[_F, _ET]): diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index dd36559ce1b..f6392561951 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -1,6 +1,8 @@ from __future__ import annotations import atexit +from collections.abc import Iterable +from collections.abc import Iterator import contextlib from enum import Enum from errno import EBADF @@ -27,8 +29,6 @@ from types import ModuleType from typing import Any from typing import Callable -from typing import Iterable -from typing import Iterator from typing import TypeVar import uuid import warnings diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 3f7520ee4ad..d19151391b0 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -7,6 +7,9 @@ from __future__ import annotations import collections.abc +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Sequence import contextlib from fnmatch import fnmatch import gc @@ -25,12 +28,9 @@ from typing import Callable from typing import Final from typing import final -from typing import Generator from typing import IO -from typing import Iterable from typing import Literal from typing import overload -from typing import Sequence from typing import TextIO from typing import TYPE_CHECKING from weakref import WeakKeyDictionary diff --git a/src/_pytest/pytester_assertions.py b/src/_pytest/pytester_assertions.py index d543798f75a..915cc8a10ff 100644 --- a/src/_pytest/pytester_assertions.py +++ b/src/_pytest/pytester_assertions.py @@ -6,7 +6,7 @@ # module to not be already imported. from __future__ import annotations -from typing import Sequence +from collections.abc import Sequence from _pytest.reports import CollectReport from _pytest.reports import TestReport diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 1456b5212d4..153fbf8893b 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -6,6 +6,11 @@ import abc from collections import Counter from collections import defaultdict +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import Sequence import dataclasses import enum import fnmatch @@ -14,18 +19,12 @@ import itertools import os from pathlib import Path +from re import Pattern import types from typing import Any from typing import Callable -from typing import Dict from typing import final -from typing import Generator -from typing import Iterable -from typing import Iterator from typing import Literal -from typing import Mapping -from typing import Pattern -from typing import Sequence from typing import TYPE_CHECKING import warnings @@ -1078,7 +1077,7 @@ def get_direct_param_fixture_func(request: FixtureRequest) -> Any: # Used for storing pseudo fixturedefs for direct parametrization. -name2pseudofixturedef_key = StashKey[Dict[str, FixtureDef[Any]]]() +name2pseudofixturedef_key = StashKey[dict[str, FixtureDef[Any]]]() @final diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index d2107c2fc78..30a630c42ff 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -2,24 +2,22 @@ from __future__ import annotations from collections.abc import Collection +from collections.abc import Mapping +from collections.abc import Sequence from collections.abc import Sized from decimal import Decimal import math from numbers import Complex import pprint import re +from re import Pattern from types import TracebackType from typing import Any from typing import Callable from typing import cast from typing import ContextManager from typing import final -from typing import Mapping from typing import overload -from typing import Pattern -from typing import Sequence -from typing import Tuple -from typing import Type from typing import TYPE_CHECKING from typing import TypeVar @@ -1017,7 +1015,7 @@ def __exit__( if not issubclass(exc_type, self.expected_exception): return False # Cast to narrow the exception type now that it's verified. - exc_info = cast(Tuple[Type[E], E, TracebackType], (exc_type, exc_val, exc_tb)) + exc_info = cast(tuple[type[E], E, TracebackType], (exc_type, exc_val, exc_tb)) self.excinfo.fill_unfilled(exc_info) if self.match_expr is not None: self.excinfo.match(self.match_expr) diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 0dc002edd94..fc26fcc78e4 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -3,16 +3,16 @@ from __future__ import annotations +from collections.abc import Generator +from collections.abc import Iterator from pprint import pformat import re +from re import Pattern from types import TracebackType from typing import Any from typing import Callable from typing import final -from typing import Generator -from typing import Iterator from typing import overload -from typing import Pattern from typing import TYPE_CHECKING from typing import TypeVar diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index 77cbf773e23..4d926ef6c9e 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -1,6 +1,10 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import Sequence import dataclasses from io import StringIO import os @@ -8,12 +12,8 @@ from typing import Any from typing import cast from typing import final -from typing import Iterable -from typing import Iterator from typing import Literal -from typing import Mapping from typing import NoReturn -from typing import Sequence from typing import TYPE_CHECKING from _pytest._code.code import ExceptionChainRepr diff --git a/src/_pytest/setuponly.py b/src/_pytest/setuponly.py index de297f408d3..1e887a896f5 100644 --- a/src/_pytest/setuponly.py +++ b/src/_pytest/setuponly.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Generator +from collections.abc import Generator from _pytest._io.saferepr import saferepr from _pytest.config import Config diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index 9818be2ab03..8fa17a01eb0 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -3,13 +3,13 @@ from __future__ import annotations +from collections.abc import Generator from collections.abc import Mapping import dataclasses import os import platform import sys import traceback -from typing import Generator from typing import Optional from _pytest.config import Config diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 55ef1ea8eee..d2baee40d9c 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -8,6 +8,9 @@ import argparse from collections import Counter +from collections.abc import Generator +from collections.abc import Mapping +from collections.abc import Sequence import dataclasses import datetime from functools import partial @@ -20,11 +23,8 @@ from typing import Callable from typing import ClassVar from typing import final -from typing import Generator from typing import Literal -from typing import Mapping from typing import NamedTuple -from typing import Sequence from typing import TextIO from typing import TYPE_CHECKING import warnings diff --git a/src/_pytest/threadexception.py b/src/_pytest/threadexception.py index c1ed80387aa..49560bf7d7f 100644 --- a/src/_pytest/threadexception.py +++ b/src/_pytest/threadexception.py @@ -1,11 +1,11 @@ from __future__ import annotations +from collections.abc import Generator import threading import traceback from types import TracebackType from typing import Any from typing import Callable -from typing import Generator from typing import TYPE_CHECKING import warnings diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 1731a4b8d0d..c5b51c87741 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -3,6 +3,7 @@ from __future__ import annotations +from collections.abc import Generator import dataclasses import os from pathlib import Path @@ -10,9 +11,7 @@ from shutil import rmtree import tempfile from typing import Any -from typing import Dict from typing import final -from typing import Generator from typing import Literal from .pathlib import cleanup_dead_symlinks @@ -34,7 +33,7 @@ from _pytest.stash import StashKey -tmppath_result_key = StashKey[Dict[str, bool]]() +tmppath_result_key = StashKey[dict[str, bool]]() RetentionType = Literal["all", "failed", "none"] diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index 8cecd4f9339..6d967b972ee 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -3,16 +3,14 @@ from __future__ import annotations +from collections.abc import Generator +from collections.abc import Iterable import inspect import sys import traceback import types from typing import Any from typing import Callable -from typing import Generator -from typing import Iterable -from typing import Tuple -from typing import Type from typing import TYPE_CHECKING from typing import Union @@ -43,8 +41,8 @@ _SysExcInfoType = Union[ - Tuple[Type[BaseException], BaseException, types.TracebackType], - Tuple[None, None, None], + tuple[type[BaseException], BaseException, types.TracebackType], + tuple[None, None, None], ] diff --git a/src/_pytest/unraisableexception.py b/src/_pytest/unraisableexception.py index 77a2de20041..9ef583137e1 100644 --- a/src/_pytest/unraisableexception.py +++ b/src/_pytest/unraisableexception.py @@ -1,11 +1,11 @@ from __future__ import annotations +from collections.abc import Generator import sys import traceback from types import TracebackType from typing import Any from typing import Callable -from typing import Generator from typing import TYPE_CHECKING import warnings diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index eeb4772649d..87cdbda288f 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -1,9 +1,9 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Generator from contextlib import contextmanager import sys -from typing import Generator from typing import Literal import warnings diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index ab56904d040..22e695977e1 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -964,8 +964,8 @@ def raiseos(): upframe = sys._getframe().f_back assert upframe is not None if upframe.f_code.co_name == "_makepath": - # Only raise with expected calls, but not via e.g. inspect for - # py38-windows. + # Only raise with expected calls, and not accidentally via 'inspect' + # See 79ae86cc3f76d69460e1c7beca4ce95e68ab80a6 raised += 1 raise OSError(2, "custom_oserror") return orig_path_cwd() diff --git a/testing/code/test_source.py b/testing/code/test_source.py index a00259976c4..d78d9e7025a 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -336,7 +336,7 @@ def test_findsource(monkeypatch) -> None: assert src is not None assert "if 1:" in str(src) - d: Dict[str, Any] = {} + d: dict[str, Any] = {} eval(co, d) src, lineno = findsource(d["x"]) assert src is not None diff --git a/testing/conftest.py b/testing/conftest.py index 69af03324d6..110ad0d9b35 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -1,10 +1,10 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Generator import dataclasses import re import sys -from typing import Generator from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester diff --git a/testing/io/test_terminalwriter.py b/testing/io/test_terminalwriter.py index 92cde240a11..f14e884b1f0 100644 --- a/testing/io/test_terminalwriter.py +++ b/testing/io/test_terminalwriter.py @@ -1,13 +1,13 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Generator import io import os from pathlib import Path import re import shutil import sys -from typing import Generator from unittest import mock from _pytest._io import terminalwriter diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index 0603eaba218..5f94cb8508a 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -2,8 +2,8 @@ # mypy: disallow-untyped-defs from __future__ import annotations +from collections.abc import Iterator import logging -from typing import Iterator from _pytest.logging import caplog_records_key from _pytest.pytester import Pytester diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 0a4ebf2c9af..df6dbaee0fd 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -1,6 +1,8 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Iterator +from collections.abc import Sequence import dataclasses import itertools import re @@ -8,9 +10,6 @@ import textwrap from typing import Any from typing import cast -from typing import Dict -from typing import Iterator -from typing import Sequence import hypothesis from hypothesis import strategies @@ -154,7 +153,7 @@ class DummyFixtureDef: _scope: Scope fixtures_defs = cast( - Dict[str, Sequence[fixtures.FixtureDef[object]]], + dict[str, Sequence[fixtures.FixtureDef[object]]], dict( session_fix=[DummyFixtureDef(Scope.Session)], package_fix=[DummyFixtureDef(Scope.Package)], diff --git a/testing/test_assertion.py b/testing/test_assertion.py index b10ca1c91f4..a14c4125cf6 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -1,10 +1,10 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import MutableSequence import sys import textwrap from typing import Any -from typing import MutableSequence from typing import NamedTuple import attr diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 7be473d897a..2a988149daf 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -2,6 +2,8 @@ from __future__ import annotations import ast +from collections.abc import Generator +from collections.abc import Mapping import dis import errno from functools import partial @@ -17,8 +19,6 @@ import sys import textwrap from typing import cast -from typing import Generator -from typing import Mapping from unittest import mock import zipfile diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 94bc55d3047..1ba4659d84e 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -1,13 +1,13 @@ from __future__ import annotations +from collections.abc import Generator +from collections.abc import Sequence from enum import auto from enum import Enum import os from pathlib import Path import shutil from typing import Any -from typing import Generator -from typing import Sequence from _pytest.compat import assert_never from _pytest.config import ExitCode diff --git a/testing/test_capture.py b/testing/test_capture.py index 328de740e8a..98986af6f1f 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1,6 +1,7 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Generator import contextlib import io from io import UnsupportedOperation @@ -10,7 +11,6 @@ import textwrap from typing import BinaryIO from typing import cast -from typing import Generator from typing import TextIO from _pytest import capture diff --git a/testing/test_config.py b/testing/test_config.py index b2825678b46..53f26269f09 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1,6 +1,7 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Sequence import dataclasses import importlib.metadata import os @@ -9,7 +10,6 @@ import sys import textwrap from typing import Any -from typing import Sequence import _pytest._code from _pytest.config import _get_plugin_specs_as_list diff --git a/testing/test_conftest.py b/testing/test_conftest.py index ea60c1909c2..bbb1d301ebe 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -1,13 +1,12 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Generator +from collections.abc import Sequence import os from pathlib import Path import textwrap from typing import cast -from typing import Generator -from typing import List -from typing import Sequence from _pytest.config import ExitCode from _pytest.config import PytestPluginManager @@ -459,7 +458,7 @@ def impct(p, importmode, root, consider_namespace_packages): rootpath=pytester.path, consider_namespace_packages=False, ) - mods = cast(List[Path], conftest._getconftestmodules(sub)) + mods = cast(list[Path], conftest._getconftestmodules(sub)) expected = [ct1, ct2] assert mods == expected diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index 7c62d90f2b9..ad75273d703 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -1,12 +1,12 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Generator import os from pathlib import Path import re import sys import textwrap -from typing import Generator from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index 62359303f3b..436a6b9cae3 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -1,6 +1,9 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Generator +from collections.abc import Iterator +from collections.abc import Sequence import errno import importlib.abc import importlib.machinery @@ -12,9 +15,6 @@ from textwrap import dedent from types import ModuleType from typing import Any -from typing import Generator -from typing import Iterator -from typing import Sequence import unittest.mock from _pytest.config import ExitCode diff --git a/testing/test_reports.py b/testing/test_reports.py index 3e314d2aade..7a893981838 100644 --- a/testing/test_reports.py +++ b/testing/test_reports.py @@ -1,7 +1,7 @@ # mypy: allow-untyped-defs from __future__ import annotations -from typing import Sequence +from collections.abc import Sequence from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionRepr diff --git a/tox.ini b/tox.ini index 61563ca2c5f..80fae513142 100644 --- a/tox.ini +++ b/tox.ini @@ -4,18 +4,17 @@ minversion = 3.20.0 distshare = {homedir}/.tox/distshare envlist = linting - py38 py39 py310 py311 py312 py313 pypy3 - py38-{pexpect,xdist,unittestextras,numpy,pluggymain,pylib} + py39-{pexpect,xdist,unittestextras,numpy,pluggymain,pylib} doctesting doctesting-coverage plugins - py38-freeze + py39-freeze docs docs-checklinks @@ -58,7 +57,7 @@ setenv = PYTHONWARNDEFAULTENCODING=1 # Configuration to run with coverage similar to CI, e.g. - # "tox -e py38-coverage". + # "tox -e py39-coverage". coverage: _PYTEST_TOX_COVERAGE_RUN=coverage run -m coverage: _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess coverage: COVERAGE_FILE={toxinidir}/.coverage @@ -178,7 +177,7 @@ commands = pytest pytest_twisted_integration.py pytest simple_integration.py --force-sugar --flakes -[testenv:py38-freeze] +[testenv:py39-freeze] description = test pytest frozen with `pyinstaller` under `{basepython}` changedir = testing/freeze From 05ed0d0f943ce25b635e92e4e166524435386018 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 10 Nov 2024 15:43:08 +0100 Subject: [PATCH 57/64] Enable pylint's python 3.8 typing checks (deprecated-typing-alias) --- pyproject.toml | 1 - src/_pytest/_code/code.py | 2 +- src/_pytest/_io/pprint.py | 2 +- src/_pytest/_py/error.py | 2 +- src/_pytest/_py/path.py | 2 +- src/_pytest/assertion/rewrite.py | 2 +- src/_pytest/assertion/util.py | 2 +- src/_pytest/compat.py | 2 +- src/_pytest/config/__init__.py | 2 +- src/_pytest/config/argparsing.py | 2 +- src/_pytest/debugging.py | 2 +- src/_pytest/doctest.py | 2 +- src/_pytest/fixtures.py | 8 +++----- src/_pytest/junitxml.py | 2 +- src/_pytest/main.py | 2 +- src/_pytest/mark/structures.py | 2 +- src/_pytest/nodes.py | 2 +- src/_pytest/outcomes.py | 2 +- src/_pytest/pathlib.py | 2 +- src/_pytest/pytester.py | 2 +- src/_pytest/python.py | 2 +- src/_pytest/python_api.py | 2 +- src/_pytest/recwarn.py | 2 +- src/_pytest/runner.py | 2 +- src/_pytest/terminal.py | 2 +- src/_pytest/threadexception.py | 2 +- src/_pytest/unittest.py | 2 +- src/_pytest/unraisableexception.py | 2 +- testing/test_doctest.py | 2 +- testing/test_mark_expression.py | 2 +- testing/test_tmpdir.py | 2 +- 31 files changed, 32 insertions(+), 35 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0541da9906e..6856e652829 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -229,7 +229,6 @@ disable = [ "deprecated-argument", "deprecated-attribute", "deprecated-class", - "deprecated-typing-alias", "disallowed-name", # foo / bar are used often in tests "duplicate-code", "else-if-used", # not activated by default, PLR5501 disabled in ruff diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index ea7415be23e..14ac545490f 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -2,6 +2,7 @@ from __future__ import annotations import ast +from collections.abc import Callable from collections.abc import Iterable from collections.abc import Mapping from collections.abc import Sequence @@ -21,7 +22,6 @@ from types import FrameType from types import TracebackType from typing import Any -from typing import Callable from typing import ClassVar from typing import Final from typing import final diff --git a/src/_pytest/_io/pprint.py b/src/_pytest/_io/pprint.py index d212b773166..ca780c41344 100644 --- a/src/_pytest/_io/pprint.py +++ b/src/_pytest/_io/pprint.py @@ -16,13 +16,13 @@ from __future__ import annotations import collections as _collections +from collections.abc import Callable from collections.abc import Iterator import dataclasses as _dataclasses from io import StringIO as _StringIO import re import types as _types from typing import Any -from typing import Callable from typing import IO diff --git a/src/_pytest/_py/error.py b/src/_pytest/_py/error.py index ab3a4ed318e..3a63304008a 100644 --- a/src/_pytest/_py/error.py +++ b/src/_pytest/_py/error.py @@ -2,10 +2,10 @@ from __future__ import annotations +from collections.abc import Callable import errno import os import sys -from typing import Callable from typing import TYPE_CHECKING from typing import TypeVar diff --git a/src/_pytest/_py/path.py b/src/_pytest/_py/path.py index c7ab1182f4a..e353c1a9b52 100644 --- a/src/_pytest/_py/path.py +++ b/src/_pytest/_py/path.py @@ -4,6 +4,7 @@ from __future__ import annotations import atexit +from collections.abc import Callable from contextlib import contextmanager import fnmatch import importlib.util @@ -23,7 +24,6 @@ from stat import S_ISREG import sys from typing import Any -from typing import Callable from typing import cast from typing import Literal from typing import overload diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 05934797a14..93a08a4e69f 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -4,6 +4,7 @@ import ast from collections import defaultdict +from collections.abc import Callable from collections.abc import Iterable from collections.abc import Iterator from collections.abc import Sequence @@ -22,7 +23,6 @@ import sys import tokenize import types -from typing import Callable from typing import IO from typing import TYPE_CHECKING diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 3593f0ce036..49e977171b9 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -4,6 +4,7 @@ from __future__ import annotations import collections.abc +from collections.abc import Callable from collections.abc import Iterable from collections.abc import Mapping from collections.abc import Sequence @@ -11,7 +12,6 @@ import pprint from typing import AbstractSet from typing import Any -from typing import Callable from typing import Literal from typing import Protocol from unicodedata import normalize diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 82aea5e635e..2f7413d466a 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -3,6 +3,7 @@ from __future__ import annotations +from collections.abc import Callable import dataclasses import enum import functools @@ -13,7 +14,6 @@ from pathlib import Path import sys from typing import Any -from typing import Callable from typing import Final from typing import NoReturn diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 5ec4d7b52ea..fb012d081e1 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -5,6 +5,7 @@ import argparse import collections.abc +from collections.abc import Callable from collections.abc import Generator from collections.abc import Iterable from collections.abc import Iterator @@ -26,7 +27,6 @@ import types from types import FunctionType from typing import Any -from typing import Callable from typing import cast from typing import Final from typing import final diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index 455d2d8db26..5a4e2e7993e 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -2,13 +2,13 @@ from __future__ import annotations import argparse +from collections.abc import Callable from collections.abc import Mapping from collections.abc import Sequence from gettext import gettext import os import sys from typing import Any -from typing import Callable from typing import cast from typing import final from typing import Literal diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index 665d8536262..a9886c29d3c 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -5,12 +5,12 @@ from __future__ import annotations import argparse +from collections.abc import Callable from collections.abc import Generator import functools import sys import types from typing import Any -from typing import Callable import unittest from _pytest import outcomes diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index a1f680570d8..94868d06106 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -4,6 +4,7 @@ from __future__ import annotations import bdb +from collections.abc import Callable from collections.abc import Generator from collections.abc import Iterable from collections.abc import Sequence @@ -18,7 +19,6 @@ import traceback import types from typing import Any -from typing import Callable from typing import TYPE_CHECKING import warnings diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index c4a8a09f84a..80c4ec962b8 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -5,6 +5,7 @@ from collections import defaultdict from collections import deque from collections import OrderedDict +from collections.abc import Callable from collections.abc import Generator from collections.abc import Iterable from collections.abc import Iterator @@ -20,7 +21,6 @@ import types from typing import AbstractSet from typing import Any -from typing import Callable from typing import cast from typing import Final from typing import final @@ -89,7 +89,7 @@ FixtureFunction = TypeVar("FixtureFunction", bound=Callable[..., object]) # The type of a fixture function (type alias generic in fixture value). _FixtureFunc = Union[ - Callable[..., FixtureValue], Callable[..., Generator[FixtureValue, None, None]] + Callable[..., FixtureValue], Callable[..., Generator[FixtureValue]] ] # The type of FixtureDef.cached_result (type alias generic in fixture value). _FixtureCachedResult = Union[ @@ -891,9 +891,7 @@ def call_fixture_func( fixturefunc: _FixtureFunc[FixtureValue], request: FixtureRequest, kwargs ) -> FixtureValue: if inspect.isgeneratorfunction(fixturefunc): - fixturefunc = cast( - Callable[..., Generator[FixtureValue, None, None]], fixturefunc - ) + fixturefunc = cast(Callable[..., Generator[FixtureValue]], fixturefunc) generator = fixturefunc(**kwargs) try: fixture_result = next(generator) diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index 46c456a5b6e..af072f0d1a4 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -10,6 +10,7 @@ from __future__ import annotations +from collections.abc import Callable from datetime import datetime from datetime import timezone import functools @@ -17,7 +18,6 @@ import platform import re from re import Match -from typing import Callable import xml.etree.ElementTree as ET from _pytest import nodes diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 1e18a60141d..ebd94c84c76 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -3,6 +3,7 @@ from __future__ import annotations import argparse +from collections.abc import Callable from collections.abc import Iterable from collections.abc import Iterator from collections.abc import Sequence @@ -15,7 +16,6 @@ from pathlib import Path import sys from typing import AbstractSet -from typing import Callable from typing import final from typing import Literal from typing import overload diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 09ef7d02cef..d1e0a49b62d 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -2,6 +2,7 @@ from __future__ import annotations import collections.abc +from collections.abc import Callable from collections.abc import Collection from collections.abc import Iterable from collections.abc import Iterator @@ -11,7 +12,6 @@ import dataclasses import inspect from typing import Any -from typing import Callable from typing import final from typing import NamedTuple from typing import overload diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index e84fb37c9d0..5b50fbc92cb 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -2,6 +2,7 @@ from __future__ import annotations import abc +from collections.abc import Callable from collections.abc import Iterable from collections.abc import Iterator from collections.abc import MutableMapping @@ -11,7 +12,6 @@ import pathlib from pathlib import Path from typing import Any -from typing import Callable from typing import cast from typing import NoReturn from typing import overload diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index ab39bac61b9..d792382a9c1 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -3,9 +3,9 @@ from __future__ import annotations +from collections.abc import Callable import sys from typing import Any -from typing import Callable from typing import cast from typing import NoReturn from typing import Protocol diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index f6392561951..55b27985d31 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -1,6 +1,7 @@ from __future__ import annotations import atexit +from collections.abc import Callable from collections.abc import Iterable from collections.abc import Iterator import contextlib @@ -28,7 +29,6 @@ import types from types import ModuleType from typing import Any -from typing import Callable from typing import TypeVar import uuid import warnings diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index d19151391b0..056be52a4ed 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -7,6 +7,7 @@ from __future__ import annotations import collections.abc +from collections.abc import Callable from collections.abc import Generator from collections.abc import Iterable from collections.abc import Sequence @@ -25,7 +26,6 @@ import sys import traceback from typing import Any -from typing import Callable from typing import Final from typing import final from typing import IO diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 153fbf8893b..3a42b43d0a1 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -6,6 +6,7 @@ import abc from collections import Counter from collections import defaultdict +from collections.abc import Callable from collections.abc import Generator from collections.abc import Iterable from collections.abc import Iterator @@ -22,7 +23,6 @@ from re import Pattern import types from typing import Any -from typing import Callable from typing import final from typing import Literal from typing import TYPE_CHECKING diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 30a630c42ff..aae0bd50ce0 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -1,6 +1,7 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Callable from collections.abc import Collection from collections.abc import Mapping from collections.abc import Sequence @@ -13,7 +14,6 @@ from re import Pattern from types import TracebackType from typing import Any -from typing import Callable from typing import cast from typing import ContextManager from typing import final diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index fc26fcc78e4..b168bbe746d 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -3,6 +3,7 @@ from __future__ import annotations +from collections.abc import Callable from collections.abc import Generator from collections.abc import Iterator from pprint import pformat @@ -10,7 +11,6 @@ from re import Pattern from types import TracebackType from typing import Any -from typing import Callable from typing import final from typing import overload from typing import TYPE_CHECKING diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 0b60301bf5f..5189efce538 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -4,11 +4,11 @@ from __future__ import annotations import bdb +from collections.abc import Callable import dataclasses import os import sys import types -from typing import Callable from typing import cast from typing import final from typing import Generic diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index d2baee40d9c..a74f73bff17 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -8,6 +8,7 @@ import argparse from collections import Counter +from collections.abc import Callable from collections.abc import Generator from collections.abc import Mapping from collections.abc import Sequence @@ -20,7 +21,6 @@ import sys import textwrap from typing import Any -from typing import Callable from typing import ClassVar from typing import final from typing import Literal diff --git a/src/_pytest/threadexception.py b/src/_pytest/threadexception.py index 49560bf7d7f..4a76a9d9000 100644 --- a/src/_pytest/threadexception.py +++ b/src/_pytest/threadexception.py @@ -1,11 +1,11 @@ from __future__ import annotations +from collections.abc import Callable from collections.abc import Generator import threading import traceback from types import TracebackType from typing import Any -from typing import Callable from typing import TYPE_CHECKING import warnings diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index 6d967b972ee..04d50b53090 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -3,6 +3,7 @@ from __future__ import annotations +from collections.abc import Callable from collections.abc import Generator from collections.abc import Iterable import inspect @@ -10,7 +11,6 @@ import traceback import types from typing import Any -from typing import Callable from typing import TYPE_CHECKING from typing import Union diff --git a/src/_pytest/unraisableexception.py b/src/_pytest/unraisableexception.py index 9ef583137e1..5796b37715d 100644 --- a/src/_pytest/unraisableexception.py +++ b/src/_pytest/unraisableexception.py @@ -1,11 +1,11 @@ from __future__ import annotations +from collections.abc import Callable from collections.abc import Generator import sys import traceback from types import TracebackType from typing import Any -from typing import Callable from typing import TYPE_CHECKING import warnings diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 4aa4876c711..d3ad09da871 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -1,11 +1,11 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Callable import inspect from pathlib import Path import sys import textwrap -from typing import Callable from _pytest.doctest import _get_checker from _pytest.doctest import _is_main_py diff --git a/testing/test_mark_expression.py b/testing/test_mark_expression.py index a61a9f21560..884c4b5af88 100644 --- a/testing/test_mark_expression.py +++ b/testing/test_mark_expression.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Callable +from collections.abc import Callable from typing import cast from _pytest.mark import MarkMatcher diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 865d8e0b05c..016588a143d 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -1,12 +1,12 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Callable import dataclasses import os from pathlib import Path import stat import sys -from typing import Callable from typing import cast import warnings From f57b26b76c064535f54f5d9cd612ae888254be8f Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Thu, 10 Oct 2024 18:22:48 +0200 Subject: [PATCH 58/64] Remove code that is impossible to reach on python 3.9+ --- src/_pytest/config/argparsing.py | 40 -------------------------------- testing/test_assertrewrite.py | 4 ---- 2 files changed, 44 deletions(-) diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index 5a4e2e7993e..d535fe1096a 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -5,9 +5,7 @@ from collections.abc import Callable from collections.abc import Mapping from collections.abc import Sequence -from gettext import gettext import os -import sys from typing import Any from typing import cast from typing import final @@ -446,44 +444,6 @@ def parse_args( # type: ignore getattr(parsed, FILE_OR_DIR).extend(unrecognized) return parsed - if sys.version_info < (3, 9): # pragma: no cover - # Backport of https://github.com/python/cpython/pull/14316 so we can - # disable long --argument abbreviations without breaking short flags. - def _parse_optional( - self, arg_string: str - ) -> tuple[argparse.Action | None, str, str | None] | None: - if not arg_string: - return None - if arg_string[0] not in self.prefix_chars: - return None - if arg_string in self._option_string_actions: - action = self._option_string_actions[arg_string] - return action, arg_string, None - if len(arg_string) == 1: - return None - if "=" in arg_string: - option_string, explicit_arg = arg_string.split("=", 1) - if option_string in self._option_string_actions: - action = self._option_string_actions[option_string] - return action, option_string, explicit_arg - if self.allow_abbrev or not arg_string.startswith("--"): - option_tuples = self._get_option_tuples(arg_string) - if len(option_tuples) > 1: - msg = gettext( - "ambiguous option: %(option)s could match %(matches)s" - ) - options = ", ".join(option for _, option, _ in option_tuples) - self.error(msg % {"option": arg_string, "matches": options}) - elif len(option_tuples) == 1: - (option_tuple,) = option_tuples - return option_tuple - if self._negative_number_matcher.match(arg_string): - if not self._has_negative_number_optionals: - return None - if " " in arg_string: - return None - return None, arg_string, None - class DropShorterLongHelpFormatter(argparse.HelpFormatter): """Shorten help for long options that differ only in extra hyphens. diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 2a988149daf..eed59a2dce7 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1018,10 +1018,6 @@ def test_zipfile(self, pytester: Pytester) -> None: ) assert pytester.runpytest().ret == ExitCode.NO_TESTS_COLLECTED - @pytest.mark.skipif( - sys.version_info < (3, 9), - reason="importlib.resources.files was introduced in 3.9", - ) def test_load_resource_via_files_with_rewrite(self, pytester: Pytester) -> None: example = pytester.path.joinpath("demo") / "example" init = pytester.path.joinpath("demo") / "__init__.py" From 66fc31f05f02914c190fd9120653143f0bcb5105 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 10 Nov 2024 22:03:44 +0100 Subject: [PATCH 59/64] Fix 'typing.AbstractSet' is deprecated, use 'collections.abc.Set' --- src/_pytest/assertion/util.py | 2 +- src/_pytest/fixtures.py | 2 +- src/_pytest/logging.py | 2 +- src/_pytest/main.py | 2 +- src/_pytest/mark/__init__.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 49e977171b9..0db846ce204 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -8,9 +8,9 @@ from collections.abc import Iterable from collections.abc import Mapping from collections.abc import Sequence +from collections.abc import Set as AbstractSet import os import pprint -from typing import AbstractSet from typing import Any from typing import Literal from typing import Protocol diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 80c4ec962b8..01707418755 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -12,6 +12,7 @@ from collections.abc import Mapping from collections.abc import MutableMapping from collections.abc import Sequence +from collections.abc import Set as AbstractSet import dataclasses import functools import inspect @@ -19,7 +20,6 @@ from pathlib import Path import sys import types -from typing import AbstractSet from typing import Any from typing import cast from typing import Final diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 00645dae2da..ca5fbda6fcc 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -5,6 +5,7 @@ from collections.abc import Generator from collections.abc import Mapping +from collections.abc import Set as AbstractSet from contextlib import contextmanager from contextlib import nullcontext from datetime import datetime @@ -18,7 +19,6 @@ from pathlib import Path import re from types import TracebackType -from typing import AbstractSet from typing import final from typing import Generic from typing import Literal diff --git a/src/_pytest/main.py b/src/_pytest/main.py index ebd94c84c76..d7086537f39 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -7,6 +7,7 @@ from collections.abc import Iterable from collections.abc import Iterator from collections.abc import Sequence +from collections.abc import Set as AbstractSet import dataclasses import fnmatch import functools @@ -15,7 +16,6 @@ import os from pathlib import Path import sys -from typing import AbstractSet from typing import final from typing import Literal from typing import overload diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py index c8c51d19aa0..efb966c09aa 100644 --- a/src/_pytest/mark/__init__.py +++ b/src/_pytest/mark/__init__.py @@ -5,8 +5,8 @@ import collections from collections.abc import Collection from collections.abc import Iterable +from collections.abc import Set as AbstractSet import dataclasses -from typing import AbstractSet from typing import Optional from typing import TYPE_CHECKING From e684e64dd04967c94d34b7cab651640512b67fa6 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 10 Nov 2024 22:04:32 +0100 Subject: [PATCH 60/64] Fix 'typing.Type' is deprecated, use 'type' instead --- src/_pytest/config/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index fb012d081e1..6160f780b1b 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -32,7 +32,6 @@ from typing import final from typing import IO from typing import TextIO -from typing import Type from typing import TYPE_CHECKING import warnings From a6ef97b65bea694f179ed026e988cf7535ba8d2e Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sun, 10 Nov 2024 22:05:05 +0100 Subject: [PATCH 61/64] Fix 'typing.ContextManager' is deprecated, use 'contextlib.AbstractContextManager' --- src/_pytest/python_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index aae0bd50ce0..fb00f1dd749 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -6,6 +6,7 @@ from collections.abc import Mapping from collections.abc import Sequence from collections.abc import Sized +from contextlib import AbstractContextManager from decimal import Decimal import math from numbers import Complex @@ -15,7 +16,6 @@ from types import TracebackType from typing import Any from typing import cast -from typing import ContextManager from typing import final from typing import overload from typing import TYPE_CHECKING @@ -978,7 +978,7 @@ def raises( @final -class RaisesContext(ContextManager[_pytest._code.ExceptionInfo[E]]): +class RaisesContext(AbstractContextManager[_pytest._code.ExceptionInfo[E]]): def __init__( self, expected_exception: type[E] | tuple[type[E], ...], From 1bacc000779e46e55925533eeb97465e8c857254 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Wed, 20 Nov 2024 22:24:45 +0100 Subject: [PATCH 62/64] [typing] Use the re namespace in 're' typing everywhere --- src/_pytest/_code/code.py | 9 ++++----- src/_pytest/doctest.py | 5 ++--- src/_pytest/junitxml.py | 3 +-- src/_pytest/python.py | 4 ++-- src/_pytest/python_api.py | 7 +++---- src/_pytest/recwarn.py | 11 ++++++----- 6 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 14ac545490f..b98ce013b6e 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -14,7 +14,6 @@ import os from pathlib import Path import re -from re import Pattern import sys import traceback from traceback import format_exception_only @@ -722,7 +721,7 @@ def _stringify_exception(self, exc: BaseException) -> str: ] ) - def match(self, regexp: str | Pattern[str]) -> Literal[True]: + def match(self, regexp: str | re.Pattern[str]) -> Literal[True]: """Check whether the regular expression `regexp` matches the string representation of the exception using :func:`python:re.search`. @@ -741,7 +740,7 @@ def _group_contains( self, exc_group: BaseExceptionGroup[BaseException], expected_exception: EXCEPTION_OR_MORE, - match: str | Pattern[str] | None, + match: str | re.Pattern[str] | None, target_depth: int | None = None, current_depth: int = 1, ) -> bool: @@ -771,7 +770,7 @@ def group_contains( self, expected_exception: EXCEPTION_OR_MORE, *, - match: str | Pattern[str] | None = None, + match: str | re.Pattern[str] | None = None, depth: int | None = None, ) -> bool: """Check whether a captured exception group contains a matching exception. @@ -780,7 +779,7 @@ def group_contains( The expected exception type, or a tuple if one of multiple possible exception types are expected. - :param str | Pattern[str] | None match: + :param str | re.Pattern[str] | None match: If specified, a string containing a regular expression, or a regular expression object, that is tested against the string representation of the exception and its `PEP-678 ` `__notes__` diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 94868d06106..598df84d70e 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -14,7 +14,7 @@ import os from pathlib import Path import platform -from re import Pattern +import re import sys import traceback import types @@ -593,7 +593,6 @@ def _from_module(self, module, object): def _init_checker_class() -> type[doctest.OutputChecker]: import doctest - import re class LiteralsOutputChecker(doctest.OutputChecker): # Based on doctest_nose_plugin.py from the nltk project @@ -636,7 +635,7 @@ def check_output(self, want: str, got: str, optionflags: int) -> bool: if not allow_unicode and not allow_bytes and not allow_number: return False - def remove_prefixes(regex: Pattern[str], txt: str) -> str: + def remove_prefixes(regex: re.Pattern[str], txt: str) -> str: return re.sub(regex, r"\1\2", txt) if allow_unicode: diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index af072f0d1a4..d129cd295e7 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -17,7 +17,6 @@ import os import platform import re -from re import Match import xml.etree.ElementTree as ET from _pytest import nodes @@ -48,7 +47,7 @@ def bin_xml_escape(arg: object) -> str: The idea is to escape visually for the user rather than for XML itself. """ - def repl(matchobj: Match[str]) -> str: + def repl(matchobj: re.Match[str]) -> str: i = ord(matchobj.group()) if i <= 0xFF: return f"#x{i:02X}" diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 3a42b43d0a1..f75bb5b432e 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -20,7 +20,7 @@ import itertools import os from pathlib import Path -from re import Pattern +import re import types from typing import Any from typing import final @@ -973,7 +973,7 @@ def _idval_from_value(self, val: object) -> str | None: return _ascii_escaped_by_config(val, self.config) elif val is None or isinstance(val, (float, int, bool, complex)): return str(val) - elif isinstance(val, Pattern): + elif isinstance(val, re.Pattern): return ascii_escaped(val.pattern) elif val is NOTSET: # Fallback to default. Note that NOTSET is an enum.Enum. diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index fb00f1dd749..af13c3dc981 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -12,7 +12,6 @@ from numbers import Complex import pprint import re -from re import Pattern from types import TracebackType from typing import Any from typing import cast @@ -780,7 +779,7 @@ def _as_numpy_array(obj: object) -> ndarray | None: def raises( expected_exception: type[E] | tuple[type[E], ...], *, - match: str | Pattern[str] | None = ..., + match: str | re.Pattern[str] | None = ..., ) -> RaisesContext[E]: ... @@ -955,7 +954,7 @@ def raises( message = f"DID NOT RAISE {expected_exception}" if not args: - match: str | Pattern[str] | None = kwargs.pop("match", None) + match: str | re.Pattern[str] | None = kwargs.pop("match", None) if kwargs: msg = "Unexpected keyword arguments passed to pytest.raises: " msg += ", ".join(sorted(kwargs)) @@ -983,7 +982,7 @@ def __init__( self, expected_exception: type[E] | tuple[type[E], ...], message: str, - match_expr: str | Pattern[str] | None = None, + match_expr: str | re.Pattern[str] | None = None, ) -> None: self.expected_exception = expected_exception self.message = message diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index b168bbe746d..440e3efac8a 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -8,7 +8,6 @@ from collections.abc import Iterator from pprint import pformat import re -from re import Pattern from types import TracebackType from typing import Any from typing import final @@ -44,7 +43,9 @@ def recwarn() -> Generator[WarningsRecorder]: @overload -def deprecated_call(*, match: str | Pattern[str] | None = ...) -> WarningsRecorder: ... +def deprecated_call( + *, match: str | re.Pattern[str] | None = ... +) -> WarningsRecorder: ... @overload @@ -89,7 +90,7 @@ def deprecated_call( def warns( expected_warning: type[Warning] | tuple[type[Warning], ...] = ..., *, - match: str | Pattern[str] | None = ..., + match: str | re.Pattern[str] | None = ..., ) -> WarningsChecker: ... @@ -105,7 +106,7 @@ def warns( def warns( expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning, *args: Any, - match: str | Pattern[str] | None = None, + match: str | re.Pattern[str] | None = None, **kwargs: Any, ) -> WarningsChecker | Any: r"""Assert that code raises a particular class of warning. @@ -258,7 +259,7 @@ class WarningsChecker(WarningsRecorder): def __init__( self, expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning, - match_expr: str | Pattern[str] | None = None, + match_expr: str | re.Pattern[str] | None = None, *, _ispytest: bool = False, ) -> None: From 58e25811b6d8a481c15004c2f0c097db6b24d5ad Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Wed, 20 Nov 2024 22:29:13 +0100 Subject: [PATCH 63/64] [black] Fix the target-version configuration --- pyproject.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6856e652829..c13da163259 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,9 +83,8 @@ scripts.pytest = "pytest:console_main" write_to = "src/_pytest/_version.py" [tool.black] -target-version = [ - 'py39', -] +# See https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#t-target-version +target-version = [ "py39", "py310", "py311", "py312", "py313" ] [tool.ruff] line-length = 88 From 10c0c6271604e339042bbf5dd4f271fe6bfd0f5b Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Thu, 28 Nov 2024 21:12:53 +0100 Subject: [PATCH 64/64] [ruff] Set the target version to python 3.9 --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index c13da163259..dce6a0870e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,6 +87,7 @@ write_to = "src/_pytest/_version.py" target-version = [ "py39", "py310", "py311", "py312", "py313" ] [tool.ruff] +target-version = "py39" line-length = 88 src = [ "src",