From c77a96dc4e1eb629b9b65dd7f8439f5102e1dee1 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 31 Jul 2024 22:34:24 -0300 Subject: [PATCH 01/29] Merge pull request #12675 from nicoddemus/pin-towncrier Pin towncrier due to incompatibility with sphinxcontrib-towncrier (cherry picked from commit f0a0436dd0b2c2fa2a3adea0f4a7cb388ec54af9) --- doc/en/broken-dep-constraints.txt | 6 ++++++ doc/en/requirements.txt | 5 +---- 2 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 doc/en/broken-dep-constraints.txt diff --git a/doc/en/broken-dep-constraints.txt b/doc/en/broken-dep-constraints.txt new file mode 100644 index 00000000000..f7c3211a9a0 --- /dev/null +++ b/doc/en/broken-dep-constraints.txt @@ -0,0 +1,6 @@ +# This file contains transitive dependencies that need to be pinned for some reason. +# Eventually this file will be empty, but in this case keep it around for future use. + +# Pin towncrier temporarily due to incompatibility with sphinxcontrib-towncrier: +# https://github.com/sphinx-contrib/sphinxcontrib-towncrier/issues/92 +towncrier!=24.7.0,!=24.7.1 diff --git a/doc/en/requirements.txt b/doc/en/requirements.txt index 0637c967b8a..ddcb7efb99b 100644 --- a/doc/en/requirements.txt +++ b/doc/en/requirements.txt @@ -1,13 +1,10 @@ +-c broken-dep-constraints.txt pluggy>=1.5.0 pygments-pytest>=2.3.0 sphinx-removed-in>=0.2.0 sphinx>=7 sphinxcontrib-trio sphinxcontrib-svg2pdfconverter -# Pin packaging because it no longer handles 'latest' version, which -# is the version that is assigned to the docs. -# See https://github.com/pytest-dev/pytest/pull/10578#issuecomment-1348249045. -packaging furo sphinxcontrib-towncrier sphinx-issues From 32f995188cf4c7e398bfa734985e7309cd0a8626 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 3 Aug 2024 14:15:52 +0000 Subject: [PATCH 02/29] Merge pull request #12679 from RFCArrow/tmp_path_retention_policy-example (#12683) Remove quotes from example tmp_path_retention_policy (cherry picked from commit dc756f4117f7cbabbe0866bab1fe562ac287cc03) Co-authored-by: Bruno Oliveira --- AUTHORS | 1 + changelog/12678.doc.rst | 1 + doc/en/reference/reference.rst | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changelog/12678.doc.rst diff --git a/AUTHORS b/AUTHORS index 9b6cb6a9d23..8103a1d52a5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -351,6 +351,7 @@ Rafal Semik Raquel Alegre Ravi Chandra Reagan Lee +Rob Arrow Robert Holt Roberto Aldera Roberto Polli diff --git a/changelog/12678.doc.rst b/changelog/12678.doc.rst new file mode 100644 index 00000000000..9d79ec41007 --- /dev/null +++ b/changelog/12678.doc.rst @@ -0,0 +1 @@ +Remove erroneous quotes from `tmp_path_retention_policy` example in docs. diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 099c8a00260..dbb5cc36192 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -1866,7 +1866,7 @@ passed multiple times. The expected format is ``name=value``. For example:: .. code-block:: ini [pytest] - tmp_path_retention_policy = "all" + tmp_path_retention_policy = all Default: ``all`` From dcff37ae9e9b6760e0863d156493372978ce6512 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 13:58:40 +0000 Subject: [PATCH 03/29] Obey verbosity level when printing 'msg' part of assertions (#12662) (#12687) Seems like we just missed that case when more fine-grained verbosity levels were added. Fixes #6682, #12307 (cherry picked from commit bc1e17e90c5324797b4ebc5fd229d2662ad15e6e) Co-authored-by: Bruno Oliveira --- changelog/6682.bugfix.rst | 1 + src/_pytest/assertion/rewrite.py | 2 +- testing/test_assertrewrite.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 changelog/6682.bugfix.rst diff --git a/changelog/6682.bugfix.rst b/changelog/6682.bugfix.rst new file mode 100644 index 00000000000..7f756cbd9c2 --- /dev/null +++ b/changelog/6682.bugfix.rst @@ -0,0 +1 @@ +Fixed bug where the verbosity levels where not being respected when printing the "msg" part of failed assertion (as in ``assert condition, msg``). diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index bfcbcbd3f8d..f7ff4f6f7a2 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -451,7 +451,7 @@ def _format_assertmsg(obj: object) -> str: # However in either case we want to preserve the newline. replaces = [("\n", "\n~"), ("%", "%%")] if not isinstance(obj, str): - obj = saferepr(obj) + obj = saferepr(obj, _get_maxsize_for_saferepr(util._config)) replaces.append(("\\n", "\n~")) for r1, r2 in replaces: diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 5ee40ee6568..11688acb6f5 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -341,6 +341,34 @@ def test_assertion_messages_bytes(self, pytester: Pytester) -> None: assert result.ret == 1 result.stdout.fnmatch_lines(["*AssertionError: b'ohai!'", "*assert False"]) + def test_assertion_message_verbosity(self, pytester: Pytester) -> None: + """ + Obey verbosity levels when printing the "message" part of assertions, when they are + non-strings (#6682). + """ + pytester.makepyfile( + """ + class LongRepr: + + def __repr__(self): + return "A" * 500 + + def test_assertion_verbosity(): + assert False, LongRepr() + """ + ) + # Normal verbosity: assertion message gets abbreviated. + result = pytester.runpytest() + assert result.ret == 1 + result.stdout.re_match_lines( + [r".*AssertionError: A+\.\.\.A+$", ".*assert False"] + ) + + # High-verbosity: do not abbreviate the assertion message. + result = pytester.runpytest("-vv") + assert result.ret == 1 + result.stdout.re_match_lines([r".*AssertionError: A+$", ".*assert False"]) + def test_boolop(self) -> None: def f1() -> None: f = g = False From de6824fa7417e580c890b057c544717e6b2a3835 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:01:33 +0000 Subject: [PATCH 04/29] Add merge/squash guidelines to CONTRIBUTING.rst (#12672) (#12692) As discussed in https://github.com/pytest-dev/pytest/discussions/12633. (cherry picked from commit 2b99703acace3194a7e28c05a097d06d29949aa9) Co-authored-by: Bruno Oliveira --- CONTRIBUTING.rst | 55 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 12e2b18bb52..6e96fd24c40 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -380,6 +380,57 @@ pull requests from other contributors yourself after having reviewed them. +Merge/squash guidelines +----------------------- + +When a PR is approved and ready to be integrated to the ``main`` branch, one has the option to *merge* the commits unchanged, or *squash* all the commits into a single commit. + +Here are some guidelines on how to proceed, based on examples of a single PR commit history: + +1. Miscellaneous commits: + + * ``Implement X`` + * ``Fix test_a`` + * ``Add myself to AUTHORS`` + * ``fixup! Fix test_a`` + * ``Update tests/test_integration.py`` + * ``Merge origin/main into PR branch`` + * ``Update tests/test_integration.py`` + + In this case, prefer to use the **Squash** merge strategy: the commit history is a bit messy (not in a derogatory way, often one just commits changes because they know the changes will eventually be squashed together), so squashing everything into a single commit is best. You must clean up the commit message, making sure it contains useful details. + +2. Separate commits related to the same topic: + + * ``Implement X`` + * ``Add myself to AUTHORS`` + * ``Update CHANGELOG for X`` + + In this case, prefer to use the **Squash** merge strategy: while the commit history is not "messy" as in the example above, the individual commits do not bring much value overall, specially when looking at the changes a few months/years down the line. + +3. Separate commits, each with their own topic (refactorings, renames, etc), but still have a larger topic/purpose. + + * ``Refactor class X in preparation for feature Y`` + * ``Remove unused method`` + * ``Implement feature Y`` + + In this case, prefer to use the **Merge** strategy: each commit is valuable on its own, even if they serve a common topic overall. Looking at the history later, it is useful to have the removal of the unused method separately on its own commit, along with more information (such as how it became unused in the first place). + +4. Separate commits, each with their own topic, but without a larger topic/purpose other than improve the code base (using more modern techniques, improve typing, removing clutter, etc). + + * ``Improve internal names in X`` + * ``Add type annotations to Y`` + * ``Remove unnecessary dict access`` + * ``Remove unreachable code due to EOL Python`` + + In this case, prefer to use the **Merge** strategy: each commit is valuable on its own, and the information on each is valuable in the long term. + + +As mentioned, those are overall guidelines, not rules cast in stone. This topic was discussed in `#12633 `_. + + +*Backport PRs* (as those created automatically from a ``backport`` label) should always be **squashed**, as they preserve the original PR author. + + Backporting bug fixes for the next patch release ------------------------------------------------ @@ -438,6 +489,8 @@ above? All the above are not rules, but merely some guidelines/suggestions on what we should expect about backports. +Backports should be **squashed** (rather than **merged**), as doing so preserves the original PR author correctly. + Handling stale issues/PRs ------------------------- @@ -485,7 +538,7 @@ When closing a Pull Request, it needs to be acknowledging the time, effort, and -Closing Issues +Closing issues -------------- When a pull request is submitted to fix an issue, add text like ``closes #XYZW`` to the PR description and/or commits (where ``XYZW`` is the issue number). See the `GitHub docs `_ for more information. From ae7959a2a72e43d6c97a47450eac3011e2519184 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 17:20:18 +0000 Subject: [PATCH 05/29] Add Pierre to official contacts (#12694) (#12695) As part of onboarding Pierre to the core team. Also add contacts in alphabetical order (cherry picked from commit a01dca5321c899dd01f01c6ff698af87d86b9fd3) Co-authored-by: Bruno Oliveira --- doc/en/contact.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/en/contact.rst b/doc/en/contact.rst index ef9d1e8edca..d650a7efbab 100644 --- a/doc/en/contact.rst +++ b/doc/en/contact.rst @@ -33,10 +33,11 @@ Mail discussed in public. Mails sent there will be distributed among the members in the pytest core team, who can also be contacted individually: - * Ronny Pfannschmidt (:user:`RonnyPfannschmidt`, `ronny@pytest.org `_) - * Florian Bruhin (:user:`The-Compiler`, `florian@pytest.org `_) * Bruno Oliveira (:user:`nicoddemus`, `bruno@pytest.org `_) + * Florian Bruhin (:user:`The-Compiler`, `florian@pytest.org `_) + * Pierre Sassoulas (:user:`Pierre-Sassoulas`, `pierre@pytest.org `_) * Ran Benita (:user:`bluetech`, `ran@pytest.org `_) + * Ronny Pfannschmidt (:user:`RonnyPfannschmidt`, `ronny@pytest.org `_) * Zac Hatfield-Dodds (:user:`Zac-HD`, `zac@pytest.org `_) Other From 74caad915d9fbf8f87ade5a1bd0d402f2567cc11 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 15:40:34 +0000 Subject: [PATCH 06/29] docs: Revise `cache_dir` option section (#12701) (#12703) Update the description for the `cache_dir` configuration options section to improve semantics and clarity for an unclear sentence in the file `reference.rst`. (cherry picked from commit 49eafce55bf31c1993db9f411bc2d669804d5bb3) Co-authored-by: Eugene Mwangi --- doc/en/reference/reference.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index dbb5cc36192..f1110097ecc 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -1287,10 +1287,10 @@ passed multiple times. The expected format is ``name=value``. For example:: .. confval:: cache_dir - Sets a directory where stores content of cache plugin. Default directory is + Sets the directory where the cache plugin's content is stored. Default directory is ``.pytest_cache`` which is created in :ref:`rootdir `. Directory may be relative or absolute path. If setting relative path, then directory is created - relative to :ref:`rootdir `. Additionally path may contain environment + relative to :ref:`rootdir `. Additionally, a path may contain environment variables, that will be expanded. For more information about cache plugin please refer to :ref:`cache_provider`. From 8fb7faed48b1dcd804dd1a99dd7bd3dd0f180c68 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 15:43:07 +0000 Subject: [PATCH 07/29] Fix `errisinstance` typing (#12700) (#12704) Fixes #12667 (cherry picked from commit cb98538e9c2ba3c65baf07432229d97fd5a29657) Co-authored-by: Reagan Lee <96998476+reaganjlee@users.noreply.github.com> --- changelog/12667.bugfix.rst | 1 + src/_pytest/_code/code.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog/12667.bugfix.rst diff --git a/changelog/12667.bugfix.rst b/changelog/12667.bugfix.rst new file mode 100644 index 00000000000..eceee86cb53 --- /dev/null +++ b/changelog/12667.bugfix.rst @@ -0,0 +1 @@ +Fixed a regression where type change in `ExceptionInfo.errisinstance` caused `mypy` to fail. diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index e7452825756..8fac39ea298 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -56,7 +56,7 @@ TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"] -EXCEPTION_OR_MORE = Union[Type[Exception], Tuple[Type[Exception], ...]] +EXCEPTION_OR_MORE = Union[Type[BaseException], Tuple[Type[BaseException], ...]] class Code: From 6d59143aa4170a9174d253adc826043eb7545788 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:13:28 +0000 Subject: [PATCH 08/29] docs: clarify `pytest_deselected`/`pytest_collection_modifyitems` usage (#12729) (#12733) Closes #12663 (cherry picked from commit 51845fc70dba0fba27387e21e2db39d583892dec) Co-authored-by: Stefaan Lippens --- changelog/12663.doc.rst | 1 + src/_pytest/hookspec.py | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 changelog/12663.doc.rst diff --git a/changelog/12663.doc.rst b/changelog/12663.doc.rst new file mode 100644 index 00000000000..e665bc67a10 --- /dev/null +++ b/changelog/12663.doc.rst @@ -0,0 +1 @@ +Clarify that the `pytest_deselected` hook should be called from `pytest_collection_modifyitems` hook implementations when items are deselected. diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 99614899994..0a41b0aca47 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -274,6 +274,11 @@ def pytest_collection_modifyitems( """Called after collection has been performed. May filter or re-order the items in-place. + When items are deselected (filtered out from ``items``), + the hook :hook:`pytest_deselected` must be called explicitly + with the deselected items to properly notify other plugins, + e.g. with ``config.hook.pytest_deselected(deselected_items)``. + :param session: The pytest session object. :param config: The pytest config object. :param items: List of item objects. @@ -454,6 +459,12 @@ def pytest_collectreport(report: CollectReport) -> None: def pytest_deselected(items: Sequence[Item]) -> None: """Called for deselected test items, e.g. by keyword. + Note that this hook has two integration aspects for plugins: + + - it can be *implemented* to be notified of deselected items + - it must be *called* from :hook:`pytest_collection_modifyitems` + implementations when items are deselected (to properly notify other plugins). + May be called multiple times. :param items: From 2d5618ec4e9499ba4f39131f1fb8423aa89d1d4b Mon Sep 17 00:00:00 2001 From: Avasam Date: Thu, 29 Aug 2024 07:54:47 -0400 Subject: [PATCH 09/29] Replaced `typing.Self` with `typing_extensions.Self` (#12744) Fix incorrect Self import from typing instead of typing_extensions. --------- Co-authored-by: Bruno Oliveira (cherry picked from commit c947145fbb4aeec810a259b19f70fcb52fd53ad4) --- changelog/12744.bugfix.rst | 1 + src/_pytest/doctest.py | 3 ++- src/_pytest/nodes.py | 2 +- src/_pytest/python.py | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 changelog/12744.bugfix.rst diff --git a/changelog/12744.bugfix.rst b/changelog/12744.bugfix.rst new file mode 100644 index 00000000000..7814b59c180 --- /dev/null +++ b/changelog/12744.bugfix.rst @@ -0,0 +1 @@ +Fixed typing compatibility with Python 3.9 or less -- replaced `typing.Self` with `typing_extensions.Self` -- by :user:`Avasam` diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index cb46d9a3bb5..7f09e1794ac 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -44,7 +44,8 @@ if TYPE_CHECKING: import doctest - from typing import Self + + from typing_extensions import Self DOCTEST_REPORT_CHOICE_NONE = "none" DOCTEST_REPORT_CHOICE_CDIFF = "cdiff" diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index bbde2664b90..cc678695848 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -43,7 +43,7 @@ if TYPE_CHECKING: - from typing import Self + from typing_extensions import Self # Imported here due to circular import. from _pytest.main import Session diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 9182ce7dfe9..3a2c4d0d1d3 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -78,7 +78,7 @@ if TYPE_CHECKING: - from typing import Self + from typing_extensions import Self def pytest_addoption(parser: Parser) -> None: From cafc729ae1835f68c600d25f9709787600f78398 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 22:33:00 +0000 Subject: [PATCH 10/29] [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.5.2 → v0.5.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.2...v0.5.4) - [github.com/pre-commit/mirrors-mypy: v1.10.1 → v1.11.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.1...v1.11.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 419addd95be..79697cf1caa 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.5.2" + rev: "v0.5.4" hooks: - id: ruff args: ["--fix"] @@ -21,7 +21,7 @@ repos: hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.1 + rev: v1.11.0 hooks: - id: mypy files: ^(src/|testing/|scripts/) From c185df620b3f26379116713c3d5bd998fa79591f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2024 07:25:50 +0200 Subject: [PATCH 11/29] [pre-commit.ci] pre-commit autoupdate (#12669) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.4 → v0.5.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.4...v0.5.5) - [github.com/asottile/pyupgrade: v3.16.0 → v3.17.0](https://github.com/asottile/pyupgrade/compare/v3.16.0...v3.17.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 79697cf1caa..aaa28d6b7c4 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.5.4" + rev: "v0.5.5" hooks: - id: ruff args: ["--fix"] @@ -44,7 +44,7 @@ repos: # 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.16.0 + rev: v3.17.0 hooks: - id: pyupgrade stages: [manual] From 3b829563ab76455bb01c874dca64840a8ff20dc3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 23:10:25 +0000 Subject: [PATCH 12/29] [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.5.5 → v0.5.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.5...v0.5.6) - [github.com/pre-commit/mirrors-mypy: v1.11.0 → v1.11.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.0...v1.11.1) - [github.com/tox-dev/pyproject-fmt: 2.1.4 → 2.2.1](https://github.com/tox-dev/pyproject-fmt/compare/2.1.4...2.2.1) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index aaa28d6b7c4..5348fdde93d 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.5.5" + rev: "v0.5.6" hooks: - id: ruff args: ["--fix"] @@ -21,7 +21,7 @@ repos: hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.0 + rev: v1.11.1 hooks: - id: mypy files: ^(src/|testing/|scripts/) @@ -38,7 +38,7 @@ repos: # on <3.11 - exceptiongroup>=1.0.0rc8 - repo: https://github.com/tox-dev/pyproject-fmt - rev: "2.1.4" + rev: "2.2.1" hooks: - id: pyproject-fmt # https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version From 0765c55658e2d18dce3c86511273e55bd8698652 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 7 Aug 2024 08:42:58 -0300 Subject: [PATCH 13/29] Replace types-pkg-resources by types-setuptools According to https://pypi.org/project/types-pkg-resources/#history, that package has been supplanted by `types-setuptools`. --- .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 5348fdde93d..f27c46f3a54 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,7 +32,7 @@ repos: - pluggy>=1.5.0 - packaging - tomli - - types-pkg_resources + - types-setuptools - types-tabulate # for mypy running on python>=3.11 since exceptiongroup is only a dependency # on <3.11 From d2bb828eef219cd7c7581493d0ad4f81eb18799e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 21:53:50 +0200 Subject: [PATCH 14/29] [pre-commit.ci] pre-commit autoupdate (#12712) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.6 → v0.5.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.6...v0.5.7) * Apply pyupgrade latest changes Co-authored-by: Pierre Sassoulas --- .pre-commit-config.yaml | 2 +- src/_pytest/cacheprovider.py | 6 ++---- src/_pytest/capture.py | 22 +++++++++++----------- src/_pytest/doctest.py | 2 +- src/_pytest/logging.py | 22 ++++++++++------------ src/_pytest/monkeypatch.py | 4 ++-- src/_pytest/pytester.py | 4 ++-- src/_pytest/python.py | 8 ++++---- src/_pytest/recwarn.py | 2 +- src/_pytest/skipping.py | 2 +- src/_pytest/terminal.py | 4 ++-- src/_pytest/threadexception.py | 8 ++++---- src/_pytest/tmpdir.py | 2 +- src/_pytest/unittest.py | 4 ++-- src/_pytest/unraisableexception.py | 8 ++++---- src/_pytest/warnings.py | 8 ++++---- testing/conftest.py | 2 +- testing/io/test_terminalwriter.py | 4 +--- testing/test_assertrewrite.py | 2 +- testing/test_cacheprovider.py | 2 +- testing/test_capture.py | 2 +- testing/test_conftest.py | 4 +--- testing/test_monkeypatch.py | 2 +- testing/test_pathlib.py | 2 +- 24 files changed, 60 insertions(+), 68 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f27c46f3a54..0e36d1e6137 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.5.6" + rev: "v0.5.7" hooks: - id: ruff args: ["--fix"] diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 20bb262e05d..7190810570e 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -369,7 +369,7 @@ def pytest_collectreport(self, report: CollectReport) -> None: @hookimpl(wrapper=True, tryfirst=True) def pytest_collection_modifyitems( self, config: Config, items: list[nodes.Item] - ) -> Generator[None, None, None]: + ) -> Generator[None]: res = yield if not self.active: @@ -439,9 +439,7 @@ def __init__(self, config: Config) -> None: self.cached_nodeids = set(config.cache.get("cache/nodeids", [])) @hookimpl(wrapper=True, tryfirst=True) - def pytest_collection_modifyitems( - self, items: list[nodes.Item] - ) -> Generator[None, None, None]: + def pytest_collection_modifyitems(self, items: list[nodes.Item]) -> Generator[None]: res = yield if self.active: diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index c4dfcc27552..f4586a42a69 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -135,7 +135,7 @@ def _reopen_stdio(f, mode): @hookimpl(wrapper=True) -def pytest_load_initial_conftests(early_config: Config) -> Generator[None, None, None]: +def pytest_load_initial_conftests(early_config: Config) -> Generator[None]: ns = early_config.known_args_namespace if ns.capture == "fd": _windowsconsoleio_workaround(sys.stdout) @@ -817,7 +817,7 @@ def resume_fixture(self) -> None: # Helper context managers @contextlib.contextmanager - def global_and_fixture_disabled(self) -> Generator[None, None, None]: + def global_and_fixture_disabled(self) -> Generator[None]: """Context manager to temporarily disable global and current fixture capturing.""" do_fixture = self._capture_fixture and self._capture_fixture._is_started() if do_fixture: @@ -834,7 +834,7 @@ def global_and_fixture_disabled(self) -> Generator[None, None, None]: self.resume_fixture() @contextlib.contextmanager - def item_capture(self, when: str, item: Item) -> Generator[None, None, None]: + def item_capture(self, when: str, item: Item) -> Generator[None]: self.resume_global_capture() self.activate_fixture() try: @@ -869,17 +869,17 @@ def pytest_make_collect_report( return rep @hookimpl(wrapper=True) - def pytest_runtest_setup(self, item: Item) -> Generator[None, None, None]: + def pytest_runtest_setup(self, item: Item) -> Generator[None]: with self.item_capture("setup", item): return (yield) @hookimpl(wrapper=True) - def pytest_runtest_call(self, item: Item) -> Generator[None, None, None]: + def pytest_runtest_call(self, item: Item) -> Generator[None]: with self.item_capture("call", item): return (yield) @hookimpl(wrapper=True) - def pytest_runtest_teardown(self, item: Item) -> Generator[None, None, None]: + def pytest_runtest_teardown(self, item: Item) -> Generator[None]: with self.item_capture("teardown", item): return (yield) @@ -961,7 +961,7 @@ def _is_started(self) -> bool: return False @contextlib.contextmanager - def disabled(self) -> Generator[None, None, None]: + def disabled(self) -> Generator[None]: """Temporarily disable capturing while inside the ``with`` block.""" capmanager: CaptureManager = self.request.config.pluginmanager.getplugin( "capturemanager" @@ -974,7 +974,7 @@ def disabled(self) -> Generator[None, None, None]: @fixture -def capsys(request: SubRequest) -> Generator[CaptureFixture[str], None, None]: +def capsys(request: SubRequest) -> Generator[CaptureFixture[str]]: r"""Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``. The captured output is made available via ``capsys.readouterr()`` method @@ -1002,7 +1002,7 @@ def test_output(capsys): @fixture -def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, None]: +def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes]]: r"""Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``. The captured output is made available via ``capsysbinary.readouterr()`` @@ -1030,7 +1030,7 @@ def test_output(capsysbinary): @fixture -def capfd(request: SubRequest) -> Generator[CaptureFixture[str], None, None]: +def capfd(request: SubRequest) -> Generator[CaptureFixture[str]]: r"""Enable text capturing of writes to file descriptors ``1`` and ``2``. The captured output is made available via ``capfd.readouterr()`` method @@ -1058,7 +1058,7 @@ def test_system_echo(capfd): @fixture -def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, None]: +def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes]]: r"""Enable bytes capturing of writes to file descriptors ``1`` and ``2``. The captured output is made available via ``capfd.readouterr()`` method diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 7f09e1794ac..384dea976ad 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -468,7 +468,7 @@ def _is_mocked(obj: object) -> bool: @contextmanager -def _patch_unwrap_mock_aware() -> Generator[None, None, None]: +def _patch_unwrap_mock_aware() -> Generator[None]: """Context manager which replaces ``inspect.unwrap`` with a version that's aware of mock objects and doesn't recurse into them.""" real_unwrap = inspect.unwrap diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 44af8ff2041..9f3417e9af2 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -554,9 +554,7 @@ def set_level(self, level: int | str, logger: str | None = None) -> None: self._initial_disabled_logging_level = initial_disabled_logging_level @contextmanager - def at_level( - self, level: int | str, logger: str | None = None - ) -> Generator[None, None, None]: + def at_level(self, level: int | str, logger: str | None = None) -> Generator[None]: """Context manager that sets the level for capturing of logs. After the end of the 'with' statement the level is restored to its original value. @@ -580,7 +578,7 @@ def at_level( logging.disable(original_disable_level) @contextmanager - def filtering(self, filter_: logging.Filter) -> Generator[None, None, None]: + def filtering(self, filter_: logging.Filter) -> Generator[None]: """Context manager that temporarily adds the given filter to the caplog's :meth:`handler` for the 'with' statement block, and removes that filter at the end of the block. @@ -597,7 +595,7 @@ def filtering(self, filter_: logging.Filter) -> Generator[None, None, None]: @fixture -def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture, None, None]: +def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture]: """Access and control log capturing. Captured logs are available through the following properties/methods:: @@ -776,7 +774,7 @@ def _log_cli_enabled(self) -> bool: return True @hookimpl(wrapper=True, tryfirst=True) - def pytest_sessionstart(self) -> Generator[None, None, None]: + def pytest_sessionstart(self) -> Generator[None]: self.log_cli_handler.set_when("sessionstart") with catching_logs(self.log_cli_handler, level=self.log_cli_level): @@ -784,7 +782,7 @@ def pytest_sessionstart(self) -> Generator[None, None, None]: return (yield) @hookimpl(wrapper=True, tryfirst=True) - def pytest_collection(self) -> Generator[None, None, None]: + def pytest_collection(self) -> Generator[None]: self.log_cli_handler.set_when("collection") with catching_logs(self.log_cli_handler, level=self.log_cli_level): @@ -813,7 +811,7 @@ def pytest_runtest_logstart(self) -> None: def pytest_runtest_logreport(self) -> None: self.log_cli_handler.set_when("logreport") - def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None, None, 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, @@ -834,7 +832,7 @@ def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None, None, Non item.add_report_section(when, "log", log) @hookimpl(wrapper=True) - def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None, None, None]: + def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None]: self.log_cli_handler.set_when("setup") empty: dict[str, list[logging.LogRecord]] = {} @@ -842,13 +840,13 @@ def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None, None, None]: yield from self._runtest_for(item, "setup") @hookimpl(wrapper=True) - def pytest_runtest_call(self, item: nodes.Item) -> Generator[None, None, None]: + def pytest_runtest_call(self, item: nodes.Item) -> Generator[None]: self.log_cli_handler.set_when("call") yield from self._runtest_for(item, "call") @hookimpl(wrapper=True) - def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None, None, None]: + def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None]: self.log_cli_handler.set_when("teardown") try: @@ -862,7 +860,7 @@ def pytest_runtest_logfinish(self) -> None: self.log_cli_handler.set_when("finish") @hookimpl(wrapper=True, tryfirst=True) - def pytest_sessionfinish(self) -> Generator[None, None, None]: + def pytest_sessionfinish(self) -> Generator[None]: self.log_cli_handler.set_when("sessionfinish") with catching_logs(self.log_cli_handler, level=self.log_cli_level): diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index 75b019a3be6..46eb1724e35 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -28,7 +28,7 @@ @fixture -def monkeypatch() -> Generator[MonkeyPatch, None, None]: +def monkeypatch() -> Generator[MonkeyPatch]: """A convenient fixture for monkey-patching. The fixture provides these methods to modify objects, dictionaries, or @@ -135,7 +135,7 @@ def __init__(self) -> None: @classmethod @contextmanager - def context(cls) -> Generator[MonkeyPatch, None, None]: + def context(cls) -> Generator[MonkeyPatch]: """Context manager that returns a new :class:`MonkeyPatch` object which undoes any patching done inside the ``with`` block upon exit. diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 5c6ce5e889f..3f7520ee4ad 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -491,7 +491,7 @@ def pytester( @fixture -def _sys_snapshot() -> Generator[None, None, None]: +def _sys_snapshot() -> Generator[None]: snappaths = SysPathsSnapshot() snapmods = SysModulesSnapshot() yield @@ -500,7 +500,7 @@ def _sys_snapshot() -> Generator[None, None, None]: @fixture -def _config_for_test() -> Generator[Config, None, None]: +def _config_for_test() -> Generator[Config]: from _pytest.config import get_config config = get_config() diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 3a2c4d0d1d3..b2199c76fc6 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -568,7 +568,7 @@ def _register_setup_module_fixture(self) -> None: if setup_module is None and teardown_module is None: return - def xunit_setup_module_fixture(request) -> Generator[None, None, None]: + def xunit_setup_module_fixture(request) -> Generator[None]: module = request.module if setup_module is not None: _call_with_optional_argument(setup_module, module) @@ -599,7 +599,7 @@ def _register_setup_function_fixture(self) -> None: if setup_function is None and teardown_function is None: return - def xunit_setup_function_fixture(request) -> Generator[None, None, None]: + def xunit_setup_function_fixture(request) -> Generator[None]: if request.instance is not None: # in this case we are bound to an instance, so we need to let # setup_method handle this @@ -780,7 +780,7 @@ def _register_setup_class_fixture(self) -> None: if setup_class is None and teardown_class is None: return - def xunit_setup_class_fixture(request) -> Generator[None, None, None]: + def xunit_setup_class_fixture(request) -> Generator[None]: cls = request.cls if setup_class is not None: func = getimfunc(setup_class) @@ -813,7 +813,7 @@ def _register_setup_method_fixture(self) -> None: if setup_method is None and teardown_method is None: return - def xunit_setup_method_fixture(request) -> Generator[None, None, None]: + def xunit_setup_method_fixture(request) -> Generator[None]: instance = request.instance method = request.function if setup_method is not None: diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 3fc00d94736..85d8de84abb 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -32,7 +32,7 @@ @fixture -def recwarn() -> Generator[WarningsRecorder, None, None]: +def recwarn() -> Generator[WarningsRecorder]: """Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions. See https://docs.pytest.org/en/latest/how-to/capture-warnings.html for information diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index 08fcb283eb2..9818be2ab03 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -245,7 +245,7 @@ def pytest_runtest_setup(item: Item) -> None: @hookimpl(wrapper=True) -def pytest_runtest_call(item: Item) -> Generator[None, None, None]: +def pytest_runtest_call(item: Item) -> Generator[None]: xfailed = item.stash.get(xfailed_key, None) if xfailed is None: item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 8c722124d04..ed267bf5bfd 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -889,7 +889,7 @@ def _printcollecteditems(self, items: Sequence[Item]) -> None: @hookimpl(wrapper=True) def pytest_sessionfinish( self, session: Session, exitstatus: int | ExitCode - ) -> Generator[None, None, None]: + ) -> Generator[None]: result = yield self._tw.line("") summary_exit_codes = ( @@ -914,7 +914,7 @@ def pytest_sessionfinish( return result @hookimpl(wrapper=True) - def pytest_terminal_summary(self) -> Generator[None, None, None]: + def pytest_terminal_summary(self) -> Generator[None]: self.summary_errors() self.summary_failures() self.summary_xfailures() diff --git a/src/_pytest/threadexception.py b/src/_pytest/threadexception.py index d78c32c852f..c1ed80387aa 100644 --- a/src/_pytest/threadexception.py +++ b/src/_pytest/threadexception.py @@ -62,7 +62,7 @@ def __exit__( del self.args -def thread_exception_runtest_hook() -> Generator[None, None, None]: +def thread_exception_runtest_hook() -> Generator[None]: with catch_threading_exception() as cm: try: yield @@ -83,15 +83,15 @@ def thread_exception_runtest_hook() -> Generator[None, None, None]: @pytest.hookimpl(wrapper=True, trylast=True) -def pytest_runtest_setup() -> Generator[None, None, None]: +def pytest_runtest_setup() -> Generator[None]: yield from thread_exception_runtest_hook() @pytest.hookimpl(wrapper=True, tryfirst=True) -def pytest_runtest_call() -> Generator[None, None, None]: +def pytest_runtest_call() -> Generator[None]: yield from thread_exception_runtest_hook() @pytest.hookimpl(wrapper=True, tryfirst=True) -def pytest_runtest_teardown() -> Generator[None, None, None]: +def pytest_runtest_teardown() -> Generator[None]: yield from thread_exception_runtest_hook() diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 91109ea69ef..de0cbcfeb1c 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -256,7 +256,7 @@ def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path: @fixture def tmp_path( request: FixtureRequest, tmp_path_factory: TempPathFactory -) -> Generator[Path, None, None]: +) -> Generator[Path]: """Return a temporary directory path object which is unique to each test function invocation, created as a sub directory of the base temporary directory. diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index aefea1333d9..8cecd4f9339 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -137,7 +137,7 @@ def process_teardown_exceptions() -> None: def unittest_setup_class_fixture( request: FixtureRequest, - ) -> Generator[None, None, None]: + ) -> Generator[None]: cls = request.cls if _is_skipped(cls): reason = cls.__unittest_skip_why__ @@ -178,7 +178,7 @@ def _register_unittest_setup_method_fixture(self, cls: type) -> None: def unittest_setup_method_fixture( request: FixtureRequest, - ) -> Generator[None, None, None]: + ) -> Generator[None]: self = request.instance if _is_skipped(self): reason = self.__unittest_skip_why__ diff --git a/src/_pytest/unraisableexception.py b/src/_pytest/unraisableexception.py index c191703a3de..77a2de20041 100644 --- a/src/_pytest/unraisableexception.py +++ b/src/_pytest/unraisableexception.py @@ -64,7 +64,7 @@ def __exit__( del self.unraisable -def unraisable_exception_runtest_hook() -> Generator[None, None, None]: +def unraisable_exception_runtest_hook() -> Generator[None]: with catch_unraisable_exception() as cm: try: yield @@ -86,15 +86,15 @@ def unraisable_exception_runtest_hook() -> Generator[None, None, None]: @pytest.hookimpl(wrapper=True, tryfirst=True) -def pytest_runtest_setup() -> Generator[None, None, None]: +def pytest_runtest_setup() -> Generator[None]: yield from unraisable_exception_runtest_hook() @pytest.hookimpl(wrapper=True, tryfirst=True) -def pytest_runtest_call() -> Generator[None, None, None]: +def pytest_runtest_call() -> Generator[None]: yield from unraisable_exception_runtest_hook() @pytest.hookimpl(wrapper=True, tryfirst=True) -def pytest_runtest_teardown() -> Generator[None, None, None]: +def pytest_runtest_teardown() -> Generator[None]: yield from unraisable_exception_runtest_hook() diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index 5c59e55c5db..eeb4772649d 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -30,7 +30,7 @@ def catch_warnings_for_item( ihook, when: Literal["config", "collect", "runtest"], item: Item | None, -) -> Generator[None, None, None]: +) -> Generator[None]: """Context manager that catches warnings generated in the contained execution block. ``item`` can be None if we are not in the context of an item execution. @@ -124,7 +124,7 @@ def pytest_collection(session: Session) -> Generator[None, object, object]: @pytest.hookimpl(wrapper=True) def pytest_terminal_summary( terminalreporter: TerminalReporter, -) -> Generator[None, None, None]: +) -> Generator[None]: config = terminalreporter.config with catch_warnings_for_item( config=config, ihook=config.hook, when="config", item=None @@ -133,7 +133,7 @@ def pytest_terminal_summary( @pytest.hookimpl(wrapper=True) -def pytest_sessionfinish(session: Session) -> Generator[None, None, None]: +def pytest_sessionfinish(session: Session) -> Generator[None]: config = session.config with catch_warnings_for_item( config=config, ihook=config.hook, when="config", item=None @@ -144,7 +144,7 @@ def pytest_sessionfinish(session: Session) -> Generator[None, None, None]: @pytest.hookimpl(wrapper=True) def pytest_load_initial_conftests( early_config: Config, -) -> Generator[None, None, None]: +) -> Generator[None]: with catch_warnings_for_item( config=early_config, ihook=early_config.hook, when="config", item=None ): diff --git a/testing/conftest.py b/testing/conftest.py index 24e5d183094..046bb77a109 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -46,7 +46,7 @@ def reset_colors(monkeypatch: pytest.MonkeyPatch) -> None: @pytest.hookimpl(wrapper=True, tryfirst=True) -def pytest_collection_modifyitems(items) -> Generator[None, None, None]: +def pytest_collection_modifyitems(items) -> Generator[None]: """Prefer faster tests. Use a hook wrapper to do this in the beginning, so e.g. --ff still works diff --git a/testing/io/test_terminalwriter.py b/testing/io/test_terminalwriter.py index 043c2d1d904..92cde240a11 100644 --- a/testing/io/test_terminalwriter.py +++ b/testing/io/test_terminalwriter.py @@ -67,9 +67,7 @@ def test_terminalwriter_not_unicode() -> None: class TestTerminalWriter: @pytest.fixture(params=["path", "stringio"]) - def tw( - self, request, tmp_path: Path - ) -> Generator[terminalwriter.TerminalWriter, None, None]: + def tw(self, request, tmp_path: Path) -> Generator[terminalwriter.TerminalWriter]: if request.param == "path": p = tmp_path.joinpath("tmpfile") f = open(str(p), "w+", encoding="utf8") diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 11688acb6f5..73c11a1a9d8 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1660,7 +1660,7 @@ class TestEarlyRewriteBailout: @pytest.fixture def hook( self, pytestconfig, monkeypatch, pytester: Pytester - ) -> Generator[AssertionRewritingHook, None, None]: + ) -> Generator[AssertionRewritingHook]: """Returns a patched AssertionRewritingHook instance so we can configure its initial paths and track if PathFinder.find_spec has been called. """ diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 72b4265cf75..94bc55d3047 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -69,7 +69,7 @@ def test_cache_writefail_cachefile_silent(self, pytester: Pytester) -> None: cache.set("test/broken", []) @pytest.fixture - def unwritable_cache_dir(self, pytester: Pytester) -> Generator[Path, None, None]: + def unwritable_cache_dir(self, pytester: Pytester) -> Generator[Path]: cache_dir = pytester.path.joinpath(".pytest_cache") cache_dir.mkdir() mode = cache_dir.stat().st_mode diff --git a/testing/test_capture.py b/testing/test_capture.py index fe6bd7d14fa..328de740e8a 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -939,7 +939,7 @@ def test_captureresult() -> None: @pytest.fixture -def tmpfile(pytester: Pytester) -> Generator[BinaryIO, None, None]: +def tmpfile(pytester: Pytester) -> Generator[BinaryIO]: f = pytester.makepyfile("").open("wb+") yield f if not f.closed: diff --git a/testing/test_conftest.py b/testing/test_conftest.py index d51846f2f5f..ea60c1909c2 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -44,9 +44,7 @@ def conftest_setinitial( @pytest.mark.usefixtures("_sys_snapshot") class TestConftestValueAccessGlobal: @pytest.fixture(scope="module", params=["global", "inpackage"]) - def basedir( - self, request, tmp_path_factory: TempPathFactory - ) -> Generator[Path, None, None]: + def basedir(self, request, tmp_path_factory: TempPathFactory) -> Generator[Path]: tmp_path = tmp_path_factory.mktemp("basedir", numbered=True) tmp_path.joinpath("adir/b").mkdir(parents=True) tmp_path.joinpath("adir/conftest.py").write_text( diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index 079d8ff60ad..1a429e3e681 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -14,7 +14,7 @@ @pytest.fixture -def mp() -> Generator[MonkeyPatch, None, None]: +def mp() -> Generator[MonkeyPatch]: cwd = os.getcwd() sys_path = list(sys.path) yield MonkeyPatch() diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index 81aba25f78f..377c30ca900 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -125,7 +125,7 @@ class TestImportPath: """ @pytest.fixture(scope="session") - def path1(self, tmp_path_factory: TempPathFactory) -> Generator[Path, None, None]: + def path1(self, tmp_path_factory: TempPathFactory) -> Generator[Path]: path = tmp_path_factory.mktemp("path") self.setuptestfs(path) yield path From 87bc32f6e28a7e60bd387a2f2df4aebebf98f867 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 21:06:03 -0300 Subject: [PATCH 15/29] [pre-commit.ci] pre-commit autoupdate (#12725) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.7 → v0.6.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.7...v0.6.1) 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 0e36d1e6137..a841a4a9a0f 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.5.7" + rev: "v0.6.1" hooks: - id: ruff args: ["--fix"] From eaf553d9a7fa3fb3119e8033edf623e8b5187f70 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 23:32:54 +0200 Subject: [PATCH 16/29] [pre-commit.ci] pre-commit autoupdate (#12741) 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.1 → v0.6.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.1...v0.6.2) - [github.com/pre-commit/mirrors-mypy: v1.11.1 → v1.11.2](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.1...v1.11.2) * Fix sphinx with a more robust fix Until https://github.com/sphinx-contrib/sphinxcontrib-towncrier/issues/92 is resolved. Co-authored-by: Pierre Sassoulas --- .pre-commit-config.yaml | 4 ++-- doc/en/broken-dep-constraints.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a841a4a9a0f..938b0bf407b 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.1" + rev: "v0.6.2" hooks: - id: ruff args: ["--fix"] @@ -21,7 +21,7 @@ repos: hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.1 + rev: v1.11.2 hooks: - id: mypy files: ^(src/|testing/|scripts/) diff --git a/doc/en/broken-dep-constraints.txt b/doc/en/broken-dep-constraints.txt index f7c3211a9a0..3a8d2c34b5a 100644 --- a/doc/en/broken-dep-constraints.txt +++ b/doc/en/broken-dep-constraints.txt @@ -3,4 +3,4 @@ # Pin towncrier temporarily due to incompatibility with sphinxcontrib-towncrier: # https://github.com/sphinx-contrib/sphinxcontrib-towncrier/issues/92 -towncrier!=24.7.0,!=24.7.1 +towncrier<24.7 From 6af50c05bd88b2fa2816e91161bd318bb527b18d Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 23 Jul 2024 09:57:35 -0300 Subject: [PATCH 17/29] Adapt code after updating to mypy 1.11 --- src/_pytest/_io/pprint.py | 4 ++-- src/_pytest/capture.py | 1 + src/_pytest/faulthandler.py | 1 + testing/test_assertion.py | 2 +- testing/test_monkeypatch.py | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/_pytest/_io/pprint.py b/src/_pytest/_io/pprint.py index 7213be7ba9b..fc29989be0b 100644 --- a/src/_pytest/_io/pprint.py +++ b/src/_pytest/_io/pprint.py @@ -111,7 +111,7 @@ def _format( p(self, object, stream, indent, allowance, context, level + 1) context.remove(objid) elif ( - _dataclasses.is_dataclass(object) + _dataclasses.is_dataclass(object) # type:ignore[unreachable] and not isinstance(object, type) and object.__dataclass_params__.repr and @@ -119,7 +119,7 @@ def _format( hasattr(object.__repr__, "__wrapped__") and "__create_fn__" in object.__repr__.__wrapped__.__qualname__ ): - context.add(objid) + context.add(objid) # type:ignore[unreachable] self._pprint_dataclass( object, stream, indent, allowance, context, level + 1 ) diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index f4586a42a69..506c0b3d287 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -202,6 +202,7 @@ def write(self, s: str) -> int: class DontReadFromInput(TextIO): @property def encoding(self) -> str: + assert sys.__stdin__ is not None return sys.__stdin__.encoding def read(self, size: int = -1) -> str: diff --git a/src/_pytest/faulthandler.py b/src/_pytest/faulthandler.py index 07e60f03fc9..d16aea1eb88 100644 --- a/src/_pytest/faulthandler.py +++ b/src/_pytest/faulthandler.py @@ -64,6 +64,7 @@ def get_stderr_fileno() -> int: # pytest-xdist monkeypatches sys.stderr with an object that is not an actual file. # https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors # This is potentially dangerous, but the best we can do. + assert sys.__stderr__ is not None return sys.__stderr__.fileno() diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 69ca0f73ff2..31192df0f6f 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -823,7 +823,7 @@ def __setitem__(self, item, value): def __delitem__(self, item): pass - def insert(self, item, index): + def insert(self, index, value): pass expl = callequal(TestSequence([0, 1]), list([0, 2])) diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index 1a429e3e681..7c62d90f2b9 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -415,7 +415,7 @@ def test_context() -> None: with monkeypatch.context() as m: m.setattr(functools, "partial", 3) assert not inspect.isclass(functools.partial) - assert inspect.isclass(functools.partial) + assert inspect.isclass(functools.partial) # type:ignore[unreachable] def test_context_classmethod() -> None: From be9e36edb938af2a819aa72c4f9f61d55b1ffc9f Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:15:38 +0000 Subject: [PATCH 18/29] Use oldest supported Python in mypy pre-commit checks (#12747) (#12750) Follow up to #12744, this ensures type checking works at the oldest Python version supported by pytest. (cherry picked from commit 419bc7a7c39d21af8a2b7cf29928ee93b7e02b19) Co-authored-by: Bruno Oliveira --- .pre-commit-config.yaml | 1 + src/_pytest/main.py | 2 +- src/_pytest/runner.py | 4 ++-- testing/test_runner.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 938b0bf407b..8c7fde3f2c4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,6 +26,7 @@ repos: - id: mypy files: ^(src/|testing/|scripts/) args: [] + language_version: "3.8" additional_dependencies: - iniconfig>=1.1.0 - attrs>=19.2.0 diff --git a/src/_pytest/main.py b/src/_pytest/main.py index befc7ccce6e..e5534e98d69 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -49,7 +49,7 @@ if TYPE_CHECKING: - from typing import Self + from typing_extensions import Self from _pytest.fixtures import FixtureManager diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 716c4948f4a..d0e1cdc7faa 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -167,7 +167,7 @@ def pytest_runtest_call(item: Item) -> None: del sys.last_value del sys.last_traceback if sys.version_info >= (3, 12, 0): - del sys.last_exc + del sys.last_exc # type:ignore[attr-defined] except AttributeError: pass try: @@ -177,7 +177,7 @@ def pytest_runtest_call(item: Item) -> None: sys.last_type = type(e) sys.last_value = e if sys.version_info >= (3, 12, 0): - sys.last_exc = e + sys.last_exc = e # type:ignore[attr-defined] assert e.__traceback__ is not None # Skip *this* frame sys.last_traceback = e.__traceback__.tb_next diff --git a/testing/test_runner.py b/testing/test_runner.py index 1b59ff78633..0d9facdcd71 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -1030,7 +1030,7 @@ def runtest(self): assert sys.last_type is IndexError assert isinstance(sys.last_value, IndexError) if sys.version_info >= (3, 12, 0): - assert isinstance(sys.last_exc, IndexError) + assert isinstance(sys.last_exc, IndexError) # type:ignore[attr-defined] assert sys.last_value.args[0] == "TEST" assert sys.last_traceback From bf379f44b6e4f19c6242edd65344f961243a29f4 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 14:55:28 +0000 Subject: [PATCH 19/29] Fix assertion rewriting with importlib mode (#12716) (#12755) Fixes #12659 (cherry picked from commit 9a444d113658be6ccb8dc9f57ed118a1ef17c94c) Co-authored-by: dongfangtianyu <7629022+dongfangtianyu@users.noreply.github.com> --- AUTHORS | 1 + changelog/12659.bugfix.rst | 1 + src/_pytest/assertion/rewrite.py | 10 +++++++++ src/_pytest/pathlib.py | 4 +++- testing/test_pathlib.py | 36 ++++++++++++++++++++++++++++++++ 5 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 changelog/12659.bugfix.rst diff --git a/AUTHORS b/AUTHORS index 8103a1d52a5..db993353689 100644 --- a/AUTHORS +++ b/AUTHORS @@ -412,6 +412,7 @@ Ted Xiao Terje Runde Thomas Grainger Thomas Hisch +Tianyu Dongfang Tim Hoffmann Tim Strazny TJ Bruno diff --git a/changelog/12659.bugfix.rst b/changelog/12659.bugfix.rst new file mode 100644 index 00000000000..297381b9602 --- /dev/null +++ b/changelog/12659.bugfix.rst @@ -0,0 +1 @@ +Fixed the issue of not displaying assertion failure differences when using the parameter ``--import-mode=importlib`` in pytest>=8.1. diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index f7ff4f6f7a2..a7a92c0f1fe 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -101,6 +101,16 @@ def find_spec( # Type ignored because mypy is confused about the `self` binding here. spec = self._find_spec(name, path) # type: ignore + + if spec is None and path is not None: + # With --import-mode=importlib, PathFinder cannot find spec without modifying `sys.path`, + # causing inability to assert rewriting (#12659). + # At this point, try using the file path to find the module spec. + for _path_str in path: + spec = importlib.util.spec_from_file_location(name, _path_str) + if spec is not None: + break + if ( # the import machinery could not find a file to import spec is None diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index e4dc4eddc9c..81e52ea729d 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -621,7 +621,9 @@ def _import_module_using_spec( # Checking with sys.meta_path first in case one of its hooks can import this module, # such as our own assertion-rewrite hook. for meta_importer in sys.meta_path: - spec = meta_importer.find_spec(module_name, [str(module_location)]) + spec = meta_importer.find_spec( + module_name, [str(module_location), str(module_path)] + ) if spec_matches_module_path(spec, module_path): break else: diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index 377c30ca900..7c0a0e03d76 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -1372,6 +1372,42 @@ def test_resolve_pkg_root_and_module_name_ns_multiple_levels( ) assert mod is mod2 + def test_ns_multiple_levels_import_rewrite_assertions( + self, + tmp_path: Path, + monkeypatch: MonkeyPatch, + pytester: Pytester, + ) -> None: + """Check assert rewriting with `--import-mode=importlib` (#12659).""" + self.setup_directories(tmp_path, monkeypatch, pytester) + code = dedent(""" + def test(): + assert "four lights" == "five lights" + """) + + # A case is in a subdirectory with an `__init__.py` file. + test_py = tmp_path / "src/dist2/com/company/calc/algo/test_demo.py" + test_py.write_text(code, encoding="UTF-8") + + pkg_root, module_name = resolve_pkg_root_and_module_name( + test_py, consider_namespace_packages=True + ) + assert (pkg_root, module_name) == ( + tmp_path / "src/dist2", + "com.company.calc.algo.test_demo", + ) + + result = pytester.runpytest("--import-mode=importlib", test_py) + + result.stdout.fnmatch_lines( + [ + "E AssertionError: assert 'four lights' == 'five lights'", + "E *", + "E - five lights*", + "E + four lights", + ] + ) + @pytest.mark.parametrize("import_mode", ["prepend", "append", "importlib"]) def test_incorrect_namespace_package( self, From 2dfea2742e7c4dad56c97fe634646456df7a0460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sviatoslav=20Sydorenko=20=28=D0=A1=D0=B2=D1=8F=D1=82=D0=BE?= =?UTF-8?q?=D1=81=D0=BB=D0=B0=D0=B2=20=D0=A1=D0=B8=D0=B4=D0=BE=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE=29?= Date: Tue, 3 Sep 2024 00:28:23 +0200 Subject: [PATCH 20/29] Merge pull request #12761 from pytest-dev/dependabot/github_actions/pypa/gh-action-pypi-publish-1.10.0 (cherry picked from commit c335a376b0f4f0f48b70b73910599049f6d84e09) --- .github/workflows/deploy.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f5ea4d39764..3b1a16837b9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -54,7 +54,9 @@ jobs: path: dist - name: Publish package to PyPI - uses: pypa/gh-action-pypi-publish@v1.9.0 + uses: pypa/gh-action-pypi-publish@v1.10.0 + with: + attestations: true - name: Push tag run: | From 4873394d53635ef62d1915d23972ed4281a784eb Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 20:21:35 +0000 Subject: [PATCH 21/29] doc: Remove past training (#12772) (#12773) (cherry picked from commit c6a0ef175a802efd01115454e8d18d0711ca350d) Co-authored-by: Florian Bruhin --- doc/en/index.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/en/index.rst b/doc/en/index.rst index 95044e8a544..8b34d589643 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -2,7 +2,6 @@ .. sidebar:: **Next Open Trainings and Events** - - `pytest: Professionelles Testen (nicht nur) für Python `_, at `CH Open Workshoptage `_, **September 2nd 2024**, HSLU Rotkreuz (CH) - `Professional Testing with Python `_, via `Python Academy `_ (3 day in-depth training), **March 4th -- 6th 2025**, Leipzig (DE) / Remote Also see :doc:`previous talks and blogposts ` From 01cfcc9f2dda817b25511772593012fd93e092d0 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 20:46:47 +0000 Subject: [PATCH 22/29] Fix typos and introduce codespell pre-commit hook (#12769) (#12774) (cherry picked from commit 09e386e3e0df5a58ef9ab4f8e43f55c650633be7) Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 7 +++++++ AUTHORS | 1 + CONTRIBUTING.rst | 2 +- changelog/12769.misc.rst | 1 + doc/en/announce/release-2.3.0.rst | 4 ++-- doc/en/changelog.rst | 8 ++++---- doc/en/deprecations.rst | 2 +- doc/en/explanation/fixtures.rst | 2 +- doc/en/funcarg_compare.rst | 6 +++--- doc/en/how-to/fixtures.rst | 2 +- doc/en/how-to/unittest.rst | 2 +- pyproject.toml | 5 +++++ src/_pytest/config/__init__.py | 2 +- src/_pytest/debugging.py | 2 +- testing/python/fixtures.py | 2 +- testing/test_config.py | 2 +- 16 files changed, 32 insertions(+), 18 deletions(-) create mode 100644 changelog/12769.misc.rst diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8c7fde3f2c4..4cb466cbb2c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,6 +16,13 @@ repos: hooks: - id: blacken-docs additional_dependencies: [black==24.1.1] +- repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + args: ["--toml=pyproject.toml"] + additional_dependencies: + - tomli - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: diff --git a/AUTHORS b/AUTHORS index db993353689..8bc8ad5cbde 100644 --- a/AUTHORS +++ b/AUTHORS @@ -85,6 +85,7 @@ Chris NeJame Chris Rose Chris Wheeler Christian Boelsen +Christian Clauss Christian Fetzer Christian Neumüller Christian Theunert diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 6e96fd24c40..d615e5fb113 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -543,4 +543,4 @@ Closing issues When a pull request is submitted to fix an issue, add text like ``closes #XYZW`` to the PR description and/or commits (where ``XYZW`` is the issue number). See the `GitHub docs `_ for more information. -When an issue is due to user error (e.g. misunderstanding of a functionality), please politely explain to the user why the issue raised is really a non-issue and ask them to close the issue if they have no further questions. If the original requestor is unresponsive, the issue will be handled as described in the section `Handling stale issues/PRs`_ above. +When an issue is due to user error (e.g. misunderstanding of a functionality), please politely explain to the user why the issue raised is really a non-issue and ask them to close the issue if they have no further questions. If the original requester is unresponsive, the issue will be handled as described in the section `Handling stale issues/PRs`_ above. diff --git a/changelog/12769.misc.rst b/changelog/12769.misc.rst new file mode 100644 index 00000000000..aae4d137a0d --- /dev/null +++ b/changelog/12769.misc.rst @@ -0,0 +1 @@ +Fix typos discovered by codespell and add codespell to pre-commit hooks. diff --git a/doc/en/announce/release-2.3.0.rst b/doc/en/announce/release-2.3.0.rst index 6905b77b923..c405073ef40 100644 --- a/doc/en/announce/release-2.3.0.rst +++ b/doc/en/announce/release-2.3.0.rst @@ -6,10 +6,10 @@ and parametrized testing in Python. It is now easier, more efficient and more predictable to re-run the same tests with different fixture instances. Also, you can directly declare the caching "scope" of fixtures so that dependent tests throughout your whole test suite can -re-use database or other expensive fixture objects with ease. Lastly, +reuse database or other expensive fixture objects with ease. Lastly, it's possible for fixture functions (formerly known as funcarg factories) to use other fixtures, allowing for a completely modular and -re-usable fixture design. +reusable fixture design. For detailed info and tutorial-style examples, see: diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 3178f82044d..9da6f487f8d 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -215,7 +215,7 @@ Improved documentation -- by :user:`webknjaz` -- `#12577 `_: `CI` and `BUILD_NUMBER` environment variables role is discribed in +- `#12577 `_: `CI` and `BUILD_NUMBER` environment variables role is described in the reference doc. They now also appear when doing `pytest -h` -- by :user:`MarcBresson`. @@ -319,7 +319,7 @@ Bug Fixes - `#12367 `_: Fix a regression in pytest 8.2.0 where unittest class instances (a fresh one is created for each test) were not released promptly on test teardown but only on session teardown. -- `#12381 `_: Fix possible "Directory not empty" crashes arising from concurent cache dir (``.pytest_cache``) creation. Regressed in pytest 8.2.0. +- `#12381 `_: Fix possible "Directory not empty" crashes arising from concurrent cache dir (``.pytest_cache``) creation. Regressed in pytest 8.2.0. @@ -330,7 +330,7 @@ Improved Documentation - `#12356 `_: Added a subsection to the documentation for debugging flaky tests to mention - lack of thread safety in pytest as a possible source of flakyness. + lack of thread safety in pytest as a possible source of flakiness. - `#12363 `_: The documentation webpages now links to a canonical version to reduce outdated documentation in search engine results. @@ -676,7 +676,7 @@ Bug Fixes This bug was introduced in pytest 8.0.0rc1. -- `#9765 `_, `#11816 `_: Fixed a frustrating bug that afflicted some users with the only error being ``assert mod not in mods``. The issue was caused by the fact that ``str(Path(mod))`` and ``mod.__file__`` don't necessarily produce the same string, and was being erroneously used interchangably in some places in the code. +- `#9765 `_, `#11816 `_: Fixed a frustrating bug that afflicted some users with the only error being ``assert mod not in mods``. The issue was caused by the fact that ``str(Path(mod))`` and ``mod.__file__`` don't necessarily produce the same string, and was being erroneously used interchangeably in some places in the code. This fix also broke the internal API of ``PytestPluginManager.consider_conftest`` by introducing a new parameter -- we mention this in case it is being used by external code, even if marked as *private*. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index bf6268a4980..153d5195476 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -43,7 +43,7 @@ If the import attempt raises :class:`ModuleNotFoundError` (the usual case), then warning is emitted. This way, the usual cases will keep working the same way, while unexpected errors will now issue a warning, with -users being able to supress the warning by passing ``exc_type=ImportError`` explicitly. +users being able to suppress the warning by passing ``exc_type=ImportError`` explicitly. In ``9.0``, the warning will turn into an error, and in ``9.1`` :func:`pytest.importorskip` will only capture :class:`ModuleNotFoundError` by default and no warnings will be issued anymore -- but users can still capture diff --git a/doc/en/explanation/fixtures.rst b/doc/en/explanation/fixtures.rst index 0bb3bf49fb0..53d4796c825 100644 --- a/doc/en/explanation/fixtures.rst +++ b/doc/en/explanation/fixtures.rst @@ -75,7 +75,7 @@ style of setup/teardown functions: * fixture management scales from simple unit to complex functional testing, allowing to parametrize fixtures and tests according - to configuration and component options, or to re-use fixtures + to configuration and component options, or to reuse fixtures across function, class, module or whole test session scopes. * teardown logic can be easily, and safely managed, no matter how many fixtures diff --git a/doc/en/funcarg_compare.rst b/doc/en/funcarg_compare.rst index 8b900d30f20..bc5e7d3c515 100644 --- a/doc/en/funcarg_compare.rst +++ b/doc/en/funcarg_compare.rst @@ -16,7 +16,7 @@ Shortcomings of the previous ``pytest_funcarg__`` mechanism The pre pytest-2.3 funcarg mechanism calls a factory each time a funcarg for a test function is required. If a factory wants to -re-use a resource across different scopes, it often used +reuse a resource across different scopes, it often used the ``request.cached_setup()`` helper to manage caching of resources. Here is a basic example how we could implement a per-session Database object: @@ -107,7 +107,7 @@ the tests requiring "db" will run twice as well. The "mysql" and "pg" values will also be used for reporting the test-invocation variants. This new way of parametrizing funcarg factories should in many cases -allow to re-use already written factories because effectively +allow to reuse already written factories because effectively ``request.param`` was already used when test functions/classes were parametrized via :py:func:`metafunc.parametrize(indirect=True) ` calls. @@ -164,7 +164,7 @@ hook which are often used to setup global resources. This suffers from several problems: 1. in distributed testing the managing process would setup test resources - that are never needed because it only co-ordinates the test run + that are never needed because it only coordinates the test run activities of the worker processes. 2. if you only perform a collection (with "--collect-only") diff --git a/doc/en/how-to/fixtures.rst b/doc/en/how-to/fixtures.rst index f4d59ff93c0..d60635c4fea 100644 --- a/doc/en/how-to/fixtures.rst +++ b/doc/en/how-to/fixtures.rst @@ -1487,7 +1487,7 @@ Modularity: using fixtures from a fixture function In addition to using fixtures in test functions, fixture functions can use other fixtures themselves. This contributes to a modular design -of your fixtures and allows re-use of framework-specific fixtures across +of your fixtures and allows reuse of framework-specific fixtures across many projects. As a simple example, we can extend the previous example and instantiate an object ``app`` where we stick the already defined ``smtp_connection`` resource into it: diff --git a/doc/en/how-to/unittest.rst b/doc/en/how-to/unittest.rst index 508aebde016..62e32b6d28f 100644 --- a/doc/en/how-to/unittest.rst +++ b/doc/en/how-to/unittest.rst @@ -109,7 +109,7 @@ achieves this by receiving a special ``request`` object which gives access to :ref:`the requesting test context ` such as the ``cls`` attribute, denoting the class from which the fixture is used. This architecture de-couples fixture writing from actual test -code and allows re-use of the fixture by a minimal reference, the fixture +code and allows reuse of the fixture by a minimal reference, the fixture name. So let's write an actual ``unittest.TestCase`` class using our fixture definition: diff --git a/pyproject.toml b/pyproject.toml index f3eba4a08a8..12764f3d127 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -306,6 +306,11 @@ disable = [ "wrong-import-position", # handled by isort / ruff ] +[tool.codespell] +ignore-words-list = "afile,asser,assertio,feld,hove,ned,noes,notin,paramete,parth,socio-economic,tesults,varius,wil" +skip = "*/plugin_list.rst" +write-changes = true + [tool.check-wheel-contents] # check-wheel-contents is executed by the build-and-inspect-python-package action. # W009: Wheel contains multiple toplevel library entries diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 0c1850df503..5c1becbf40c 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -361,7 +361,7 @@ def _get_legacy_hook_marks( opt_names: tuple[str, ...], ) -> dict[str, bool]: if TYPE_CHECKING: - # abuse typeguard from importlib to avoid massive method type union thats lacking a alias + # abuse typeguard from importlib to avoid massive method type union that's lacking an alias assert inspect.isroutine(method) known_marks: set[str] = {m.name for m in getattr(method, "pytestmark", [])} must_warn: list[str] = [] diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index 3e1463fff26..2dfe321e968 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -332,7 +332,7 @@ def maybe_wrap_pytest_function_for_tracing(pyfuncitem) -> None: def _enter_pdb( node: Node, excinfo: ExceptionInfo[BaseException], rep: BaseReport ) -> BaseReport: - # XXX we re-use the TerminalReporter's terminalwriter + # XXX we reuse the TerminalReporter's terminalwriter # because this seems to avoid some encoding related troubles # for not completely clear reasons. tw = node.config.pluginmanager.getplugin("terminalreporter")._tw diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 8d2646309a8..46f0a762cbb 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -4338,7 +4338,7 @@ def test_func(self, f2, f1, m2): assert request.fixturenames == "s1 p1 m1 m2 c1 f2 f1".split() def test_parametrized_package_scope_reordering(self, pytester: Pytester) -> None: - """A paramaterized package-scoped fixture correctly reorders items to + """A parameterized package-scoped fixture correctly reorders items to minimize setups & teardowns. Regression test for #12328. diff --git a/testing/test_config.py b/testing/test_config.py index 232839399e2..daf69845bb1 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -917,7 +917,7 @@ def pytest_addoption(parser): # default for string is "" value = config.getini("string1") assert value == "" - # should return None if None is explicity set as default value + # should return None if None is explicitly set as default value # irrespective of the type argument value = config.getini("none_1") assert value is None From bc913d194ec009699194b016ca619d5ae7f22c91 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:22:37 +0000 Subject: [PATCH 23/29] Streamline checks for verbose option (#12706) (#12778) Instead of calling `Config.option.verbose`, call the new `Config.get_verbosity` function to determine the verbosity level. This enables pytest to run correctly with the terminal plugin disabled. Fix #9422 (cherry picked from commit 72c682ff9773ad2690711105a100423ebf7c7c15) Co-authored-by: GTowers1 <130098608+GTowers1@users.noreply.github.com> --- changelog/9422.bugfix.rst | 3 +++ doc/en/example/simple.rst | 2 +- src/_pytest/cacheprovider.py | 2 +- src/_pytest/config/__init__.py | 2 +- src/_pytest/fixtures.py | 6 +++--- src/_pytest/logging.py | 2 +- src/_pytest/nodes.py | 4 ++-- src/_pytest/python.py | 2 +- src/_pytest/runner.py | 2 +- src/_pytest/stepwise.py | 2 +- testing/acceptance_test.py | 7 +++++++ 11 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 changelog/9422.bugfix.rst diff --git a/changelog/9422.bugfix.rst b/changelog/9422.bugfix.rst new file mode 100644 index 00000000000..38196340fd5 --- /dev/null +++ b/changelog/9422.bugfix.rst @@ -0,0 +1,3 @@ +Fix bug where disabling the terminal plugin via ``-p no:terminal`` would cause crashes related to missing the ``verbose`` option. + +-- by :user:`GTowers1` diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index a5e2e78c397..a14c34c19c3 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -460,7 +460,7 @@ display more information if applicable: def pytest_report_header(config): - if config.getoption("verbose") > 0: + if config.get_verbosity() > 0: return ["info1: did you know that ...", "did you?"] which will add info only when run with "--v": diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 7190810570e..1b236efdc9b 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -347,7 +347,7 @@ def get_last_failed_paths(self) -> set[Path]: return {x for x in result if x.exists()} def pytest_report_collectionfinish(self) -> str | None: - if self.active and self.config.getoption("verbose") >= 0: + if self.active and self.config.get_verbosity() >= 0: return f"run-last-failure: {self._report_status}" return None diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 5c1becbf40c..cc0a1449970 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1744,7 +1744,7 @@ def get_verbosity(self, verbosity_type: str | None = None) -> int: print(config.get_verbosity()) # 1 print(config.get_verbosity(Config.VERBOSITY_ASSERTIONS)) # 2 """ - global_level = self.option.verbose + global_level = self.getoption("verbose", default=0) assert isinstance(global_level, int) if verbosity_type is None: return global_level diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 7d0b40b150a..aaa92c63725 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -1348,7 +1348,7 @@ def pytestconfig(request: FixtureRequest) -> Config: Example:: def test_foo(pytestconfig): - if pytestconfig.getoption("verbose") > 0: + if pytestconfig.get_verbosity() > 0: ... """ @@ -1807,7 +1807,7 @@ def _show_fixtures_per_test(config: Config, session: Session) -> None: session.perform_collect() invocation_dir = config.invocation_params.dir tw = _pytest.config.create_terminal_writer(config) - verbose = config.getvalue("verbose") + verbose = config.get_verbosity() def get_best_relpath(func) -> str: loc = getlocation(func, invocation_dir) @@ -1866,7 +1866,7 @@ def _showfixtures_main(config: Config, session: Session) -> None: session.perform_collect() invocation_dir = config.invocation_params.dir tw = _pytest.config.create_terminal_writer(config) - verbose = config.getvalue("verbose") + verbose = config.get_verbosity() fm = session._fixturemanager diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 9f3417e9af2..08c826ff6d4 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -794,7 +794,7 @@ def pytest_runtestloop(self, session: Session) -> Generator[None, object, object if session.config.option.collectonly: return (yield) - if self._log_cli_enabled() and self._config.getoption("verbose") < 1: + if self._log_cli_enabled() and self._config.get_verbosity() < 1: # The verbose flag is needed to avoid messy test progress output. self._config.option.verbose = 1 diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index cc678695848..51bc5174628 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -435,12 +435,12 @@ def _repr_failure_py( else: style = "long" - if self.config.getoption("verbose", 0) > 1: + if self.config.get_verbosity() > 1: truncate_locals = False else: truncate_locals = True - truncate_args = False if self.config.getoption("verbose", 0) > 2 else True + truncate_args = False if self.config.get_verbosity() > 2 else True # excinfo.getrepr() formats paths relative to the CWD if `abspath` is False. # It is possible for a fixture/test to change the CWD while this code runs, which diff --git a/src/_pytest/python.py b/src/_pytest/python.py index b2199c76fc6..3478c34c47d 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -512,7 +512,7 @@ def importtestmodule( ) from e except ImportError as e: exc_info = ExceptionInfo.from_current() - if config.getoption("verbose") < 2: + if config.get_verbosity() < 2: exc_info.traceback = exc_info.traceback.filter(filter_traceback) exc_repr = ( exc_info.getrepr(style="short") diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index d0e1cdc7faa..0b60301bf5f 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -71,7 +71,7 @@ def pytest_addoption(parser: Parser) -> None: def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: durations = terminalreporter.config.option.durations durations_min = terminalreporter.config.option.durations_min - verbose = terminalreporter.config.getvalue("verbose") + verbose = terminalreporter.config.get_verbosity() if durations is None: return tr = terminalreporter diff --git a/src/_pytest/stepwise.py b/src/_pytest/stepwise.py index bd906ce63c1..c7860808c35 100644 --- a/src/_pytest/stepwise.py +++ b/src/_pytest/stepwise.py @@ -113,7 +113,7 @@ def pytest_runtest_logreport(self, report: TestReport) -> None: self.lastfailed = None def pytest_report_collectionfinish(self) -> str | None: - if self.config.getoption("verbose") >= 0 and self.report_status: + if self.config.get_verbosity() >= 0 and self.report_status: return f"stepwise: {self.report_status}" return None diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 01d911e8ca4..64a07ba8554 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1486,3 +1486,10 @@ def my_fixture(self, request): raise AssertionError( f"pytest command failed:\n{exc.stdout=!s}\n{exc.stderr=!s}" ) from exc + + +def test_no_terminal_plugin(pytester: Pytester) -> None: + """Smoke test to ensure pytest can execute without the terminal plugin (#9422).""" + pytester.makepyfile("def test(): assert 1 == 2") + result = pytester.runpytest("-pno:terminal", "-s") + assert result.ret == ExitCode.TESTS_FAILED From e5d32c73abcf4fa1362b15aaf660074de8f710d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sviatoslav=20Sydorenko=20=28=D0=A1=D0=B2=D1=8F=D1=82=D0=BE?= =?UTF-8?q?=D1=81=D0=BB=D0=B0=D0=B2=20=D0=A1=D0=B8=D0=B4=D0=BE=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE=29?= Date: Sat, 7 Sep 2024 02:13:45 +0200 Subject: [PATCH 24/29] Merge pull request #12784 from svenevs/fix/docs-example-parametrize-minor-typo [doc] Fix minor typo in example/parametrize.rst (cherry picked from commit 57cccf7f44b73e95a4cd2b09376a00ab35df5ec2) --- doc/en/example/parametrize.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index 3e449b2eaa2..b175510da6a 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -686,5 +686,5 @@ For example: assert (6 / example_input) == e In the example above, the first three test cases should run without any -exceptions, while the fourth should raise a``ZeroDivisionError`` exception, +exceptions, while the fourth should raise a ``ZeroDivisionError`` exception, which is expected by pytest. From 0f10b6b0d8138d3539de75cb7b2e33167b6fc882 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 8 Sep 2024 14:53:19 +0000 Subject: [PATCH 25/29] Fix issue with slashes being turned into backslashes on Windows (#12760) (#12787) Fix #12745 (cherry picked from commit d35b802805b210c6d5281864d663f53c6a72f153) Co-authored-by: Nauman Ahmed <90570675+nauman897@users.noreply.github.com> --- AUTHORS | 1 + changelog/12745.bugfix.rst | 1 + src/_pytest/config/__init__.py | 8 ++++++-- testing/test_terminal.py | 35 ++++++++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 changelog/12745.bugfix.rst diff --git a/AUTHORS b/AUTHORS index 8bc8ad5cbde..4f1b172ff33 100644 --- a/AUTHORS +++ b/AUTHORS @@ -300,6 +300,7 @@ mrbean-bremen Nathan Goldbaum Nathaniel Compton Nathaniel Waisbrot +Nauman Ahmed Ned Batchelder Neil Martin Neven Mundar diff --git a/changelog/12745.bugfix.rst b/changelog/12745.bugfix.rst new file mode 100644 index 00000000000..420be931ce9 --- /dev/null +++ b/changelog/12745.bugfix.rst @@ -0,0 +1 @@ +Fixed an issue with backslashes being incorrectly converted in nodeid paths on Windows, ensuring consistent path handling across environments. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index cc0a1449970..3cca1479381 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1179,8 +1179,12 @@ def notify_exception( def cwd_relative_nodeid(self, nodeid: str) -> str: # nodeid's are relative to the rootpath, compute relative to cwd. if self.invocation_params.dir != self.rootpath: - fullpath = self.rootpath / nodeid - nodeid = bestrelpath(self.invocation_params.dir, fullpath) + base_path_part, *nodeid_part = nodeid.split("::") + # Only process path part + fullpath = self.rootpath / base_path_part + relative_path = bestrelpath(self.invocation_params.dir, fullpath) + + nodeid = "::".join([relative_path, *nodeid_part]) return nodeid @classmethod diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 11ad623fb6b..14c152d6123 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -3067,3 +3067,38 @@ def test_pass(): "*= 1 xpassed in * =*", ] ) + + +class TestNodeIDHandling: + def test_nodeid_handling_windows_paths(self, pytester: Pytester, tmp_path) -> None: + """Test the correct handling of Windows-style paths with backslashes.""" + pytester.makeini("[pytest]") # Change `config.rootpath` + + test_path = pytester.path / "tests" / "test_foo.py" + test_path.parent.mkdir() + os.chdir(test_path.parent) # Change `config.invocation_params.dir` + + test_path.write_text( + textwrap.dedent( + """ + import pytest + + @pytest.mark.parametrize("a", ["x/y", "C:/path", "\\\\", "C:\\\\path", "a::b/"]) + def test_x(a): + assert False + """ + ), + encoding="utf-8", + ) + + result = pytester.runpytest("-v") + + result.stdout.re_match_lines( + [ + r".*test_foo.py::test_x\[x/y\] .*FAILED.*", + r".*test_foo.py::test_x\[C:/path\] .*FAILED.*", + r".*test_foo.py::test_x\[\\\\\] .*FAILED.*", + r".*test_foo.py::test_x\[C:\\\\path\] .*FAILED.*", + r".*test_foo.py::test_x\[a::b/\] .*FAILED.*", + ] + ) From a9910a413a691e1b216e2235a9cbec0921117702 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 8 Sep 2024 14:59:54 +0000 Subject: [PATCH 26/29] Do not discover properties when iterating fixtures (#12781) (#12788) Resolves #12446 (cherry picked from commit c6a529032231ccddd3040e8ab1a5a756eb9ea4a0) Co-authored-by: Anthony Sottile --- changelog/12446.bugfix.rst | 1 + src/_pytest/fixtures.py | 16 +++++++++++++--- testing/python/collect.py | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 changelog/12446.bugfix.rst diff --git a/changelog/12446.bugfix.rst b/changelog/12446.bugfix.rst new file mode 100644 index 00000000000..2f591c48eed --- /dev/null +++ b/changelog/12446.bugfix.rst @@ -0,0 +1 @@ +Avoid calling ``@property`` (and other instance descriptors) during fixture discovery -- by :user:`asottile` diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index aaa92c63725..6b882fa3515 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -53,6 +53,7 @@ from _pytest.compat import NOTSET from _pytest.compat import NotSetType from _pytest.compat import safe_getattr +from _pytest.compat import safe_isclass from _pytest.config import _PluggyPlugin from _pytest.config import Config from _pytest.config import ExitCode @@ -1724,17 +1725,26 @@ def parsefactories( if holderobj in self._holderobjseen: return + # Avoid accessing `@property` (and other descriptors) when iterating fixtures. + if not safe_isclass(holderobj) and not isinstance(holderobj, types.ModuleType): + holderobj_tp: object = type(holderobj) + else: + holderobj_tp = holderobj + self._holderobjseen.add(holderobj) for name in dir(holderobj): # The attribute can be an arbitrary descriptor, so the attribute - # access below can raise. safe_getatt() ignores such exceptions. - obj = safe_getattr(holderobj, name, None) - marker = getfixturemarker(obj) + # access below can raise. safe_getattr() ignores such exceptions. + obj_ub = safe_getattr(holderobj_tp, name, None) + marker = getfixturemarker(obj_ub) if not isinstance(marker, FixtureFunctionMarker): # Magic globals with __getattr__ might have got us a wrong # fixture attribute. continue + # OK we know it is a fixture -- now safe to look up on the _instance_. + obj = getattr(holderobj, name) + if marker.name: name = marker.name diff --git a/testing/python/collect.py b/testing/python/collect.py index 06386611279..530f1c340ff 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -263,6 +263,43 @@ def prop(self): result = pytester.runpytest() assert result.ret == ExitCode.NO_TESTS_COLLECTED + def test_does_not_discover_properties(self, pytester: Pytester) -> None: + """Regression test for #12446.""" + pytester.makepyfile( + """\ + class TestCase: + @property + def oops(self): + raise SystemExit('do not call me!') + """ + ) + result = pytester.runpytest() + assert result.ret == ExitCode.NO_TESTS_COLLECTED + + def test_does_not_discover_instance_descriptors(self, pytester: Pytester) -> None: + """Regression test for #12446.""" + pytester.makepyfile( + """\ + # not `@property`, but it acts like one + # this should cover the case of things like `@cached_property` / etc. + class MyProperty: + def __init__(self, func): + self._func = func + def __get__(self, inst, owner): + if inst is None: + return self + else: + return self._func.__get__(inst, owner)() + + class TestCase: + @MyProperty + def oops(self): + raise SystemExit('do not call me!') + """ + ) + result = pytester.runpytest() + assert result.ret == ExitCode.NO_TESTS_COLLECTED + def test_abstract_class_is_not_collected(self, pytester: Pytester) -> None: """Regression test for #12275 (non-unittest version).""" pytester.makepyfile( From 0dabdcfe4de99147a07bd577804b60818ea25bc4 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:40:49 -0300 Subject: [PATCH 27/29] Include co-authors in release announcement (#12795) (#12797) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As noted in #12789, the `release.py` script did not consider `Co-authored-by` fields, and since we introduced the new backport bot, this problem became more apparent due to how the backport commit and PR are generated. Previously, the list of authors produced by the script in the #12789 branch was: ``` * Avasam * Bruno Oliveira * Ronny Pfannschmidt * Sviatoslav Sydorenko (Святослав Сидоренко) ``` With this script: ``` * Anthony Sottile * Avasam * Bruno Oliveira * Christian Clauss * Eugene Mwangi * Florian Bruhin * GTowers1 * Nauman Ahmed * Pierre Sassoulas * Reagan Lee * Ronny Pfannschmidt * Stefaan Lippens * Sviatoslav Sydorenko (Святослав Сидоренко) * dongfangtianyu ``` (cherry picked from commit e8504ed49be73e516ee1559a250daec659e96b9d) Co-authored-by: Bruno Oliveira --- scripts/release.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/scripts/release.py b/scripts/release.py index 545919cd60b..aef5d6d5f73 100644 --- a/scripts/release.py +++ b/scripts/release.py @@ -6,6 +6,7 @@ import argparse import os from pathlib import Path +import re from subprocess import call from subprocess import check_call from subprocess import check_output @@ -16,17 +17,27 @@ def announce(version: str, template_name: str, doc_version: str) -> None: """Generates a new release announcement entry in the docs.""" - # Get our list of authors + # Get our list of authors and co-authors. stdout = check_output(["git", "describe", "--abbrev=0", "--tags"], encoding="UTF-8") last_version = stdout.strip() + rev_range = f"{last_version}..HEAD" - stdout = check_output( - ["git", "log", f"{last_version}..HEAD", "--format=%aN"], encoding="UTF-8" + authors = check_output( + ["git", "log", rev_range, "--format=%aN"], encoding="UTF-8" + ).splitlines() + + co_authors_output = check_output( + ["git", "log", rev_range, "--format=%(trailers:key=Co-authored-by) "], + encoding="UTF-8", ) + co_authors: list[str] = [] + for co_author_line in co_authors_output.splitlines(): + if m := re.search(r"Co-authored-by: (.+?)<", co_author_line): + co_authors.append(m.group(1).strip()) contributors = { name - for name in stdout.splitlines() + for name in authors + co_authors if not name.endswith("[bot]") and name != "pytest bot" } From 972f307c7861ae498e705d3d12e003fa4b035ac0 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Mon, 9 Sep 2024 21:44:43 +0000 Subject: [PATCH 28/29] Prepare release version 8.3.3 --- changelog/12446.bugfix.rst | 1 - changelog/12659.bugfix.rst | 1 - changelog/12663.doc.rst | 1 - changelog/12667.bugfix.rst | 1 - changelog/12678.doc.rst | 1 - changelog/12744.bugfix.rst | 1 - changelog/12745.bugfix.rst | 1 - changelog/12769.misc.rst | 1 - changelog/6682.bugfix.rst | 1 - changelog/9422.bugfix.rst | 3 -- doc/en/announce/index.rst | 1 + doc/en/announce/release-8.3.3.rst | 31 +++++++++++++++++++ doc/en/builtin.rst | 18 +++++------ doc/en/changelog.rst | 46 +++++++++++++++++++++++++++++ doc/en/example/parametrize.rst | 6 ++-- doc/en/example/pythoncollection.rst | 4 +-- doc/en/getting-started.rst | 2 +- doc/en/how-to/fixtures.rst | 2 +- 18 files changed, 94 insertions(+), 28 deletions(-) delete mode 100644 changelog/12446.bugfix.rst delete mode 100644 changelog/12659.bugfix.rst delete mode 100644 changelog/12663.doc.rst delete mode 100644 changelog/12667.bugfix.rst delete mode 100644 changelog/12678.doc.rst delete mode 100644 changelog/12744.bugfix.rst delete mode 100644 changelog/12745.bugfix.rst delete mode 100644 changelog/12769.misc.rst delete mode 100644 changelog/6682.bugfix.rst delete mode 100644 changelog/9422.bugfix.rst create mode 100644 doc/en/announce/release-8.3.3.rst diff --git a/changelog/12446.bugfix.rst b/changelog/12446.bugfix.rst deleted file mode 100644 index 2f591c48eed..00000000000 --- a/changelog/12446.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid calling ``@property`` (and other instance descriptors) during fixture discovery -- by :user:`asottile` diff --git a/changelog/12659.bugfix.rst b/changelog/12659.bugfix.rst deleted file mode 100644 index 297381b9602..00000000000 --- a/changelog/12659.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed the issue of not displaying assertion failure differences when using the parameter ``--import-mode=importlib`` in pytest>=8.1. diff --git a/changelog/12663.doc.rst b/changelog/12663.doc.rst deleted file mode 100644 index e665bc67a10..00000000000 --- a/changelog/12663.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Clarify that the `pytest_deselected` hook should be called from `pytest_collection_modifyitems` hook implementations when items are deselected. diff --git a/changelog/12667.bugfix.rst b/changelog/12667.bugfix.rst deleted file mode 100644 index eceee86cb53..00000000000 --- a/changelog/12667.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed a regression where type change in `ExceptionInfo.errisinstance` caused `mypy` to fail. diff --git a/changelog/12678.doc.rst b/changelog/12678.doc.rst deleted file mode 100644 index 9d79ec41007..00000000000 --- a/changelog/12678.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Remove erroneous quotes from `tmp_path_retention_policy` example in docs. diff --git a/changelog/12744.bugfix.rst b/changelog/12744.bugfix.rst deleted file mode 100644 index 7814b59c180..00000000000 --- a/changelog/12744.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed typing compatibility with Python 3.9 or less -- replaced `typing.Self` with `typing_extensions.Self` -- by :user:`Avasam` diff --git a/changelog/12745.bugfix.rst b/changelog/12745.bugfix.rst deleted file mode 100644 index 420be931ce9..00000000000 --- a/changelog/12745.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed an issue with backslashes being incorrectly converted in nodeid paths on Windows, ensuring consistent path handling across environments. diff --git a/changelog/12769.misc.rst b/changelog/12769.misc.rst deleted file mode 100644 index aae4d137a0d..00000000000 --- a/changelog/12769.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Fix typos discovered by codespell and add codespell to pre-commit hooks. diff --git a/changelog/6682.bugfix.rst b/changelog/6682.bugfix.rst deleted file mode 100644 index 7f756cbd9c2..00000000000 --- a/changelog/6682.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed bug where the verbosity levels where not being respected when printing the "msg" part of failed assertion (as in ``assert condition, msg``). diff --git a/changelog/9422.bugfix.rst b/changelog/9422.bugfix.rst deleted file mode 100644 index 38196340fd5..00000000000 --- a/changelog/9422.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix bug where disabling the terminal plugin via ``-p no:terminal`` would cause crashes related to missing the ``verbose`` option. - --- by :user:`GTowers1` diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index 09311a1a1ab..61e4a772beb 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-8.3.3 release-8.3.2 release-8.3.1 release-8.3.0 diff --git a/doc/en/announce/release-8.3.3.rst b/doc/en/announce/release-8.3.3.rst new file mode 100644 index 00000000000..5e3eb36b921 --- /dev/null +++ b/doc/en/announce/release-8.3.3.rst @@ -0,0 +1,31 @@ +pytest-8.3.3 +======================================= + +pytest 8.3.3 has just been released to PyPI. + +This is a bug-fix release, being a drop-in replacement. To upgrade:: + + pip install --upgrade pytest + +The full changelog is available at https://docs.pytest.org/en/stable/changelog.html. + +Thanks to all of the contributors to this release: + +* Anthony Sottile +* Avasam +* Bruno Oliveira +* Christian Clauss +* Eugene Mwangi +* Florian Bruhin +* GTowers1 +* Nauman Ahmed +* Pierre Sassoulas +* Reagan Lee +* Ronny Pfannschmidt +* Stefaan Lippens +* Sviatoslav Sydorenko (Святослав Сидоренко) +* dongfangtianyu + + +Happy testing, +The pytest Development Team diff --git a/doc/en/builtin.rst b/doc/en/builtin.rst index b4c0f91f8e5..9b406a6a512 100644 --- a/doc/en/builtin.rst +++ b/doc/en/builtin.rst @@ -22,7 +22,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a cachedir: .pytest_cache rootdir: /home/sweet/project collected 0 items - cache -- .../_pytest/cacheprovider.py:558 + cache -- .../_pytest/cacheprovider.py:556 Return a cache object that can persist state between testing sessions. cache.get(key, default) @@ -33,7 +33,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a Values can be any object handled by the json stdlib module. - capsysbinary -- .../_pytest/capture.py:1005 + capsysbinary -- .../_pytest/capture.py:1006 Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``. The captured output is made available via ``capsysbinary.readouterr()`` @@ -51,7 +51,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a captured = capsysbinary.readouterr() assert captured.out == b"hello\n" - capfd -- .../_pytest/capture.py:1033 + capfd -- .../_pytest/capture.py:1034 Enable text capturing of writes to file descriptors ``1`` and ``2``. The captured output is made available via ``capfd.readouterr()`` method @@ -69,7 +69,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a captured = capfd.readouterr() assert captured.out == "hello\n" - capfdbinary -- .../_pytest/capture.py:1061 + capfdbinary -- .../_pytest/capture.py:1062 Enable bytes capturing of writes to file descriptors ``1`` and ``2``. The captured output is made available via ``capfd.readouterr()`` method @@ -87,7 +87,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a captured = capfdbinary.readouterr() assert captured.out == b"hello\n" - capsys -- .../_pytest/capture.py:977 + capsys -- .../_pytest/capture.py:978 Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``. The captured output is made available via ``capsys.readouterr()`` method @@ -105,7 +105,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a captured = capsys.readouterr() assert captured.out == "hello\n" - doctest_namespace [session scope] -- .../_pytest/doctest.py:740 + doctest_namespace [session scope] -- .../_pytest/doctest.py:741 Fixture that returns a :py:class:`dict` that will be injected into the namespace of doctests. @@ -119,14 +119,14 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a For more details: :ref:`doctest_namespace`. - pytestconfig [session scope] -- .../_pytest/fixtures.py:1344 + pytestconfig [session scope] -- .../_pytest/fixtures.py:1345 Session-scoped fixture that returns the session's :class:`pytest.Config` object. Example:: def test_foo(pytestconfig): - if pytestconfig.getoption("verbose") > 0: + if pytestconfig.get_verbosity() > 0: ... record_property -- .../_pytest/junitxml.py:280 @@ -196,7 +196,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a .. _legacy_path: https://py.readthedocs.io/en/latest/path.html - caplog -- .../_pytest/logging.py:600 + caplog -- .../_pytest/logging.py:598 Access and control log capturing. Captured logs are available through the following properties/methods:: diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 9da6f487f8d..9f30c86be3a 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -31,6 +31,52 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 8.3.3 (2024-09-09) +========================= + +Bug fixes +--------- + +- `#12446 `_: Avoid calling ``@property`` (and other instance descriptors) during fixture discovery -- by :user:`asottile` + + +- `#12659 `_: Fixed the issue of not displaying assertion failure differences when using the parameter ``--import-mode=importlib`` in pytest>=8.1. + + +- `#12667 `_: Fixed a regression where type change in `ExceptionInfo.errisinstance` caused `mypy` to fail. + + +- `#12744 `_: Fixed typing compatibility with Python 3.9 or less -- replaced `typing.Self` with `typing_extensions.Self` -- by :user:`Avasam` + + +- `#12745 `_: Fixed an issue with backslashes being incorrectly converted in nodeid paths on Windows, ensuring consistent path handling across environments. + + +- `#6682 `_: Fixed bug where the verbosity levels where not being respected when printing the "msg" part of failed assertion (as in ``assert condition, msg``). + + +- `#9422 `_: Fix bug where disabling the terminal plugin via ``-p no:terminal`` would cause crashes related to missing the ``verbose`` option. + + -- by :user:`GTowers1` + + + +Improved documentation +---------------------- + +- `#12663 `_: Clarify that the `pytest_deselected` hook should be called from `pytest_collection_modifyitems` hook implementations when items are deselected. + + +- `#12678 `_: Remove erroneous quotes from `tmp_path_retention_policy` example in docs. + + + +Miscellaneous internal changes +------------------------------ + +- `#12769 `_: Fix typos discovered by codespell and add codespell to pre-commit hooks. + + pytest 8.3.2 (2024-07-24) ========================= diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index b175510da6a..fa43308d045 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -162,7 +162,7 @@ objects, they are still using the default pytest representation: rootdir: /home/sweet/project collected 8 items - + @@ -239,7 +239,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia rootdir: /home/sweet/project collected 4 items - + @@ -318,7 +318,7 @@ Let's first see how it looks like at collection time: rootdir: /home/sweet/project collected 2 items - + diff --git a/doc/en/example/pythoncollection.rst b/doc/en/example/pythoncollection.rst index 5bd03035c14..42a603f114c 100644 --- a/doc/en/example/pythoncollection.rst +++ b/doc/en/example/pythoncollection.rst @@ -152,7 +152,7 @@ The test collection would look like this: configfile: pytest.ini collected 2 items - + @@ -215,7 +215,7 @@ You can always peek at the collection tree without running tests like this: configfile: pytest.ini collected 3 items - + diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 050fd2d80ec..faf81154c48 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -22,7 +22,7 @@ Install ``pytest`` .. code-block:: bash $ pytest --version - pytest 8.3.2 + pytest 8.3.3 .. _`simpletest`: diff --git a/doc/en/how-to/fixtures.rst b/doc/en/how-to/fixtures.rst index d60635c4fea..4f6e8cbee06 100644 --- a/doc/en/how-to/fixtures.rst +++ b/doc/en/how-to/fixtures.rst @@ -1418,7 +1418,7 @@ Running the above tests results in the following test IDs being used: rootdir: /home/sweet/project collected 12 items - + From d0f136fe64f9374f18a04562305b178fb380d1ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 09:04:10 -0300 Subject: [PATCH 29/29] build(deps): Bump pypa/gh-action-pypi-publish from 1.10.0 to 1.10.1 (#12790) Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.10.0 to 1.10.1. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.10.0...v1.10.1) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish 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> --- .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 3b1a16837b9..a58dc83639a 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.0 + uses: pypa/gh-action-pypi-publish@v1.10.1 with: attestations: true