From 3f71680ac0eb7646707b82e768f1302fcff3a357 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 10 Mar 2021 13:47:14 +0000 Subject: [PATCH 01/62] Warn when a mark is applied to a fixture Fixes #3664 --- changelog/3664.deprecation.rst | 1 + doc/en/how-to/fixtures.rst | 3 +-- src/_pytest/deprecated.py | 2 ++ src/_pytest/fixtures.py | 4 ++++ src/_pytest/mark/structures.py | 4 ++++ testing/python/fixtures.py | 18 ++++++++++++++++++ 6 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 changelog/3664.deprecation.rst diff --git a/changelog/3664.deprecation.rst b/changelog/3664.deprecation.rst new file mode 100644 index 000000000..b12273ef0 --- /dev/null +++ b/changelog/3664.deprecation.rst @@ -0,0 +1 @@ +Deprecate applying a mark to a fixture diff --git a/doc/en/how-to/fixtures.rst b/doc/en/how-to/fixtures.rst index a2eb211e1..420b0d1a5 100644 --- a/doc/en/how-to/fixtures.rst +++ b/doc/en/how-to/fixtures.rst @@ -1667,8 +1667,7 @@ into an ini-file: def my_fixture_that_sadly_wont_use_my_other_fixture(): ... - Currently this will not generate any error or warning, but this is intended - to be handled by `#3664 `_. + Currently this will generate a deprecation warning. .. _`override fixtures`: diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 596574877..1bdadb343 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -95,6 +95,8 @@ NODE_FSPATH = UnformattedWarning( "see https://docs.pytest.org/en/latest/deprecations.html#node-fspath-in-favor-of-pathlib-and-node-path", ) +MARKED_FIXTURE = PytestDeprecationWarning("Marks cannot be applied to fixtures") + # You want to make some `__init__` or function "private". # # def my_private_function(some, args): diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index b0a895a04..bbce454c3 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -54,6 +54,7 @@ from _pytest.config import Config from _pytest.config.argparsing import Parser from _pytest.deprecated import check_ispytest from _pytest.deprecated import FILLFUNCARGS +from _pytest.deprecated import MARKED_FIXTURE from _pytest.deprecated import NODE_FSPATH from _pytest.deprecated import YIELD_FIXTURE from _pytest.mark import Mark @@ -1222,6 +1223,9 @@ class FixtureFunctionMarker: "fixture is being applied more than once to the same function" ) + if hasattr(function, "pytestmark"): + warnings.warn(MARKED_FIXTURE, stacklevel=2) + function = wrap_function_to_error_out_if_called_directly(function, self) name = self.name or function.__name__ diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index d2f21e168..737a3ad05 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -29,6 +29,7 @@ from ..compat import NOTSET from ..compat import NotSetType from _pytest.config import Config from _pytest.deprecated import check_ispytest +from _pytest.deprecated import MARKED_FIXTURE from _pytest.outcomes import fail from _pytest.warning_types import PytestUnknownMarkWarning @@ -399,6 +400,9 @@ def store_mark(obj, mark: Mark) -> None: assert isinstance(mark, Mark), mark # Always reassign name to avoid updating pytestmark in a reference that # was only borrowed. + if hasattr(obj, "_pytestfixturefunction"): + warnings.warn(MARKED_FIXTURE, stacklevel=2) + obj.pytestmark = get_unpacked_marks(obj) + [mark] diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 3eb4b80f3..3d52c1c60 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -3612,6 +3612,24 @@ class TestShowFixtures: def foo(): raise NotImplementedError() + def test_fixture_disallow_on_marked_functions(self): + """Test that applying @pytest.fixture to a marked function warns (#3364).""" + with pytest.warns(pytest.PytestDeprecationWarning): + + @pytest.fixture + @pytest.mark.usefixtures("tmp_path") + def foo(): + raise NotImplementedError() + + def test_fixture_disallow_marks_on_fixtures(self): + """Test that applying a mark to a fixture warns (#3364).""" + with pytest.warns(pytest.PytestDeprecationWarning): + + @pytest.mark.usefixtures("tmp_path") + @pytest.fixture + def foo(): + raise NotImplementedError() + class TestContextManagerFixtureFuncs: def test_simple(self, pytester: Pytester) -> None: From 12efc584791ee70151bbc1d6c10fa6faa330cd2c Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 19 Mar 2021 09:43:22 +0000 Subject: [PATCH 02/62] document deprecation in deprecations.rst --- doc/en/deprecations.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 6ecb37b38..482c5e18d 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -29,6 +29,14 @@ As pytest tries to move off `py.path.local Date: Fri, 19 Mar 2021 09:44:39 +0000 Subject: [PATCH 03/62] update MARKED_FIXTURE deprecation message --- src/_pytest/deprecated.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 1bdadb343..a0498a3d0 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -95,7 +95,7 @@ NODE_FSPATH = UnformattedWarning( "see https://docs.pytest.org/en/latest/deprecations.html#node-fspath-in-favor-of-pathlib-and-node-path", ) -MARKED_FIXTURE = PytestDeprecationWarning("Marks cannot be applied to fixtures") +MARKED_FIXTURE = PytestDeprecationWarning("Marks applied to fixtures have no effect") # You want to make some `__init__` or function "private". # From 5227279c150cfc23a4c73c9538ed81a141a641b6 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 19 Mar 2021 09:47:19 +0000 Subject: [PATCH 04/62] Update changelog/3664.deprecation.rst --- changelog/3664.deprecation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/3664.deprecation.rst b/changelog/3664.deprecation.rst index b12273ef0..6436910a6 100644 --- a/changelog/3664.deprecation.rst +++ b/changelog/3664.deprecation.rst @@ -1 +1 @@ -Deprecate applying a mark to a fixture +Applying a mark to a fixture function is deprecated. Doing so has no effect. From 26b0702b9834acf01f3bd2d2354fc0be797b7546 Mon Sep 17 00:00:00 2001 From: Vijay Arora Date: Fri, 1 Oct 2021 19:34:59 +0530 Subject: [PATCH 05/62] Updated logging.py Updated logging.py for #9146 --- src/_pytest/logging.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 7f73b8a46..29cb4c751 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -447,7 +447,10 @@ class LogCaptureFixture: self.handler.reset() def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None: - """Set the level of a logger for the duration of a test. + """Sets the threshold for this logger to level. Logging messages which are less + severe than level will be ignored; logging messages which have severity level or + higher will be emitted by whichever handler or handlers service this logger, unless + a handler’s level has been set to a higher severity level than level. .. versionchanged:: 3.4 The levels of the loggers changed by this function will be From a8697601ad36436caaa3af63ed3803424059c8fc Mon Sep 17 00:00:00 2001 From: Vijay Arora Date: Fri, 1 Oct 2021 20:10:46 +0530 Subject: [PATCH 06/62] Create 9146.doc.rst Create 9146.doc.rst --- changelog/9146.doc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/9146.doc.rst diff --git a/changelog/9146.doc.rst b/changelog/9146.doc.rst new file mode 100644 index 000000000..77b375e89 --- /dev/null +++ b/changelog/9146.doc.rst @@ -0,0 +1 @@ +Documentation improvement for #9146 From 307dbf15c42ca5f3e104b600250426b011a3bce3 Mon Sep 17 00:00:00 2001 From: Vijay Arora Date: Fri, 1 Oct 2021 20:16:10 +0530 Subject: [PATCH 07/62] Add Myself as Authors --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index bfc2d3e46..1ee61d65e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -328,6 +328,7 @@ Vasily Kuznetsov Victor Maryama Victor Uriarte Vidar T. Fauske +Vijay Arora Virgil Dupras Vitaly Lashmanov Vlad Dragos From de1f378b60e9e5c0e4f0ab4eb873ddc1f1d236b1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 1 Oct 2021 14:53:25 +0000 Subject: [PATCH 08/62] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/logging.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 29cb4c751..3f2d03059 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -447,8 +447,8 @@ class LogCaptureFixture: self.handler.reset() def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None: - """Sets the threshold for this logger to level. Logging messages which are less - severe than level will be ignored; logging messages which have severity level or + """Sets the threshold for this logger to level. Logging messages which are less + severe than level will be ignored; logging messages which have severity level or higher will be emitted by whichever handler or handlers service this logger, unless a handler’s level has been set to a higher severity level than level. From 750ce30392b1d0b99d3172e8e28aa946b1b24975 Mon Sep 17 00:00:00 2001 From: Vijay Arora Date: Fri, 1 Oct 2021 21:33:32 +0530 Subject: [PATCH 09/62] Update 9146.doc.rst --- changelog/9146.doc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/9146.doc.rst b/changelog/9146.doc.rst index 77b375e89..95189b96d 100644 --- a/changelog/9146.doc.rst +++ b/changelog/9146.doc.rst @@ -1 +1 @@ -Documentation improvement for #9146 +Improve Documentation for `caplog.set_level`. From 5fefd7de96d26f61a60bcdb54785f2ceb170f57a Mon Sep 17 00:00:00 2001 From: Vijay Arora Date: Fri, 1 Oct 2021 21:36:35 +0530 Subject: [PATCH 10/62] Updated indentation and spaces in logging.py for #9146 --- src/_pytest/logging.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 3f2d03059..9267c4070 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -447,10 +447,12 @@ class LogCaptureFixture: self.handler.reset() def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None: - """Sets the threshold for this logger to level. Logging messages which are less - severe than level will be ignored; logging messages which have severity level or - higher will be emitted by whichever handler or handlers service this logger, unless - a handler’s level has been set to a higher severity level than level. + """Sets the threshold for this logger to level. + + Logging messages which are less severe than level will be ignored; logging messages + which have severity level or higher will be emitted by whichever handler or handlers + service this logger, unless a handler’s level has been set to a higher severity level + than `level`. .. versionchanged:: 3.4 The levels of the loggers changed by this function will be From bb6155adfa52cae6f77f239f0a1915854ee565cf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 1 Oct 2021 16:07:38 +0000 Subject: [PATCH 11/62] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/logging.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 9267c4070..92a0cb6c6 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -447,11 +447,11 @@ class LogCaptureFixture: self.handler.reset() def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None: - """Sets the threshold for this logger to level. - - Logging messages which are less severe than level will be ignored; logging messages - which have severity level or higher will be emitted by whichever handler or handlers - service this logger, unless a handler’s level has been set to a higher severity level + """Sets the threshold for this logger to level. + + Logging messages which are less severe than level will be ignored; logging messages + which have severity level or higher will be emitted by whichever handler or handlers + service this logger, unless a handler’s level has been set to a higher severity level than `level`. .. versionchanged:: 3.4 From 5c286d19d80f60e9f1f607f517e9df7cda2ff6b1 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 9 Oct 2022 20:11:51 +0100 Subject: [PATCH 12/62] Update doc/en/deprecations.rst --- doc/en/deprecations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 88978b6fe..8ff0fe30d 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -276,7 +276,7 @@ deprecation warning is now raised. Applying a mark to a fixture function ------------------------------------- -.. deprecated:: 6.3 +.. deprecated:: 7.2 Applying a mark to a fixture function is deprecated. Doing so has no effect, and will raise an error in the next version. From aa9cc7e8b4f57e1c2fc15c5f7f2212c45c3ae267 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 9 Oct 2022 19:11:56 +0000 Subject: [PATCH 13/62] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/fixtures.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 04035a642..3cb152eeb 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -53,9 +53,7 @@ from _pytest.config import _PluggyPlugin from _pytest.config import Config from _pytest.config.argparsing import Parser from _pytest.deprecated import check_ispytest -from _pytest.deprecated import FILLFUNCARGS from _pytest.deprecated import MARKED_FIXTURE -from _pytest.deprecated import NODE_FSPATH from _pytest.deprecated import YIELD_FIXTURE from _pytest.mark import Mark from _pytest.mark import ParameterSet From a1b10b552abeec24a859a4596eef530d1c6f46da Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 9 Oct 2022 20:23:53 +0100 Subject: [PATCH 14/62] use getfixturemarker --- src/_pytest/mark/structures.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 32137e8fd..31797f563 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -387,11 +387,14 @@ def store_mark(obj, mark: Mark) -> None: This is used to implement the Mark declarations/decorators correctly. """ assert isinstance(mark, Mark), mark - # Always reassign name to avoid updating pytestmark in a reference that - # was only borrowed. - if hasattr(obj, "_pytestfixturefunction"): + + from ..fixtures import getfixturemarker + + if getfixturemarker(obj) is not None: warnings.warn(MARKED_FIXTURE, stacklevel=2) + # Always reassign name to avoid updating pytestmark in a reference that + # was only borrowed. obj.pytestmark = [*get_unpacked_marks(obj), mark] From 24fd2928780ea1f073d26a9ba415d62ff4101138 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 10 Oct 2022 12:28:14 +0100 Subject: [PATCH 15/62] Update changelog/3664.deprecation.rst Co-authored-by: Bruno Oliveira --- changelog/3664.deprecation.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/changelog/3664.deprecation.rst b/changelog/3664.deprecation.rst index 6436910a6..0a00e26c1 100644 --- a/changelog/3664.deprecation.rst +++ b/changelog/3664.deprecation.rst @@ -1 +1,3 @@ -Applying a mark to a fixture function is deprecated. Doing so has no effect. +Applying a mark to a fixture function now issues a warning: marks in fixtures never had any effect, but it is a common user error to apply a mark to a fixture (for example ``usefixtures``) and expect it to work. + +This will become an error in the future. From d86df89a9291c29e19af3a762bd38bd959f1325c Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 10 Oct 2022 12:28:22 +0100 Subject: [PATCH 16/62] Update doc/en/deprecations.rst Co-authored-by: Bruno Oliveira --- doc/en/deprecations.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 8ff0fe30d..25be02d41 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -278,7 +278,18 @@ Applying a mark to a fixture function .. deprecated:: 7.2 -Applying a mark to a fixture function is deprecated. Doing so has no effect, and will raise an error in the next version. +Applying a mark to a fixture function never had any effect, but it is a common user error. + +.. code-block:: python + + @pytest.mark.usefixtures("clean_database") + @pytest.fixture + def user() -> User: + ... + +Users expected in this case that the ``usefixtures`` mark would have its intended effect of using the ``clean_database`` fixture when ``user`` was invoked, when in fact it has no effect at all. + +Now pytest will issue a warning when it encounters this problem, and will raise an error in the future versions. Backward compatibilities in ``Parser.addoption`` From 7759a9d3c2d6805f1e080512f40a3ada9207e66c Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 10 Oct 2022 12:28:30 +0100 Subject: [PATCH 17/62] Update src/_pytest/deprecated.py Co-authored-by: Bruno Oliveira --- src/_pytest/deprecated.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index d6b48a3c9..fb528e38b 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -107,7 +107,10 @@ HOOK_LEGACY_MARKING = UnformattedWarning( "#configuring-hook-specs-impls-using-markers", ) -MARKED_FIXTURE = PytestDeprecationWarning("Marks applied to fixtures have no effect") +MARKED_FIXTURE = PytestDeprecationWarning( + "Marks applied to fixtures have no effect\n" + "See docs: https://docs.pytest.org/en/stable/deprecations.html#applying-a-mark-to-a-fixture-function" +) # You want to make some `__init__` or function "private". # From 0de2aa93f0e214341af40f63f498a0498cc07832 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 11:30:13 +0000 Subject: [PATCH 18/62] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- doc/en/deprecations.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 25be02d41..e61bf5567 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -281,7 +281,6 @@ Applying a mark to a fixture function Applying a mark to a fixture function never had any effect, but it is a common user error. .. code-block:: python - @pytest.mark.usefixtures("clean_database") @pytest.fixture def user() -> User: From f7542f629232633b8c9bfeee5c14de19e1070d7f Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 10 Oct 2022 13:48:26 +0100 Subject: [PATCH 19/62] move tests into deprecated_test, and test for number of warnings issued --- testing/deprecated_test.py | 32 ++++++++++++++++++++++++++++++++ testing/python/fixtures.py | 18 ------------------ 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index d468b4435..01b91ce6d 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -279,3 +279,35 @@ def test_importing_instance_is_deprecated(pytester: Pytester) -> None: match=re.escape("The pytest.Instance collector type is deprecated"), ): from _pytest.python import Instance # noqa: F401 + + +def test_fixture_disallow_on_marked_functions(): + """Test that applying @pytest.fixture to a marked function warns (#3364).""" + with pytest.warns( + pytest.PytestDeprecationWarning, + match=r"Marks applied to fixtures have no effect", + ) as record: + + @pytest.fixture + @pytest.mark.parametrize("example", ["hello"]) + @pytest.mark.usefixtures("tmp_path") + def foo(): + raise NotImplementedError() + + assert len(record) == 1 + + +def test_fixture_disallow_marks_on_fixtures(): + """Test that applying a mark to a fixture warns (#3364).""" + with pytest.warns( + pytest.PytestDeprecationWarning, + match=r"Marks applied to fixtures have no effect", + ) as record: + + @pytest.mark.parametrize("example", ["hello"]) + @pytest.mark.usefixtures("tmp_path") + @pytest.fixture + def foo(): + raise NotImplementedError() + + assert len(record) == 2 diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 4f53ffddc..3ce5cb34d 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -3620,24 +3620,6 @@ class TestShowFixtures: def foo(): raise NotImplementedError() - def test_fixture_disallow_on_marked_functions(self): - """Test that applying @pytest.fixture to a marked function warns (#3364).""" - with pytest.warns(pytest.PytestDeprecationWarning): - - @pytest.fixture - @pytest.mark.usefixtures("tmp_path") - def foo(): - raise NotImplementedError() - - def test_fixture_disallow_marks_on_fixtures(self): - """Test that applying a mark to a fixture warns (#3364).""" - with pytest.warns(pytest.PytestDeprecationWarning): - - @pytest.mark.usefixtures("tmp_path") - @pytest.fixture - def foo(): - raise NotImplementedError() - class TestContextManagerFixtureFuncs: def test_simple(self, pytester: Pytester) -> None: From 7f73722c4adedd3b9da6d7efe5b699a5bbc0ee0d Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 10 Oct 2022 13:52:26 +0100 Subject: [PATCH 20/62] add a test for fixture between mark decorators --- testing/deprecated_test.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 01b91ce6d..2934e69cc 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -294,6 +294,9 @@ def test_fixture_disallow_on_marked_functions(): def foo(): raise NotImplementedError() + # it's only possible to get one warning here because you're already prevented + # from applying @fixture twice + # ValueError("fixture is being applied more than once to the same function") assert len(record) == 1 @@ -310,4 +313,20 @@ def test_fixture_disallow_marks_on_fixtures(): def foo(): raise NotImplementedError() - assert len(record) == 2 + assert len(record) == 2 # one for each mark decorator + + +def test_fixture_disallowed_between_marks(): + """Test that applying a mark to a fixture warns (#3364).""" + with pytest.warns( + pytest.PytestDeprecationWarning, + match=r"Marks applied to fixtures have no effect", + ) as record: + + @pytest.mark.parametrize("example", ["hello"]) + @pytest.fixture + @pytest.mark.usefixtures("tmp_path") + def foo(): + raise NotImplementedError() + + assert len(record) == 2 # one for each mark decorator From 246ceb84bdd7999225b44e10f220ede1d75c21d5 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 10 Oct 2022 13:58:05 +0100 Subject: [PATCH 21/62] Update doc/en/deprecations.rst --- doc/en/deprecations.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index e5fe61ab6..17055dfb9 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -388,6 +388,7 @@ Applying a mark to a fixture function Applying a mark to a fixture function never had any effect, but it is a common user error. .. code-block:: python + @pytest.mark.usefixtures("clean_database") @pytest.fixture def user() -> User: From f1c75851846c5b709049fa92d609b4736b72474b Mon Sep 17 00:00:00 2001 From: Stefanie Molin <24376333+stefmolin@users.noreply.github.com> Date: Fri, 31 Mar 2023 10:00:45 -0700 Subject: [PATCH 22/62] Update fixture scope in package/directory fixture example. --- doc/en/example/simple.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index fa3e68ce9..97a6dd9f4 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -691,7 +691,7 @@ Here is an example for making a ``db`` fixture available in a directory: pass - @pytest.fixture(scope="session") + @pytest.fixture(scope="package") def db(): return DB() From a2feb6bd000fef069ec3065d46470f59c2d24450 Mon Sep 17 00:00:00 2001 From: Erik Hasse Date: Thu, 18 May 2023 00:19:36 -0500 Subject: [PATCH 23/62] add a warning about modifying the root logger during tests --- AUTHORS | 1 + changelog/11011.doc.rst | 1 + doc/en/how-to/logging.rst | 7 +++++++ 3 files changed, 9 insertions(+) create mode 100644 changelog/11011.doc.rst diff --git a/AUTHORS b/AUTHORS index 92b0b2a1a..b5b4cc1a1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -128,6 +128,7 @@ Eric Hunsberger Eric Liu Eric Siegerman Erik Aronesty +Erik Hasse Erik M. Bray Evan Kepner Fabien Zarifian diff --git a/changelog/11011.doc.rst b/changelog/11011.doc.rst new file mode 100644 index 000000000..5faabba9c --- /dev/null +++ b/changelog/11011.doc.rst @@ -0,0 +1 @@ +Added a warning about modifying the root logger during tests when using ``caplog``. diff --git a/doc/en/how-to/logging.rst b/doc/en/how-to/logging.rst index 9957a9bb8..b9f522fa4 100644 --- a/doc/en/how-to/logging.rst +++ b/doc/en/how-to/logging.rst @@ -172,6 +172,13 @@ the records for the ``setup`` and ``call`` stages during teardown like so: The full API is available at :class:`pytest.LogCaptureFixture`. +.. warning:: + + The ``caplog`` fixture adds a handler to the root logger to capture logs. If the root logger is + modified during a test, for example with ``logging.config.dictConfig``, this handler may be + removed and cause no logs to be captured. To avoid this, ensure that any root logger configuration + only adds to the existing handlers. + .. _live_logs: From 1daa8129c656af9352f25f9ace9cedcc7a262386 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 4 Jun 2023 00:26:58 +0000 Subject: [PATCH 24/62] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 152 ++++++++++++++++--------------- 1 file changed, 80 insertions(+), 72 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index fafc8c876..ca8851804 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -11,7 +11,7 @@ automatically. Packages classified as inactive are excluded. creating a PDF, because otherwise the table gets far too wide for the page. -This list contains 1259 plugins. +This list contains 1260 plugins. .. only:: not latex @@ -83,7 +83,7 @@ This list contains 1259 plugins. :pypi:`pytest-astropy-header` pytest plugin to add diagnostic information to the header of the test output Sep 06, 2022 3 - Alpha pytest (>=4.6) :pypi:`pytest-ast-transformer` May 04, 2019 3 - Alpha pytest :pypi:`pytest-asyncio` Pytest support for asyncio Mar 19, 2023 4 - Beta pytest (>=7.0.0) - :pypi:`pytest-asyncio-cooperative` Run all your asynchronous tests cooperatively. Feb 10, 2023 N/A N/A + :pypi:`pytest-asyncio-cooperative` Run all your asynchronous tests cooperatively. May 31, 2023 N/A N/A :pypi:`pytest-asyncio-network-simulator` pytest-asyncio-network-simulator: Plugin for pytest for simulator the network in tests Jul 31, 2018 3 - Alpha pytest (<3.7.0,>=3.3.2) :pypi:`pytest-async-mongodb` pytest plugin for async MongoDB Oct 18, 2017 5 - Production/Stable pytest (>=2.5.2) :pypi:`pytest-async-sqlalchemy` Database testing fixtures using the SQLAlchemy asyncio API Oct 07, 2021 4 - Beta pytest (>=6.0.0) @@ -213,7 +213,7 @@ This list contains 1259 plugins. :pypi:`pytest-concurrent` Concurrently execute test cases with multithread, multiprocess and gevent Jan 12, 2019 4 - Beta pytest (>=3.1.1) :pypi:`pytest-config` Base configurations and utilities for developing your Python project test suite with pytest. Nov 07, 2014 5 - Production/Stable N/A :pypi:`pytest-confluence-report` Package stands for pytest plugin to upload results into Confluence page. Apr 17, 2022 N/A N/A - :pypi:`pytest-console-scripts` Pytest plugin for testing console scripts May 22, 2023 4 - Beta N/A + :pypi:`pytest-console-scripts` Pytest plugin for testing console scripts May 31, 2023 4 - Beta pytest (>=4.0.0) :pypi:`pytest-consul` pytest plugin with fixtures for testing consul aware apps Nov 24, 2018 3 - Alpha pytest :pypi:`pytest-container` Pytest fixtures for writing container based tests Mar 21, 2023 4 - Beta pytest (>=3.10) :pypi:`pytest-contextfixture` Define pytest fixtures as context managers. Mar 12, 2013 4 - Beta N/A @@ -228,7 +228,7 @@ This list contains 1259 plugins. :pypi:`pytest-coveragemarkers` Using pytest markers to track functional coverage and filtering of tests Nov 29, 2022 N/A pytest (>=7.1.2,<8.0.0) :pypi:`pytest-cov-exclude` Pytest plugin for excluding tests based on coverage data Apr 29, 2016 4 - Beta pytest (>=2.8.0,<2.9.0); extra == 'dev' :pypi:`pytest-cpp` Use pytest's runner to discover and execute C++ tests Jan 30, 2023 5 - Production/Stable pytest (>=7.0) - :pypi:`pytest-cppython` A pytest plugin that imports CPPython testing types May 25, 2023 N/A N/A + :pypi:`pytest-cppython` A pytest plugin that imports CPPython testing types Jun 02, 2023 N/A N/A :pypi:`pytest-cqase` Custom qase pytest plugin Aug 22, 2022 N/A pytest (>=7.1.2,<8.0.0) :pypi:`pytest-cram` Run cram tests with pytest. Aug 08, 2020 N/A N/A :pypi:`pytest-crate` Manages CrateDB instances during your integration tests May 28, 2019 3 - Alpha pytest (>=4.0) @@ -268,6 +268,7 @@ This list contains 1259 plugins. :pypi:`pytest-db` Session scope fixture "db" for mysql query or change Dec 04, 2019 N/A N/A :pypi:`pytest-dbfixtures` Databases fixtures plugin for py.test. Dec 07, 2016 4 - Beta N/A :pypi:`pytest-db-plugin` Nov 27, 2021 N/A pytest (>=5.0) + :pypi:`pytest-dbt` Unit test dbt models with standard python tooling May 31, 2023 2 - Pre-Alpha pytest (>=7.0.0,<8.0.0) :pypi:`pytest-dbt-adapter` A pytest plugin for testing dbt adapter plugins Nov 24, 2021 N/A pytest (<7,>=6) :pypi:`pytest-dbt-conventions` A pytest plugin for linting a dbt project's conventions Mar 02, 2022 N/A pytest (>=6.2.5,<7.0.0) :pypi:`pytest-dbt-core` Pytest extension for dbt. May 03, 2023 N/A pytest (>=6.2.5) ; extra == 'test' @@ -366,13 +367,13 @@ This list contains 1259 plugins. :pypi:`pytest-eliot` An eliot plugin for pytest. Aug 31, 2022 1 - Planning pytest (>=5.4.0) :pypi:`pytest-elk-reporter` A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0) :pypi:`pytest-email` Send execution result email Jul 08, 2020 N/A pytest - :pypi:`pytest-embedded` pytest embedded plugin Apr 11, 2023 N/A pytest (>=7.0) - :pypi:`pytest-embedded-arduino` pytest embedded plugin for Arduino projects Apr 11, 2023 N/A N/A - :pypi:`pytest-embedded-idf` pytest embedded plugin for esp-idf project Apr 11, 2023 N/A N/A - :pypi:`pytest-embedded-jtag` pytest embedded plugin for testing with jtag Apr 11, 2023 N/A N/A - :pypi:`pytest-embedded-qemu` pytest embedded plugin for qemu, not target chip Apr 11, 2023 N/A N/A - :pypi:`pytest-embedded-serial` pytest embedded plugin for testing serial ports Apr 11, 2023 N/A N/A - :pypi:`pytest-embedded-serial-esp` pytest embedded plugin for testing espressif boards via serial ports Apr 11, 2023 N/A N/A + :pypi:`pytest-embedded` pytest embedded plugin May 31, 2023 N/A pytest (>=7.0) + :pypi:`pytest-embedded-arduino` pytest embedded plugin for Arduino projects May 31, 2023 N/A N/A + :pypi:`pytest-embedded-idf` pytest embedded plugin for esp-idf project May 31, 2023 N/A N/A + :pypi:`pytest-embedded-jtag` pytest embedded plugin for testing with jtag May 31, 2023 N/A N/A + :pypi:`pytest-embedded-qemu` pytest embedded plugin for qemu, not target chip May 31, 2023 N/A N/A + :pypi:`pytest-embedded-serial` pytest embedded plugin for testing serial ports May 31, 2023 N/A N/A + :pypi:`pytest-embedded-serial-esp` pytest embedded plugin for testing espressif boards via serial ports May 31, 2023 N/A N/A :pypi:`pytest-embrace` 💝 Dataclasses-as-tests. Describe the runtime once and multiply coverage with no boilerplate. Mar 25, 2023 N/A pytest (>=7.0,<8.0) :pypi:`pytest-emoji` A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1) :pypi:`pytest-emoji-output` Pytest plugin to represent test output with emoji support Apr 09, 2023 4 - Beta pytest (==7.0.1) @@ -486,7 +487,7 @@ This list contains 1259 plugins. :pypi:`pytest-gherkin` A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) :pypi:`pytest-gh-log-group` pytest plugin for gh actions Jan 11, 2022 3 - Alpha pytest :pypi:`pytest-ghostinspector` For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A - :pypi:`pytest-girder` A set of pytest fixtures for testing Girder applications. May 22, 2023 N/A N/A + :pypi:`pytest-girder` A set of pytest fixtures for testing Girder applications. May 31, 2023 N/A N/A :pypi:`pytest-git` Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest :pypi:`pytest-gitcov` Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A :pypi:`pytest-git-fixtures` Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest @@ -518,7 +519,7 @@ This list contains 1259 plugins. :pypi:`pytest-historic` Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest :pypi:`pytest-historic-hook` Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest :pypi:`pytest-homeassistant` A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A - :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components May 24, 2023 3 - Alpha pytest (==7.3.1) + :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Jun 02, 2023 3 - Alpha pytest (==7.3.1) :pypi:`pytest-honey` A simple plugin to use with pytest Jan 07, 2022 4 - Beta pytest (>=3.5.0) :pypi:`pytest-honors` Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A :pypi:`pytest-hot-reloading` May 18, 2023 N/A N/A @@ -540,7 +541,7 @@ This list contains 1259 plugins. :pypi:`pytest-http-mocker` Pytest plugin for http mocking (via https://github.com/vilus/mocker) Oct 20, 2019 N/A N/A :pypi:`pytest-httpretty` A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A :pypi:`pytest-httpserver` pytest-httpserver is a httpserver for pytest May 22, 2023 3 - Alpha N/A - :pypi:`pytest-httptesting` http_testing framework on top of pytest Apr 19, 2023 N/A pytest (>=7.2.0,<8.0.0) + :pypi:`pytest-httptesting` http_testing framework on top of pytest Jun 03, 2023 N/A pytest (>=7.2.0,<8.0.0) :pypi:`pytest-httpx` Send responses to httpx. Apr 12, 2023 5 - Production/Stable pytest (<8.0,>=6.0) :pypi:`pytest-httpx-blockage` Disable httpx requests during a test run Feb 16, 2023 N/A pytest (>=7.2.1) :pypi:`pytest-hue` Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A @@ -573,7 +574,7 @@ This list contains 1259 plugins. :pypi:`pytest-interactive` A pytest plugin for console based interactive test selection just after the collection phase Nov 30, 2017 3 - Alpha N/A :pypi:`pytest-intercept-remote` Pytest plugin for intercepting outgoing connection requests during pytest run. May 24, 2021 4 - Beta pytest (>=4.6) :pypi:`pytest-interface-tester` Pytest plugin for checking charm relation interface protocol compliance. May 09, 2023 4 - Beta pytest - :pypi:`pytest-invenio` Pytest fixtures for Invenio. Apr 13, 2023 5 - Production/Stable pytest (<7.2.0,>=6) + :pypi:`pytest-invenio` Pytest fixtures for Invenio. Jun 02, 2023 5 - Production/Stable pytest (<7.2.0,>=6) :pypi:`pytest-involve` Run tests covering a specific file or changeset Feb 02, 2020 4 - Beta pytest (>=3.5.0) :pypi:`pytest-ipdb` A py.test plug-in to enable drop to ipdb debugger on test failure. Mar 20, 2013 2 - Pre-Alpha N/A :pypi:`pytest-ipynb` THIS PROJECT IS ABANDONED Jan 29, 2019 3 - Alpha N/A @@ -589,7 +590,7 @@ This list contains 1259 plugins. :pypi:`pytest-jinja` A plugin to generate customizable jinja-based HTML reports in pytest Oct 04, 2022 3 - Alpha pytest (>=6.2.5,<7.0.0) :pypi:`pytest-jira` py.test JIRA integration plugin, using markers Apr 07, 2022 3 - Alpha N/A :pypi:`pytest-jira-xfail` Plugin skips (xfail) tests if unresolved Jira issue(s) linked Dec 01, 2022 N/A pytest (~=7.2.0) - :pypi:`pytest-jira-xray` pytest plugin to integrate tests with JIRA XRAY May 15, 2023 4 - Beta pytest + :pypi:`pytest-jira-xray` pytest plugin to integrate tests with JIRA XRAY May 29, 2023 4 - Beta pytest :pypi:`pytest-job-selection` A pytest plugin for load balancing test suites Jan 30, 2023 4 - Beta pytest (>=3.5.0) :pypi:`pytest-jobserver` Limit parallel tests with posix jobserver. May 15, 2019 5 - Production/Stable pytest :pypi:`pytest-joke` Test failures are better served with humor. Oct 08, 2019 4 - Beta pytest (>=4.2.1) @@ -620,7 +621,7 @@ This list contains 1259 plugins. :pypi:`pytest-launchable` Launchable Pytest Plugin Apr 05, 2023 N/A pytest (>=4.2.0) :pypi:`pytest-layab` Pytest fixtures for layab. Oct 05, 2020 5 - Production/Stable N/A :pypi:`pytest-lazy-fixture` It helps to use fixtures in pytest.mark.parametrize Feb 01, 2020 4 - Beta pytest (>=3.2.5) - :pypi:`pytest-lazy-fixtures` Allows you to use fixtures in @pytest.mark.parametrize. Mar 11, 2023 N/A pytest (>=7.2.1,<8.0.0) + :pypi:`pytest-lazy-fixtures` Allows you to use fixtures in @pytest.mark.parametrize. May 28, 2023 N/A pytest (>=7.2.1,<8.0.0) :pypi:`pytest-ldap` python-ldap fixtures for pytest Aug 18, 2020 N/A pytest :pypi:`pytest-leak-finder` Find the test that's leaking before the one that fails Feb 15, 2023 4 - Beta pytest (>=3.5.0) :pypi:`pytest-leaks` A pytest plugin to trace resource leaks. Nov 27, 2019 1 - Planning N/A @@ -649,7 +650,7 @@ This list contains 1259 plugins. :pypi:`pytest-logger` Plugin configuring handlers for loggers from Python logging module. Jul 25, 2019 4 - Beta pytest (>=3.2) :pypi:`pytest-logging` Configures logging and allows tweaking the log level with a py.test flag Nov 04, 2015 4 - Beta N/A :pypi:`pytest-logging-end-to-end-test-tool` Sep 23, 2022 N/A pytest (>=7.1.2,<8.0.0) - :pypi:`pytest-logikal` Common testing environment May 27, 2023 5 - Production/Stable pytest (==7.3.1) + :pypi:`pytest-logikal` Common testing environment Jun 03, 2023 5 - Production/Stable pytest (==7.3.1) :pypi:`pytest-log-report` Package for creating a pytest test run reprot Dec 26, 2019 N/A N/A :pypi:`pytest-loguru` Pytest Loguru Apr 12, 2022 5 - Production/Stable N/A :pypi:`pytest-loop` pytest plugin for looping tests Jul 22, 2022 5 - Production/Stable pytest (>=6) @@ -671,7 +672,7 @@ This list contains 1259 plugins. :pypi:`pytest-maybe-raises` Pytest fixture for optional exception testing. May 27, 2022 N/A pytest ; extra == 'dev' :pypi:`pytest-mccabe` pytest plugin to run the mccabe code complexity checker. Jul 22, 2020 3 - Alpha pytest (>=5.4.0) :pypi:`pytest-md` Plugin for generating Markdown reports for pytest results Jul 11, 2019 3 - Alpha pytest (>=4.2.1) - :pypi:`pytest-md-report` A pytest plugin to make a test results report with Markdown table format. Aug 06, 2022 4 - Beta pytest (!=6.0.0,<8,>=3.3.2) + :pypi:`pytest-md-report` A pytest plugin to make a test results report with Markdown table format. May 28, 2023 4 - Beta pytest (!=6.0.0,<8,>=3.3.2) :pypi:`pytest-memlog` Log memory usage during tests May 03, 2023 N/A pytest (>=7.3.0,<8.0.0) :pypi:`pytest-memprof` Estimates memory consumption of test functions Mar 29, 2019 4 - Beta N/A :pypi:`pytest-memray` A simple plugin to use with pytest Dec 02, 2022 N/A pytest>=7.2 @@ -797,7 +798,7 @@ This list contains 1259 plugins. :pypi:`pytest-pep257` py.test plugin for pep257 Jul 09, 2016 N/A N/A :pypi:`pytest-pep8` pytest plugin to check PEP8 requirements Apr 27, 2014 N/A N/A :pypi:`pytest-percent` Change the exit code of pytest test sessions when a required percent of tests pass. May 21, 2020 N/A pytest (>=5.2.0) - :pypi:`pytest-perf` pytest-perf Jun 23, 2022 5 - Production/Stable pytest (>=6) ; extra == 'testing' + :pypi:`pytest-perf` Run performance tests against the mainline code. Jun 02, 2023 5 - Production/Stable pytest (>=6) ; extra == 'testing' :pypi:`pytest-performance` A simple plugin to ensure the execution of critical sections of code has not been impacted Sep 11, 2020 5 - Production/Stable pytest (>=3.7.0) :pypi:`pytest-persistence` Pytest tool for persistent objects May 16, 2023 N/A N/A :pypi:`pytest-pg` A tiny plugin for pytest which runs PostgreSQL in Docker May 04, 2023 5 - Production/Stable pytest (>=6.0.0) @@ -863,7 +864,7 @@ This list contains 1259 plugins. :pypi:`pytest-pydocstyle` pytest plugin to run pydocstyle Jan 05, 2023 3 - Alpha N/A :pypi:`pytest-pylint` pytest plugin to check source code with pylint Sep 10, 2022 5 - Production/Stable pytest (>=5.4) :pypi:`pytest-pymysql-autorecord` Record PyMySQL queries and mock with the stored data. Sep 02, 2022 N/A N/A - :pypi:`pytest-pyodide` "Pytest plugin for testing applications that use Pyodide" May 10, 2023 N/A pytest + :pypi:`pytest-pyodide` "Pytest plugin for testing applications that use Pyodide" Jun 02, 2023 N/A pytest :pypi:`pytest-pypi` Easily test your HTTP library against a local copy of pypi Mar 04, 2018 3 - Alpha N/A :pypi:`pytest-pypom-navigation` Core engine for cookiecutter-qa and pytest-play packages Feb 18, 2019 4 - Beta pytest (>=3.0.7) :pypi:`pytest-pyppeteer` A plugin to run pyppeteer in pytest Apr 28, 2022 N/A pytest (>=6.2.5,<7.0.0) @@ -881,7 +882,7 @@ This list contains 1259 plugins. :pypi:`pytest-qaseio` Pytest plugin for Qase.io integration May 11, 2023 4 - Beta pytest (>=7.2.2,<8.0.0) :pypi:`pytest-qasync` Pytest support for qasync. Jul 12, 2021 4 - Beta pytest (>=5.4.0) :pypi:`pytest-qatouch` Pytest plugin for uploading test results to your QA Touch Testrun. Feb 14, 2023 4 - Beta pytest (>=6.2.0) - :pypi:`pytest-qgis` A pytest plugin for testing QGIS python plugins Jun 26, 2022 5 - Production/Stable pytest (>=6.2.3) + :pypi:`pytest-qgis` A pytest plugin for testing QGIS python plugins May 31, 2023 5 - Production/Stable pytest (>=6.2.5) :pypi:`pytest-qml` Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) :pypi:`pytest-qr` pytest plugin to generate test result QR codes Nov 25, 2021 4 - Beta N/A :pypi:`pytest-qt` pytest support for PyQt and PySide applications Oct 25, 2022 5 - Production/Stable pytest (>=3.0.0) @@ -911,7 +912,7 @@ This list contains 1259 plugins. :pypi:`pytest-redmine` Pytest plugin for redmine Mar 19, 2018 1 - Planning N/A :pypi:`pytest-ref` A plugin to store reference files to ease regression testing Nov 23, 2019 4 - Beta pytest (>=3.5.0) :pypi:`pytest-reference-formatter` Conveniently run pytest with a dot-formatted test reference. Oct 01, 2019 4 - Beta N/A - :pypi:`pytest-regex` Select pytest tests with regular expressions May 23, 2023 4 - Beta pytest (>=3.5.0) + :pypi:`pytest-regex` Select pytest tests with regular expressions May 29, 2023 4 - Beta pytest (>=3.5.0) :pypi:`pytest-regex-dependency` Management of Pytest dependencies via regex patterns Jun 12, 2022 N/A pytest :pypi:`pytest-regressions` Easy to use fixtures to write regression tests. Jan 13, 2023 5 - Production/Stable pytest (>=6.2.0) :pypi:`pytest-regtest` pytest plugin for regression tests Jul 08, 2022 N/A N/A @@ -945,7 +946,7 @@ This list contains 1259 plugins. :pypi:`pytest-rerunfailures` pytest plugin to re-run tests to eliminate flaky failures Mar 09, 2023 5 - Production/Stable pytest (>=5.3) :pypi:`pytest-rerunfailures-all-logs` pytest plugin to re-run tests to eliminate flaky failures Mar 07, 2022 5 - Production/Stable N/A :pypi:`pytest-reserial` Pytest fixture for recording and replaying serial port traffic. Apr 26, 2023 4 - Beta pytest - :pypi:`pytest-resilient-circuits` Resilient Circuits fixtures for PyTest May 05, 2023 N/A pytest (~=4.6) ; python_version == "2.7" + :pypi:`pytest-resilient-circuits` Resilient Circuits fixtures for PyTest Jun 01, 2023 N/A pytest (~=4.6) ; python_version == "2.7" :pypi:`pytest-resource` Load resource fixture plugin to use with pytest Nov 14, 2018 4 - Beta N/A :pypi:`pytest-resource-path` Provides path for uniform access to test resources in isolated directory May 01, 2021 5 - Production/Stable pytest (>=3.5.0) :pypi:`pytest-resource-usage` Pytest plugin for reporting running time and peak memory usage Nov 06, 2022 5 - Production/Stable pytest>=7.0.0 @@ -991,14 +992,14 @@ This list contains 1259 plugins. :pypi:`pytest-sanic` a pytest plugin for Sanic Oct 25, 2021 N/A pytest (>=5.2) :pypi:`pytest-sanity` Dec 07, 2020 N/A N/A :pypi:`pytest-sa-pg` May 14, 2019 N/A N/A - :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. May 25, 2023 5 - Production/Stable N/A + :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Jun 02, 2023 5 - Production/Stable N/A :pypi:`pytest-scenario` pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A :pypi:`pytest-schedule` The job of test scheduling for humans. Jan 07, 2023 5 - Production/Stable N/A :pypi:`pytest-schema` 👍 Validate return values against a schema-like object in testing Mar 14, 2022 5 - Production/Stable pytest (>=3.5.0) :pypi:`pytest-securestore` An encrypted password store for use within pytest cases Nov 08, 2021 4 - Beta N/A :pypi:`pytest-select` A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) - :pypi:`pytest-selenium` pytest plugin for Selenium Sep 21, 2022 5 - Production/Stable pytest (>=6.0.0,<7.0.0) - :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. May 25, 2023 5 - Production/Stable N/A + :pypi:`pytest-selenium` pytest plugin for Selenium May 28, 2023 5 - Production/Stable pytest>=6.0.0 + :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Jun 02, 2023 5 - Production/Stable N/A :pypi:`pytest-selenium-enhancer` pytest plugin for Selenium Apr 29, 2022 5 - Production/Stable N/A :pypi:`pytest-selenium-pdiff` A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A :pypi:`pytest-send-email` Send pytest execution result email Dec 04, 2019 N/A N/A @@ -1227,18 +1228,18 @@ This list contains 1259 plugins. :pypi:`pytest-vscodedebug` A pytest plugin to easily enable debugging tests within Visual Studio Code Dec 04, 2020 4 - Beta N/A :pypi:`pytest-vscode-pycharm-cls` A PyTest helper to enable start remote debugger on test start or failure or when pytest.set_trace is used. Feb 01, 2023 N/A pytest :pypi:`pytest-vts` pytest plugin for automatic recording of http stubbed tests Jun 05, 2019 N/A pytest (>=2.3) - :pypi:`pytest-vulture` A pytest plugin to checks dead code with vulture May 19, 2023 N/A pytest (>=7.0.0) + :pypi:`pytest-vulture` A pytest plugin to checks dead code with vulture Jun 01, 2023 N/A pytest (>=7.0.0) :pypi:`pytest-vw` pytest-vw makes your failing test cases succeed under CI tools scrutiny Oct 07, 2015 4 - Beta N/A :pypi:`pytest-vyper` Plugin for the vyper smart contract language. May 28, 2020 2 - Pre-Alpha N/A :pypi:`pytest-wa-e2e-plugin` Pytest plugin for testing whatsapp bots with end to end tests Feb 18, 2020 4 - Beta pytest (>=3.5.0) :pypi:`pytest-wake` May 11, 2023 N/A pytest :pypi:`pytest-watch` Local continuous test runner with pytest and watchdog. May 20, 2018 N/A N/A - :pypi:`pytest-watcher` Continiously runs pytest on changes in \*.py files Dec 11, 2022 3 - Alpha N/A + :pypi:`pytest-watcher` Automatically rerun your tests on file modifications Jun 03, 2023 4 - Beta N/A :pypi:`pytest-wdl` Pytest plugin for testing WDL workflows. Nov 17, 2020 5 - Production/Stable N/A :pypi:`pytest-web3-data` Sep 15, 2022 4 - Beta pytest :pypi:`pytest-webdriver` Selenium webdriver fixture for py.test May 28, 2019 5 - Production/Stable pytest :pypi:`pytest-wetest` Welian API Automation test framework pytest plugin Nov 10, 2018 4 - Beta N/A - :pypi:`pytest-when` Utility which makes mocking more readable and controllable May 22, 2023 N/A pytest>=7.3.1 + :pypi:`pytest-when` Utility which makes mocking more readable and controllable May 31, 2023 N/A pytest>=7.3.1 :pypi:`pytest-whirlwind` Testing Tornado. Jun 12, 2020 N/A N/A :pypi:`pytest-wholenodeid` pytest addon for displaying the whole node id for failures Aug 26, 2015 4 - Beta pytest (>=2.0) :pypi:`pytest-win32consoletitle` Pytest progress in console title (Win32 only) Aug 08, 2021 N/A N/A @@ -1260,12 +1261,12 @@ This list contains 1259 plugins. :pypi:`pytest-xrayjira` Mar 17, 2020 3 - Alpha pytest (==4.3.1) :pypi:`pytest-xray-server` May 03, 2022 3 - Alpha pytest (>=5.3.1) :pypi:`pytest-xskynet` A package to prevent Dependency Confusion attacks against Yandex. Feb 10, 2023 N/A N/A - :pypi:`pytest-xvfb` A pytest plugin to run Xvfb for tests. Jun 09, 2020 4 - Beta pytest (>=2.8.1) + :pypi:`pytest-xvfb` A pytest plugin to run Xvfb (or Xephyr/Xvnc) for tests. May 29, 2023 4 - Beta pytest (>=2.8.1) :pypi:`pytest-yaml` This plugin is used to load yaml output to your test using pytest framework. Oct 05, 2018 N/A pytest - :pypi:`pytest-yaml-sanmu` pytest plugin for generating test cases by yaml Mar 17, 2023 N/A pytest>=7.2.0 + :pypi:`pytest-yaml-sanmu` pytest plugin for generating test cases by yaml May 28, 2023 N/A pytest>=7.2.0 :pypi:`pytest-yamltree` Create or check file/directory trees described by YAML Mar 02, 2020 4 - Beta pytest (>=3.1.1) :pypi:`pytest-yamlwsgi` Run tests against wsgi apps defined in yaml May 11, 2010 N/A N/A - :pypi:`pytest-yaml-yoyo` http/https API run by yaml May 22, 2023 N/A pytest (>=7.2.0) + :pypi:`pytest-yaml-yoyo` http/https API run by yaml May 30, 2023 N/A pytest (>=7.2.0) :pypi:`pytest-yapf` Run yapf Jul 06, 2017 4 - Beta pytest (>=3.1.1) :pypi:`pytest-yapf3` Validate your Python file format with yapf Mar 29, 2023 5 - Production/Stable pytest (>=7) :pypi:`pytest-yield` PyTest plugin to run tests concurrently, each \`yield\` switch context to other one Jan 23, 2019 N/A N/A @@ -1738,7 +1739,7 @@ This list contains 1259 plugins. Pytest support for asyncio :pypi:`pytest-asyncio-cooperative` - *last release*: Feb 10, 2023, + *last release*: May 31, 2023, *status*: N/A, *requires*: N/A @@ -2648,9 +2649,9 @@ This list contains 1259 plugins. Package stands for pytest plugin to upload results into Confluence page. :pypi:`pytest-console-scripts` - *last release*: May 22, 2023, + *last release*: May 31, 2023, *status*: 4 - Beta, - *requires*: N/A + *requires*: pytest (>=4.0.0) Pytest plugin for testing console scripts @@ -2753,7 +2754,7 @@ This list contains 1259 plugins. Use pytest's runner to discover and execute C++ tests :pypi:`pytest-cppython` - *last release*: May 25, 2023, + *last release*: Jun 02, 2023, *status*: N/A, *requires*: N/A @@ -3032,6 +3033,13 @@ This list contains 1259 plugins. + :pypi:`pytest-dbt` + *last release*: May 31, 2023, + *status*: 2 - Pre-Alpha, + *requires*: pytest (>=7.0.0,<8.0.0) + + Unit test dbt models with standard python tooling + :pypi:`pytest-dbt-adapter` *last release*: Nov 24, 2021, *status*: N/A, @@ -3719,49 +3727,49 @@ This list contains 1259 plugins. Send execution result email :pypi:`pytest-embedded` - *last release*: Apr 11, 2023, + *last release*: May 31, 2023, *status*: N/A, *requires*: pytest (>=7.0) pytest embedded plugin :pypi:`pytest-embedded-arduino` - *last release*: Apr 11, 2023, + *last release*: May 31, 2023, *status*: N/A, *requires*: N/A pytest embedded plugin for Arduino projects :pypi:`pytest-embedded-idf` - *last release*: Apr 11, 2023, + *last release*: May 31, 2023, *status*: N/A, *requires*: N/A pytest embedded plugin for esp-idf project :pypi:`pytest-embedded-jtag` - *last release*: Apr 11, 2023, + *last release*: May 31, 2023, *status*: N/A, *requires*: N/A pytest embedded plugin for testing with jtag :pypi:`pytest-embedded-qemu` - *last release*: Apr 11, 2023, + *last release*: May 31, 2023, *status*: N/A, *requires*: N/A pytest embedded plugin for qemu, not target chip :pypi:`pytest-embedded-serial` - *last release*: Apr 11, 2023, + *last release*: May 31, 2023, *status*: N/A, *requires*: N/A pytest embedded plugin for testing serial ports :pypi:`pytest-embedded-serial-esp` - *last release*: Apr 11, 2023, + *last release*: May 31, 2023, *status*: N/A, *requires*: N/A @@ -4559,7 +4567,7 @@ This list contains 1259 plugins. For finding/executing Ghost Inspector tests :pypi:`pytest-girder` - *last release*: May 22, 2023, + *last release*: May 31, 2023, *status*: N/A, *requires*: N/A @@ -4783,7 +4791,7 @@ This list contains 1259 plugins. A pytest plugin for use with homeassistant custom components. :pypi:`pytest-homeassistant-custom-component` - *last release*: May 24, 2023, + *last release*: Jun 02, 2023, *status*: 3 - Alpha, *requires*: pytest (==7.3.1) @@ -4937,7 +4945,7 @@ This list contains 1259 plugins. pytest-httpserver is a httpserver for pytest :pypi:`pytest-httptesting` - *last release*: Apr 19, 2023, + *last release*: Jun 03, 2023, *status*: N/A, *requires*: pytest (>=7.2.0,<8.0.0) @@ -5168,7 +5176,7 @@ This list contains 1259 plugins. Pytest plugin for checking charm relation interface protocol compliance. :pypi:`pytest-invenio` - *last release*: Apr 13, 2023, + *last release*: Jun 02, 2023, *status*: 5 - Production/Stable, *requires*: pytest (<7.2.0,>=6) @@ -5280,7 +5288,7 @@ This list contains 1259 plugins. Plugin skips (xfail) tests if unresolved Jira issue(s) linked :pypi:`pytest-jira-xray` - *last release*: May 15, 2023, + *last release*: May 29, 2023, *status*: 4 - Beta, *requires*: pytest @@ -5497,7 +5505,7 @@ This list contains 1259 plugins. It helps to use fixtures in pytest.mark.parametrize :pypi:`pytest-lazy-fixtures` - *last release*: Mar 11, 2023, + *last release*: May 28, 2023, *status*: N/A, *requires*: pytest (>=7.2.1,<8.0.0) @@ -5700,7 +5708,7 @@ This list contains 1259 plugins. :pypi:`pytest-logikal` - *last release*: May 27, 2023, + *last release*: Jun 03, 2023, *status*: 5 - Production/Stable, *requires*: pytest (==7.3.1) @@ -5854,7 +5862,7 @@ This list contains 1259 plugins. Plugin for generating Markdown reports for pytest results :pypi:`pytest-md-report` - *last release*: Aug 06, 2022, + *last release*: May 28, 2023, *status*: 4 - Beta, *requires*: pytest (!=6.0.0,<8,>=3.3.2) @@ -6736,11 +6744,11 @@ This list contains 1259 plugins. Change the exit code of pytest test sessions when a required percent of tests pass. :pypi:`pytest-perf` - *last release*: Jun 23, 2022, + *last release*: Jun 02, 2023, *status*: 5 - Production/Stable, *requires*: pytest (>=6) ; extra == 'testing' - pytest-perf + Run performance tests against the mainline code. :pypi:`pytest-performance` *last release*: Sep 11, 2020, @@ -7198,7 +7206,7 @@ This list contains 1259 plugins. Record PyMySQL queries and mock with the stored data. :pypi:`pytest-pyodide` - *last release*: May 10, 2023, + *last release*: Jun 02, 2023, *status*: N/A, *requires*: pytest @@ -7324,9 +7332,9 @@ This list contains 1259 plugins. Pytest plugin for uploading test results to your QA Touch Testrun. :pypi:`pytest-qgis` - *last release*: Jun 26, 2022, + *last release*: May 31, 2023, *status*: 5 - Production/Stable, - *requires*: pytest (>=6.2.3) + *requires*: pytest (>=6.2.5) A pytest plugin for testing QGIS python plugins @@ -7534,7 +7542,7 @@ This list contains 1259 plugins. Conveniently run pytest with a dot-formatted test reference. :pypi:`pytest-regex` - *last release*: May 23, 2023, + *last release*: May 29, 2023, *status*: 4 - Beta, *requires*: pytest (>=3.5.0) @@ -7772,7 +7780,7 @@ This list contains 1259 plugins. Pytest fixture for recording and replaying serial port traffic. :pypi:`pytest-resilient-circuits` - *last release*: May 05, 2023, + *last release*: Jun 01, 2023, *status*: N/A, *requires*: pytest (~=4.6) ; python_version == "2.7" @@ -8094,7 +8102,7 @@ This list contains 1259 plugins. :pypi:`pytest-sbase` - *last release*: May 25, 2023, + *last release*: Jun 02, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -8136,14 +8144,14 @@ This list contains 1259 plugins. A pytest plugin which allows to (de-)select tests from a file. :pypi:`pytest-selenium` - *last release*: Sep 21, 2022, + *last release*: May 28, 2023, *status*: 5 - Production/Stable, - *requires*: pytest (>=6.0.0,<7.0.0) + *requires*: pytest>=6.0.0 pytest plugin for Selenium :pypi:`pytest-seleniumbase` - *last release*: May 25, 2023, + *last release*: Jun 02, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -9746,7 +9754,7 @@ This list contains 1259 plugins. pytest plugin for automatic recording of http stubbed tests :pypi:`pytest-vulture` - *last release*: May 19, 2023, + *last release*: Jun 01, 2023, *status*: N/A, *requires*: pytest (>=7.0.0) @@ -9788,11 +9796,11 @@ This list contains 1259 plugins. Local continuous test runner with pytest and watchdog. :pypi:`pytest-watcher` - *last release*: Dec 11, 2022, - *status*: 3 - Alpha, + *last release*: Jun 03, 2023, + *status*: 4 - Beta, *requires*: N/A - Continiously runs pytest on changes in \*.py files + Automatically rerun your tests on file modifications :pypi:`pytest-wdl` *last release*: Nov 17, 2020, @@ -9823,7 +9831,7 @@ This list contains 1259 plugins. Welian API Automation test framework pytest plugin :pypi:`pytest-when` - *last release*: May 22, 2023, + *last release*: May 31, 2023, *status*: N/A, *requires*: pytest>=7.3.1 @@ -9977,11 +9985,11 @@ This list contains 1259 plugins. A package to prevent Dependency Confusion attacks against Yandex. :pypi:`pytest-xvfb` - *last release*: Jun 09, 2020, + *last release*: May 29, 2023, *status*: 4 - Beta, *requires*: pytest (>=2.8.1) - A pytest plugin to run Xvfb for tests. + A pytest plugin to run Xvfb (or Xephyr/Xvnc) for tests. :pypi:`pytest-yaml` *last release*: Oct 05, 2018, @@ -9991,7 +9999,7 @@ This list contains 1259 plugins. This plugin is used to load yaml output to your test using pytest framework. :pypi:`pytest-yaml-sanmu` - *last release*: Mar 17, 2023, + *last release*: May 28, 2023, *status*: N/A, *requires*: pytest>=7.2.0 @@ -10012,7 +10020,7 @@ This list contains 1259 plugins. Run tests against wsgi apps defined in yaml :pypi:`pytest-yaml-yoyo` - *last release*: May 22, 2023, + *last release*: May 30, 2023, *status*: N/A, *requires*: pytest (>=7.2.0) From 313b61471f47d7d95402110ed5960c1ffacd3157 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 4 Jun 2023 00:32:28 +0300 Subject: [PATCH 25/62] main: change pkg_roots to work with `Path`s instead of string paths - Works better on Windows (case sensitivity) - Simpler code --- src/_pytest/main.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index c9a495380..8f07daeec 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -686,8 +686,8 @@ class Session(nodes.FSCollector): # are not collected more than once. matchnodes_cache: Dict[Tuple[Type[nodes.Collector], str], CollectReport] = {} - # Dirnames of pkgs with dunder-init files. - pkg_roots: Dict[str, Package] = {} + # Directories of pkgs with dunder-init files. + pkg_roots: Dict[Path, Package] = {} for argpath, names in self._initial_parts: self.trace("processing argument", (argpath, names)) @@ -708,7 +708,7 @@ class Session(nodes.FSCollector): col = self._collectfile(pkginit, handle_dupes=False) if col: if isinstance(col[0], Package): - pkg_roots[str(parent)] = col[0] + pkg_roots[parent] = col[0] node_cache1[col[0].path] = [col[0]] # If it's a directory argument, recurse and look for any Subpackages. @@ -717,7 +717,7 @@ class Session(nodes.FSCollector): assert not names, f"invalid arg {(argpath, names)!r}" seen_dirs: Set[Path] = set() - for direntry in visit(str(argpath), self._recurse): + for direntry in visit(argpath, self._recurse): if not direntry.is_file(): continue @@ -732,8 +732,8 @@ class Session(nodes.FSCollector): for x in self._collectfile(pkginit): yield x if isinstance(x, Package): - pkg_roots[str(dirpath)] = x - if str(dirpath) in pkg_roots: + pkg_roots[dirpath] = x + if dirpath in pkg_roots: # Do not collect packages here. continue @@ -750,7 +750,7 @@ class Session(nodes.FSCollector): if argpath in node_cache1: col = node_cache1[argpath] else: - collect_root = pkg_roots.get(str(argpath.parent), self) + collect_root = pkg_roots.get(argpath.parent, self) col = collect_root._collectfile(argpath, handle_dupes=False) if col: node_cache1[argpath] = col From 85c5bd26b6d94dca423c2c0c54717750eac81cae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 12:06:55 +0200 Subject: [PATCH 26/62] build(deps): Bump pytest-xvfb in /testing/plugins_integration (#11079) Bumps [pytest-xvfb](https://github.com/The-Compiler/pytest-xvfb) from 2.0.0 to 3.0.0. - [Changelog](https://github.com/The-Compiler/pytest-xvfb/blob/master/CHANGELOG.rst) - [Commits](https://github.com/The-Compiler/pytest-xvfb/compare/v2.0.0...v3.0.0) --- updated-dependencies: - dependency-name: pytest-xvfb dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 0d30ab96f..7db8cc0ab 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -12,4 +12,4 @@ pytest-sugar==0.9.7 pytest-trio==0.7.0 pytest-twisted==1.14.0 twisted==22.8.0 -pytest-xvfb==2.0.0 +pytest-xvfb==3.0.0 From ae38b076da99ea0d4092a4be604ccc4b0f73d8af Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 5 Jun 2023 17:08:46 +0300 Subject: [PATCH 27/62] main: move norecursedir check to main's pytest_ignore_collect Fix #11081 --- changelog/11081.improvement.rst | 1 + src/_pytest/main.py | 9 ++++++--- src/_pytest/python.py | 3 --- 3 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 changelog/11081.improvement.rst diff --git a/changelog/11081.improvement.rst b/changelog/11081.improvement.rst new file mode 100644 index 000000000..227e478d4 --- /dev/null +++ b/changelog/11081.improvement.rst @@ -0,0 +1 @@ +The :confval:`norecursedir` check is now performed in a :hook:`pytest_ignore_collect` implementation, so plugins can affect it. diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 8f07daeec..155d4300e 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -400,6 +400,12 @@ def pytest_ignore_collect(collection_path: Path, config: Config) -> Optional[boo allow_in_venv = config.getoption("collect_in_virtualenv") if not allow_in_venv and _in_venv(collection_path): return True + + if collection_path.is_dir(): + norecursepatterns = config.getini("norecursedirs") + if any(fnmatch_ex(pat, collection_path) for pat in norecursepatterns): + return True + return None @@ -563,9 +569,6 @@ class Session(nodes.FSCollector): ihook = self.gethookproxy(fspath.parent) if ihook.pytest_ignore_collect(collection_path=fspath, config=self.config): return False - norecursepatterns = self.config.getini("norecursedirs") - if any(fnmatch_ex(pat, fspath) for pat in norecursepatterns): - return False return True def _collectfile( diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 3b1253cf4..ad847c8af 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -706,9 +706,6 @@ class Package(Module): ihook = self.session.gethookproxy(fspath.parent) if ihook.pytest_ignore_collect(collection_path=fspath, config=self.config): return False - norecursepatterns = self.config.getini("norecursedirs") - if any(fnmatch_ex(pat, fspath) for pat in norecursepatterns): - return False return True def _collectfile( From ee8baa2676c7f4ecb7e9e64db354b2cee75a18ff Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 12:13:38 +0300 Subject: [PATCH 28/62] [pre-commit.ci] pre-commit autoupdate (#11084) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/setup-cfg-fmt: v2.2.0 → v2.3.0](https://github.com/asottile/setup-cfg-fmt/compare/v2.2.0...v2.3.0) --- .pre-commit-config.yaml | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index af6cd2623..4ecaee56c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -47,7 +47,7 @@ repos: - id: pyupgrade args: [--py37-plus] - repo: https://github.com/asottile/setup-cfg-fmt - rev: v2.2.0 + rev: v2.3.0 hooks: - id: setup-cfg-fmt args: ["--max-py-version=3.11", "--include-version-classifiers"] diff --git a/setup.cfg b/setup.cfg index 56dadae7b..1f99a8019 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,7 +6,7 @@ long_description_content_type = text/x-rst url = https://docs.pytest.org/en/latest/ author = Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others license = MIT -license_file = LICENSE +license_files = LICENSE platforms = unix, linux, osx, cygwin, win32 classifiers = Development Status :: 6 - Mature From 1790f172283abe9c22d715731445e22e546b844d Mon Sep 17 00:00:00 2001 From: Facundo Batista Date: Tue, 6 Jun 2023 06:15:57 -0300 Subject: [PATCH 29/62] Introduced a hardcoded list of project to include as plugins beyond those found by their names. (#11077) --- scripts/update-plugin-list.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/scripts/update-plugin-list.py b/scripts/update-plugin-list.py index 34d1c8bb6..ea7e7986e 100644 --- a/scripts/update-plugin-list.py +++ b/scripts/update-plugin-list.py @@ -17,7 +17,9 @@ Plugin List =========== PyPI projects that match "pytest-\*" are considered plugins and are listed -automatically. Packages classified as inactive are excluded. +automatically together with a manually-maintained list in `the source +code `_. +Packages classified as inactive are excluded. .. The following conditional uses a different format for this list when creating a PDF, because otherwise the table gets far too wide for the @@ -33,6 +35,9 @@ DEVELOPMENT_STATUS_CLASSIFIERS = ( "Development Status :: 6 - Mature", "Development Status :: 7 - Inactive", ) +ADDITIONAL_PROJECTS = { # set of additional projects to consider as plugins + "logassert", +} def escape_rst(text: str) -> str: @@ -52,18 +57,18 @@ def iter_plugins(): regex = r">([\d\w-]*)" response = requests.get("https://pypi.org/simple") - matches = list( - match - for match in re.finditer(regex, response.text) - if match.groups()[0].startswith("pytest-") - ) + match_names = (match.groups()[0] for match in re.finditer(regex, response.text)) + plugin_names = [ + name + for name in match_names + if name.startswith("pytest-") or name in ADDITIONAL_PROJECTS + ] - for match in tqdm(matches, smoothing=0): - name = match.groups()[0] + for name in tqdm(plugin_names, smoothing=0): response = requests.get(f"https://pypi.org/pypi/{name}/json") if response.status_code == 404: - # Some packages, like pytest-azurepipelines42, are included in https://pypi.org/simple but - # return 404 on the JSON API. Skip. + # Some packages, like pytest-azurepipelines42, are included in https://pypi.org/simple + # but return 404 on the JSON API. Skip. continue response.raise_for_status() info = response.json()["info"] From fc9cbbd4c4d76ca20963370cfd800395b9a87495 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Tue, 6 Jun 2023 14:05:44 +0000 Subject: [PATCH 30/62] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 76 ++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index ca8851804..40f9b5bae 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -5,19 +5,22 @@ Plugin List =========== PyPI projects that match "pytest-\*" are considered plugins and are listed -automatically. Packages classified as inactive are excluded. +automatically together with a manually-maintained list in `the source +code `_. +Packages classified as inactive are excluded. .. The following conditional uses a different format for this list when creating a PDF, because otherwise the table gets far too wide for the page. -This list contains 1260 plugins. +This list contains 1261 plugins. .. only:: not latex =============================================== ======================================================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires =============================================== ======================================================================================================================================================================================================== ============== ===================== ================================================ + :pypi:`logassert` Simple but powerful assertion and verification of logged lines. May 20, 2022 5 - Production/Stable N/A :pypi:`pytest-abq` Pytest integration for the ABQ universal test runner. Apr 07, 2023 N/A N/A :pypi:`pytest-abstracts` A contextmanager pytest fixture for handling multiple mock abstracts May 25, 2022 N/A N/A :pypi:`pytest-accept` A pytest-plugin for updating doctest outputs Dec 21, 2022 N/A pytest (>=6,<8) @@ -305,7 +308,7 @@ This list contains 1260 plugins. :pypi:`pytest-django-filefield` Replaces FileField.storage with something you can patch globally. May 09, 2022 5 - Production/Stable pytest >= 5.2 :pypi:`pytest-django-gcir` A Django plugin for pytest. Mar 06, 2018 5 - Production/Stable N/A :pypi:`pytest-django-haystack` Cleanup your Haystack indexes between tests Sep 03, 2017 5 - Production/Stable pytest (>=2.3.4) - :pypi:`pytest-django-ifactory` A model instance factory for pytest-django May 21, 2023 5 - Production/Stable N/A + :pypi:`pytest-django-ifactory` A model instance factory for pytest-django Jun 06, 2023 5 - Production/Stable N/A :pypi:`pytest-django-lite` The bare minimum to integrate py.test with Django. Jan 30, 2014 N/A N/A :pypi:`pytest-django-liveserver-ssl` Jan 20, 2022 3 - Alpha N/A :pypi:`pytest-django-model` A Simple Way to Test your Django Models Feb 14, 2019 4 - Beta N/A @@ -367,13 +370,13 @@ This list contains 1260 plugins. :pypi:`pytest-eliot` An eliot plugin for pytest. Aug 31, 2022 1 - Planning pytest (>=5.4.0) :pypi:`pytest-elk-reporter` A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0) :pypi:`pytest-email` Send execution result email Jul 08, 2020 N/A pytest - :pypi:`pytest-embedded` pytest embedded plugin May 31, 2023 N/A pytest (>=7.0) - :pypi:`pytest-embedded-arduino` pytest embedded plugin for Arduino projects May 31, 2023 N/A N/A - :pypi:`pytest-embedded-idf` pytest embedded plugin for esp-idf project May 31, 2023 N/A N/A - :pypi:`pytest-embedded-jtag` pytest embedded plugin for testing with jtag May 31, 2023 N/A N/A - :pypi:`pytest-embedded-qemu` pytest embedded plugin for qemu, not target chip May 31, 2023 N/A N/A - :pypi:`pytest-embedded-serial` pytest embedded plugin for testing serial ports May 31, 2023 N/A N/A - :pypi:`pytest-embedded-serial-esp` pytest embedded plugin for testing espressif boards via serial ports May 31, 2023 N/A N/A + :pypi:`pytest-embedded` pytest embedded plugin Jun 06, 2023 N/A pytest (>=7.0) + :pypi:`pytest-embedded-arduino` pytest embedded plugin for Arduino projects Jun 06, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-idf` pytest embedded plugin for esp-idf project Jun 06, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-jtag` pytest embedded plugin for testing with jtag Jun 06, 2023 N/A N/A + :pypi:`pytest-embedded-qemu` pytest embedded plugin for qemu, not target chip Jun 06, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-serial` pytest embedded plugin for testing serial ports Jun 06, 2023 N/A N/A + :pypi:`pytest-embedded-serial-esp` pytest embedded plugin for testing espressif boards via serial ports Jun 06, 2023 N/A N/A :pypi:`pytest-embrace` 💝 Dataclasses-as-tests. Describe the runtime once and multiply coverage with no boilerplate. Mar 25, 2023 N/A pytest (>=7.0,<8.0) :pypi:`pytest-emoji` A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1) :pypi:`pytest-emoji-output` Pytest plugin to represent test output with emoji support Apr 09, 2023 4 - Beta pytest (==7.0.1) @@ -519,7 +522,7 @@ This list contains 1260 plugins. :pypi:`pytest-historic` Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest :pypi:`pytest-historic-hook` Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest :pypi:`pytest-homeassistant` A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A - :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Jun 02, 2023 3 - Alpha pytest (==7.3.1) + :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Jun 06, 2023 3 - Alpha pytest (==7.3.1) :pypi:`pytest-honey` A simple plugin to use with pytest Jan 07, 2022 4 - Beta pytest (>=3.5.0) :pypi:`pytest-honors` Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A :pypi:`pytest-hot-reloading` May 18, 2023 N/A N/A @@ -650,7 +653,7 @@ This list contains 1260 plugins. :pypi:`pytest-logger` Plugin configuring handlers for loggers from Python logging module. Jul 25, 2019 4 - Beta pytest (>=3.2) :pypi:`pytest-logging` Configures logging and allows tweaking the log level with a py.test flag Nov 04, 2015 4 - Beta N/A :pypi:`pytest-logging-end-to-end-test-tool` Sep 23, 2022 N/A pytest (>=7.1.2,<8.0.0) - :pypi:`pytest-logikal` Common testing environment Jun 03, 2023 5 - Production/Stable pytest (==7.3.1) + :pypi:`pytest-logikal` Common testing environment Jun 04, 2023 5 - Production/Stable pytest (==7.3.1) :pypi:`pytest-log-report` Package for creating a pytest test run reprot Dec 26, 2019 N/A N/A :pypi:`pytest-loguru` Pytest Loguru Apr 12, 2022 5 - Production/Stable N/A :pypi:`pytest-loop` pytest plugin for looping tests Jul 22, 2022 5 - Production/Stable pytest (>=6) @@ -675,7 +678,7 @@ This list contains 1260 plugins. :pypi:`pytest-md-report` A pytest plugin to make a test results report with Markdown table format. May 28, 2023 4 - Beta pytest (!=6.0.0,<8,>=3.3.2) :pypi:`pytest-memlog` Log memory usage during tests May 03, 2023 N/A pytest (>=7.3.0,<8.0.0) :pypi:`pytest-memprof` Estimates memory consumption of test functions Mar 29, 2019 4 - Beta N/A - :pypi:`pytest-memray` A simple plugin to use with pytest Dec 02, 2022 N/A pytest>=7.2 + :pypi:`pytest-memray` A simple plugin to use with pytest Jun 06, 2023 N/A pytest>=7.2 :pypi:`pytest-menu` A pytest plugin for console based interactive test selection just after the collection phase Oct 04, 2017 3 - Alpha pytest (>=2.4.2) :pypi:`pytest-mercurial` pytest plugin to write integration tests for projects using Mercurial Python internals Nov 21, 2020 1 - Planning N/A :pypi:`pytest-mesh` pytest_mesh插件 Aug 05, 2022 N/A pytest (==7.1.2) @@ -730,7 +733,7 @@ This list contains 1260 plugins. :pypi:`pytest-mysql` MySQL process and client fixtures for pytest Mar 27, 2023 5 - Production/Stable pytest (>=6.2) :pypi:`pytest-needle` pytest plugin for visual testing websites using selenium Dec 10, 2018 4 - Beta pytest (<5.0.0,>=3.0.0) :pypi:`pytest-neo` pytest-neo is a plugin for pytest that shows tests like screen of Matrix. Jan 08, 2022 3 - Alpha pytest (>=6.2.0) - :pypi:`pytest-netdut` "Automated software testing for switches using pytest" Apr 18, 2023 N/A pytest (>=3.5.0) + :pypi:`pytest-netdut` "Automated software testing for switches using pytest" Jun 05, 2023 N/A pytest (>=3.5.0) :pypi:`pytest-network` A simple plugin to disable network on socket level. May 07, 2020 N/A N/A :pypi:`pytest-network-endpoints` Network endpoints plugin for pytest Mar 06, 2022 N/A pytest :pypi:`pytest-never-sleep` pytest plugin helps to avoid adding tests without mock \`time.sleep\` May 05, 2021 3 - Alpha pytest (>=3.5.1) @@ -928,7 +931,7 @@ This list contains 1260 plugins. :pypi:`pytest-repo-health` A pytest plugin to report on repository standards conformance Apr 17, 2023 3 - Alpha pytest :pypi:`pytest-report` Creates json report that is compatible with atom.io's linter message format May 11, 2016 4 - Beta N/A :pypi:`pytest-reporter` Generate Pytest reports with templates Jul 22, 2021 4 - Beta pytest - :pypi:`pytest-reporter-html1` A basic HTML report template for Pytest Jun 08, 2021 4 - Beta N/A + :pypi:`pytest-reporter-html1` A basic HTML report template for Pytest Jun 05, 2023 4 - Beta N/A :pypi:`pytest-reporter-html-dots` A basic HTML report for pytest using Jinja2 template engine. Jan 22, 2023 N/A N/A :pypi:`pytest-reportinfra` Pytest plugin for reportinfra Aug 11, 2019 3 - Alpha N/A :pypi:`pytest-reporting` A plugin to report summarized results in a table format Oct 25, 2019 4 - Beta pytest (>=3.5.0) @@ -1239,7 +1242,7 @@ This list contains 1260 plugins. :pypi:`pytest-web3-data` Sep 15, 2022 4 - Beta pytest :pypi:`pytest-webdriver` Selenium webdriver fixture for py.test May 28, 2019 5 - Production/Stable pytest :pypi:`pytest-wetest` Welian API Automation test framework pytest plugin Nov 10, 2018 4 - Beta N/A - :pypi:`pytest-when` Utility which makes mocking more readable and controllable May 31, 2023 N/A pytest>=7.3.1 + :pypi:`pytest-when` Utility which makes mocking more readable and controllable Jun 05, 2023 N/A pytest>=7.3.1 :pypi:`pytest-whirlwind` Testing Tornado. Jun 12, 2020 N/A N/A :pypi:`pytest-wholenodeid` pytest addon for displaying the whole node id for failures Aug 26, 2015 4 - Beta pytest (>=2.0) :pypi:`pytest-win32consoletitle` Pytest progress in console title (Win32 only) Aug 08, 2021 N/A N/A @@ -1283,6 +1286,13 @@ This list contains 1260 plugins. .. only:: latex + :pypi:`logassert` + *last release*: May 20, 2022, + *status*: 5 - Production/Stable, + *requires*: N/A + + Simple but powerful assertion and verification of logged lines. + :pypi:`pytest-abq` *last release*: Apr 07, 2023, *status*: N/A, @@ -3293,7 +3303,7 @@ This list contains 1260 plugins. Cleanup your Haystack indexes between tests :pypi:`pytest-django-ifactory` - *last release*: May 21, 2023, + *last release*: Jun 06, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -3727,49 +3737,49 @@ This list contains 1260 plugins. Send execution result email :pypi:`pytest-embedded` - *last release*: May 31, 2023, + *last release*: Jun 06, 2023, *status*: N/A, *requires*: pytest (>=7.0) pytest embedded plugin :pypi:`pytest-embedded-arduino` - *last release*: May 31, 2023, - *status*: N/A, + *last release*: Jun 06, 2023, + *status*: 5 - Production/Stable, *requires*: N/A pytest embedded plugin for Arduino projects :pypi:`pytest-embedded-idf` - *last release*: May 31, 2023, - *status*: N/A, + *last release*: Jun 06, 2023, + *status*: 5 - Production/Stable, *requires*: N/A pytest embedded plugin for esp-idf project :pypi:`pytest-embedded-jtag` - *last release*: May 31, 2023, + *last release*: Jun 06, 2023, *status*: N/A, *requires*: N/A pytest embedded plugin for testing with jtag :pypi:`pytest-embedded-qemu` - *last release*: May 31, 2023, - *status*: N/A, + *last release*: Jun 06, 2023, + *status*: 5 - Production/Stable, *requires*: N/A pytest embedded plugin for qemu, not target chip :pypi:`pytest-embedded-serial` - *last release*: May 31, 2023, + *last release*: Jun 06, 2023, *status*: N/A, *requires*: N/A pytest embedded plugin for testing serial ports :pypi:`pytest-embedded-serial-esp` - *last release*: May 31, 2023, + *last release*: Jun 06, 2023, *status*: N/A, *requires*: N/A @@ -4791,7 +4801,7 @@ This list contains 1260 plugins. A pytest plugin for use with homeassistant custom components. :pypi:`pytest-homeassistant-custom-component` - *last release*: Jun 02, 2023, + *last release*: Jun 06, 2023, *status*: 3 - Alpha, *requires*: pytest (==7.3.1) @@ -5708,7 +5718,7 @@ This list contains 1260 plugins. :pypi:`pytest-logikal` - *last release*: Jun 03, 2023, + *last release*: Jun 04, 2023, *status*: 5 - Production/Stable, *requires*: pytest (==7.3.1) @@ -5883,7 +5893,7 @@ This list contains 1260 plugins. Estimates memory consumption of test functions :pypi:`pytest-memray` - *last release*: Dec 02, 2022, + *last release*: Jun 06, 2023, *status*: N/A, *requires*: pytest>=7.2 @@ -6268,7 +6278,7 @@ This list contains 1260 plugins. pytest-neo is a plugin for pytest that shows tests like screen of Matrix. :pypi:`pytest-netdut` - *last release*: Apr 18, 2023, + *last release*: Jun 05, 2023, *status*: N/A, *requires*: pytest (>=3.5.0) @@ -7654,7 +7664,7 @@ This list contains 1260 plugins. Generate Pytest reports with templates :pypi:`pytest-reporter-html1` - *last release*: Jun 08, 2021, + *last release*: Jun 05, 2023, *status*: 4 - Beta, *requires*: N/A @@ -9831,7 +9841,7 @@ This list contains 1260 plugins. Welian API Automation test framework pytest plugin :pypi:`pytest-when` - *last release*: May 31, 2023, + *last release*: Jun 05, 2023, *status*: N/A, *requires*: pytest>=7.3.1 From 9335a0b4453dbb08a07b6ac1b33d1ed3d17c45a5 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 10 May 2023 10:36:09 +0300 Subject: [PATCH 31/62] Avoid ast deprecation warnings on Python 3.12 Fix #10977. --- src/_pytest/assertion/rewrite.py | 49 +++++++++++++++++++------------- src/_pytest/mark/expression.py | 8 +++++- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index ab8169da2..ab83fee32 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -46,8 +46,14 @@ if TYPE_CHECKING: if sys.version_info >= (3, 8): namedExpr = ast.NamedExpr + astNameConstant = ast.Constant + astStr = ast.Constant + astNum = ast.Constant else: namedExpr = ast.Expr + astNameConstant = ast.NameConstant + astStr = ast.Str + astNum = ast.Num assertstate_key = StashKey["AssertionState"]() @@ -680,9 +686,12 @@ class AssertionRewriter(ast.NodeVisitor): if ( expect_docstring and isinstance(item, ast.Expr) - and isinstance(item.value, ast.Str) + and isinstance(item.value, astStr) ): - doc = item.value.s + if sys.version_info >= (3, 8): + doc = item.value.value + else: + doc = item.value.s if self.is_rewrite_disabled(doc): return expect_docstring = False @@ -814,7 +823,7 @@ class AssertionRewriter(ast.NodeVisitor): current = self.stack.pop() if self.stack: self.explanation_specifiers = self.stack[-1] - keys = [ast.Str(key) for key in current.keys()] + keys = [astStr(key) for key in current.keys()] format_dict = ast.Dict(keys, list(current.values())) form = ast.BinOp(expl_expr, ast.Mod(), format_dict) name = "@py_format" + str(next(self.variable_counter)) @@ -868,16 +877,16 @@ class AssertionRewriter(ast.NodeVisitor): negation = ast.UnaryOp(ast.Not(), top_condition) if self.enable_assertion_pass_hook: # Experimental pytest_assertion_pass hook - msg = self.pop_format_context(ast.Str(explanation)) + msg = self.pop_format_context(astStr(explanation)) # Failed if assert_.msg: assertmsg = self.helper("_format_assertmsg", assert_.msg) gluestr = "\n>assert " else: - assertmsg = ast.Str("") + assertmsg = astStr("") gluestr = "assert " - err_explanation = ast.BinOp(ast.Str(gluestr), ast.Add(), msg) + err_explanation = ast.BinOp(astStr(gluestr), ast.Add(), msg) err_msg = ast.BinOp(assertmsg, ast.Add(), err_explanation) err_name = ast.Name("AssertionError", ast.Load()) fmt = self.helper("_format_explanation", err_msg) @@ -893,8 +902,8 @@ class AssertionRewriter(ast.NodeVisitor): hook_call_pass = ast.Expr( self.helper( "_call_assertion_pass", - ast.Num(assert_.lineno), - ast.Str(orig), + astNum(assert_.lineno), + astStr(orig), fmt_pass, ) ) @@ -913,7 +922,7 @@ class AssertionRewriter(ast.NodeVisitor): variables = [ ast.Name(name, ast.Store()) for name in self.format_variables ] - clear_format = ast.Assign(variables, ast.NameConstant(None)) + clear_format = ast.Assign(variables, astNameConstant(None)) self.statements.append(clear_format) else: # Original assertion rewriting @@ -924,9 +933,9 @@ class AssertionRewriter(ast.NodeVisitor): assertmsg = self.helper("_format_assertmsg", assert_.msg) explanation = "\n>assert " + explanation else: - assertmsg = ast.Str("") + assertmsg = astStr("") explanation = "assert " + explanation - template = ast.BinOp(assertmsg, ast.Add(), ast.Str(explanation)) + template = ast.BinOp(assertmsg, ast.Add(), astStr(explanation)) msg = self.pop_format_context(template) fmt = self.helper("_format_explanation", msg) err_name = ast.Name("AssertionError", ast.Load()) @@ -938,7 +947,7 @@ class AssertionRewriter(ast.NodeVisitor): # Clear temporary variables by setting them to None. if self.variables: variables = [ast.Name(name, ast.Store()) for name in self.variables] - clear = ast.Assign(variables, ast.NameConstant(None)) + clear = ast.Assign(variables, astNameConstant(None)) self.statements.append(clear) # Fix locations (line numbers/column offsets). for stmt in self.statements: @@ -952,20 +961,20 @@ class AssertionRewriter(ast.NodeVisitor): # thinks it's acceptable. locs = ast.Call(self.builtin("locals"), [], []) target_id = name.target.id # type: ignore[attr-defined] - inlocs = ast.Compare(ast.Str(target_id), [ast.In()], [locs]) + inlocs = ast.Compare(astStr(target_id), [ast.In()], [locs]) dorepr = self.helper("_should_repr_global_name", name) test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) - expr = ast.IfExp(test, self.display(name), ast.Str(target_id)) + expr = ast.IfExp(test, self.display(name), astStr(target_id)) return name, self.explanation_param(expr) def visit_Name(self, name: ast.Name) -> Tuple[ast.Name, str]: # Display the repr of the name if it's a local variable or # _should_repr_global_name() thinks it's acceptable. locs = ast.Call(self.builtin("locals"), [], []) - inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs]) + inlocs = ast.Compare(astStr(name.id), [ast.In()], [locs]) dorepr = self.helper("_should_repr_global_name", name) test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) - expr = ast.IfExp(test, self.display(name), ast.Str(name.id)) + expr = ast.IfExp(test, self.display(name), astStr(name.id)) return name, self.explanation_param(expr) def visit_BoolOp(self, boolop: ast.BoolOp) -> Tuple[ast.Name, str]: @@ -1003,7 +1012,7 @@ class AssertionRewriter(ast.NodeVisitor): self.push_format_context() res, expl = self.visit(v) body.append(ast.Assign([ast.Name(res_var, ast.Store())], res)) - expl_format = self.pop_format_context(ast.Str(expl)) + expl_format = self.pop_format_context(astStr(expl)) call = ast.Call(app, [expl_format], []) self.expl_stmts.append(ast.Expr(call)) if i < levels: @@ -1015,7 +1024,7 @@ class AssertionRewriter(ast.NodeVisitor): self.statements = body = inner self.statements = save self.expl_stmts = fail_save - expl_template = self.helper("_format_boolop", expl_list, ast.Num(is_or)) + expl_template = self.helper("_format_boolop", expl_list, astNum(is_or)) expl = self.pop_format_context(expl_template) return ast.Name(res_var, ast.Load()), self.explanation_param(expl) @@ -1118,9 +1127,9 @@ class AssertionRewriter(ast.NodeVisitor): next_expl = f"({next_expl})" results.append(next_res) sym = BINOP_MAP[op.__class__] - syms.append(ast.Str(sym)) + syms.append(astStr(sym)) expl = f"{left_expl} {sym} {next_expl}" - expls.append(ast.Str(expl)) + expls.append(astStr(expl)) res_expr = ast.Compare(left_res, [op], [next_res]) self.statements.append(ast.Assign([store_names[i]], res_expr)) left_res, left_expl = next_res, next_expl diff --git a/src/_pytest/mark/expression.py b/src/_pytest/mark/expression.py index f82a81d44..9287bcee5 100644 --- a/src/_pytest/mark/expression.py +++ b/src/_pytest/mark/expression.py @@ -18,6 +18,7 @@ import ast import dataclasses import enum import re +import sys import types from typing import Callable from typing import Iterator @@ -26,6 +27,11 @@ from typing import NoReturn from typing import Optional from typing import Sequence +if sys.version_info >= (3, 8): + astNameConstant = ast.Constant +else: + astNameConstant = ast.NameConstant + __all__ = [ "Expression", @@ -132,7 +138,7 @@ IDENT_PREFIX = "$" def expression(s: Scanner) -> ast.Expression: if s.accept(TokenType.EOF): - ret: ast.expr = ast.NameConstant(False) + ret: ast.expr = astNameConstant(False) else: ret = expr(s) s.accept(TokenType.EOF, reject=True) From fc3b5b2610ef667eb80b847cd7f5d4d6bfef79fc Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 23 May 2023 15:10:46 +0300 Subject: [PATCH 32/62] testing: install setuptools to fix pkg_resources import in a test Since Python 3.12, setuptools is no longer installed by default in venvs. We have a test which uses it, so add it to the testing extra. --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 1f99a8019..2eee4cf53 100644 --- a/setup.cfg +++ b/setup.cfg @@ -73,6 +73,7 @@ testing = nose pygments>=2.7.2 requests + setuptools xmlschema [options.package_data] From 3e65a461c7293510753ed960a04d72e263f93bb0 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 11 Apr 2023 10:20:19 +0300 Subject: [PATCH 33/62] ci: start testing Python 3.12-dev --- .github/workflows/test.yml | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cd1ffdbf9..5435949fc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,6 +43,7 @@ jobs: "windows-py39", "windows-py310", "windows-py311", + "windows-py312", "ubuntu-py37", "ubuntu-py37-pluggy", @@ -51,12 +52,13 @@ jobs: "ubuntu-py39", "ubuntu-py310", "ubuntu-py311", + "ubuntu-py312", "ubuntu-pypy3", "macos-py37", - "macos-py38", "macos-py39", "macos-py310", + "macos-py312", "docs", "doctesting", @@ -86,9 +88,13 @@ jobs: os: windows-latest tox_env: "py310-xdist" - name: "windows-py311" - python: "3.11-dev" + python: "3.11" os: windows-latest tox_env: "py311" + - name: "windows-py312" + python: "3.12-dev" + os: windows-latest + tox_env: "py312" - name: "ubuntu-py37" python: "3.7" @@ -116,10 +122,15 @@ jobs: os: ubuntu-latest tox_env: "py310-xdist" - name: "ubuntu-py311" - python: "3.11-dev" + python: "3.11" os: ubuntu-latest tox_env: "py311" use_coverage: true + - name: "ubuntu-py312" + python: "3.12-dev" + os: ubuntu-latest + tox_env: "py312" + use_coverage: true - name: "ubuntu-pypy3" python: "pypy-3.7" os: ubuntu-latest @@ -129,19 +140,19 @@ jobs: python: "3.7" os: macos-latest tox_env: "py37-xdist" - - name: "macos-py38" - python: "3.8" - os: macos-latest - tox_env: "py38-xdist" - use_coverage: true - name: "macos-py39" python: "3.9" os: macos-latest tox_env: "py39-xdist" + use_coverage: true - name: "macos-py310" python: "3.10" os: macos-latest tox_env: "py310-xdist" + - name: "macos-py312" + python: "3.12-dev" + os: macos-latest + tox_env: "py312-xdist" - name: "plugins" python: "3.9" From 7d5207a7369f9560c75ee90e070727f34e04f1ce Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 23 May 2023 15:13:55 +0300 Subject: [PATCH 34/62] Declare support for Python 3.12 --- .pre-commit-config.yaml | 2 +- changelog/10894.bugfix.rst | 1 + setup.cfg | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changelog/10894.bugfix.rst diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4ecaee56c..0edb859dd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -50,7 +50,7 @@ repos: rev: v2.3.0 hooks: - id: setup-cfg-fmt - args: ["--max-py-version=3.11", "--include-version-classifiers"] + args: ["--max-py-version=3.12", "--include-version-classifiers"] - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: diff --git a/changelog/10894.bugfix.rst b/changelog/10894.bugfix.rst new file mode 100644 index 000000000..66c6cd73f --- /dev/null +++ b/changelog/10894.bugfix.rst @@ -0,0 +1 @@ +Support for Python 3.12 (beta at the time of writing). diff --git a/setup.cfg b/setup.cfg index 2eee4cf53..bc93b1c06 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,6 +22,7 @@ classifiers = Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 Topic :: Software Development :: Libraries Topic :: Software Development :: Testing Topic :: Utilities From 40590008349facb37d48ec147c0d3b6d663cfc4c Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 26 May 2023 20:28:21 +0300 Subject: [PATCH 35/62] testing/python/collect: replace use of deprecated/removed `imp` module --- testing/python/collect.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/testing/python/collect.py b/testing/python/collect.py index 41845517b..de10ce408 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -897,25 +897,29 @@ class TestConftestCustomization: def test_issue2369_collect_module_fileext(self, pytester: Pytester) -> None: """Ensure we can collect files with weird file extensions as Python modules (#2369)""" - # We'll implement a little finder and loader to import files containing + # Implement a little meta path finder to import files containing # Python source code whose file extension is ".narf". pytester.makeconftest( """ - import sys, os, imp + import sys + import os.path + from importlib.util import spec_from_loader + from importlib.machinery import SourceFileLoader from _pytest.python import Module - class Loader(object): - def load_module(self, name): - return imp.load_source(name, name + ".narf") - class Finder(object): - def find_module(self, name, path=None): - if os.path.exists(name + ".narf"): - return Loader() - sys.meta_path.append(Finder()) + class MetaPathFinder: + def find_spec(self, fullname, path, target=None): + if os.path.exists(fullname + ".narf"): + return spec_from_loader( + fullname, + SourceFileLoader(fullname, fullname + ".narf"), + ) + sys.meta_path.append(MetaPathFinder()) def pytest_collect_file(file_path, parent): if file_path.suffix == ".narf": - return Module.from_parent(path=file_path, parent=parent)""" + return Module.from_parent(path=file_path, parent=parent) + """ ) pytester.makefile( ".narf", From 6b3ece5bdb1c013f62db55004a42b7fb221d3fd1 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 9 Jun 2023 11:28:10 +0300 Subject: [PATCH 36/62] ci; use check-latest: true for -dev pythons When testing -dev python versions, we want to always use the latest. --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5435949fc..cf5027223 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -179,6 +179,7 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} + check-latest: ${{ endsWith(matrix.python, '-dev') }} - name: Install dependencies run: | From 0142bb6687bf40e8928fb0a46eee4a794198f1cb Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 10 Jun 2023 22:30:12 +0300 Subject: [PATCH 37/62] Merge pull request #11096 from pytest-dev/release-7.3.2 Prepare release 7.3.2 (cherry picked from commit 5dcd2be4661bc606b3399bff3614c0691ce9c13f) --- changelog/10169.bugfix.rst | 1 - changelog/10894.bugfix.rst | 1 - changelog/10987.bugfix.rst | 1 - changelog/10999.bugfix.rst | 1 - changelog/11028.bugfix.rst | 1 - changelog/11054.bugfix.rst | 1 - doc/en/announce/index.rst | 1 + doc/en/announce/release-7.3.2.rst | 21 +++++++++++++++++++++ doc/en/builtin.rst | 2 +- doc/en/changelog.rst | 24 ++++++++++++++++++++++++ doc/en/getting-started.rst | 2 +- 11 files changed, 48 insertions(+), 8 deletions(-) delete mode 100644 changelog/10169.bugfix.rst delete mode 100644 changelog/10894.bugfix.rst delete mode 100644 changelog/10987.bugfix.rst delete mode 100644 changelog/10999.bugfix.rst delete mode 100644 changelog/11028.bugfix.rst delete mode 100644 changelog/11054.bugfix.rst create mode 100644 doc/en/announce/release-7.3.2.rst diff --git a/changelog/10169.bugfix.rst b/changelog/10169.bugfix.rst deleted file mode 100644 index cbf3516a9..000000000 --- a/changelog/10169.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix bug where very long option names could cause pytest to break with ``OSError: [Errno 36] File name too long`` on some systems. diff --git a/changelog/10894.bugfix.rst b/changelog/10894.bugfix.rst deleted file mode 100644 index 66c6cd73f..000000000 --- a/changelog/10894.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Support for Python 3.12 (beta at the time of writing). diff --git a/changelog/10987.bugfix.rst b/changelog/10987.bugfix.rst deleted file mode 100644 index 2aafff5f5..000000000 --- a/changelog/10987.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -:confval:`testpaths` is now honored to load root ``conftests``. diff --git a/changelog/10999.bugfix.rst b/changelog/10999.bugfix.rst deleted file mode 100644 index 08c68da01..000000000 --- a/changelog/10999.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -The `monkeypatch` `setitem`/`delitem` type annotations now allow `TypedDict` arguments. diff --git a/changelog/11028.bugfix.rst b/changelog/11028.bugfix.rst deleted file mode 100644 index 9efc04ba6..000000000 --- a/changelog/11028.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed bug in assertion rewriting where a variable assigned with the walrus operator could not be used later in a function call. diff --git a/changelog/11054.bugfix.rst b/changelog/11054.bugfix.rst deleted file mode 100644 index a8ee04fe3..000000000 --- a/changelog/11054.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed ``--last-failed``'s "(skipped N files)" functionality for files inside of packages (directories with `__init__.py` files). diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index e3919f88e..bcc0669a6 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-7.3.2 release-7.3.1 release-7.3.0 release-7.2.2 diff --git a/doc/en/announce/release-7.3.2.rst b/doc/en/announce/release-7.3.2.rst new file mode 100644 index 000000000..b3b112f0d --- /dev/null +++ b/doc/en/announce/release-7.3.2.rst @@ -0,0 +1,21 @@ +pytest-7.3.2 +======================================= + +pytest 7.3.2 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: + +* Adam J. Stewart +* Alessio Izzo +* Bruno Oliveira +* Ran Benita + + +Happy testing, +The pytest Development Team diff --git a/doc/en/builtin.rst b/doc/en/builtin.rst index 7e9b51d00..7ae185f6c 100644 --- a/doc/en/builtin.rst +++ b/doc/en/builtin.rst @@ -207,7 +207,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a * caplog.record_tuples -> list of (logger_name, level, message) tuples * caplog.clear() -> clear captured records and formatted log output string - monkeypatch -- .../_pytest/monkeypatch.py:29 + monkeypatch -- .../_pytest/monkeypatch.py:30 A convenient fixture for monkey-patching. The fixture provides these methods to modify objects, dictionaries, or diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 7d6eb1ffa..cdc9e66f6 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -28,6 +28,30 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 7.3.2 (2023-06-10) +========================= + +Bug Fixes +--------- + +- `#10169 `_: Fix bug where very long option names could cause pytest to break with ``OSError: [Errno 36] File name too long`` on some systems. + + +- `#10894 `_: Support for Python 3.12 (beta at the time of writing). + + +- `#10987 `_: :confval:`testpaths` is now honored to load root ``conftests``. + + +- `#10999 `_: The `monkeypatch` `setitem`/`delitem` type annotations now allow `TypedDict` arguments. + + +- `#11028 `_: Fixed bug in assertion rewriting where a variable assigned with the walrus operator could not be used later in a function call. + + +- `#11054 `_: Fixed ``--last-failed``'s "(skipped N files)" functionality for files inside of packages (directories with `__init__.py` files). + + pytest 7.3.1 (2023-04-14) ========================= diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index f41571141..2dbf7d387 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 7.3.1 + pytest 7.3.2 .. _`simpletest`: From 8174a301642da3cecd828c82cdc8279ab9511b8a Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 10 Jun 2023 22:34:59 +0300 Subject: [PATCH 38/62] changelog: add note to `norecursedir`/`pytest_ignore_collect` change (#11095) --- changelog/11081.improvement.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/changelog/11081.improvement.rst b/changelog/11081.improvement.rst index 227e478d4..ccfaf6016 100644 --- a/changelog/11081.improvement.rst +++ b/changelog/11081.improvement.rst @@ -1 +1,7 @@ The :confval:`norecursedir` check is now performed in a :hook:`pytest_ignore_collect` implementation, so plugins can affect it. + +If after updating to this version you see that your `norecursedir` setting is not being respected, +it means that a conftest or a plugin you use has a bad `pytest_ignore_collect` implementation. +Most likely, your hook returns `False` for paths it does not want to ignore, +which ends the processing and doesn't allow other plugins, including pytest itself, to ignore the path. +The fix is to return `None` instead of `False` for paths your hook doesn't want to ignore. From 2d824329ebf5455aaf4c6f8aa29afff401abe315 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 04:19:01 +0000 Subject: [PATCH 39/62] build(deps): Bump django in /testing/plugins_integration (#11102) Bumps [django](https://github.com/django/django) from 4.2.1 to 4.2.2. - [Commits](https://github.com/django/django/compare/4.2.1...4.2.2) --- updated-dependencies: - dependency-name: django dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 7db8cc0ab..f9ca76547 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,5 +1,5 @@ anyio[curio,trio]==3.7.0 -django==4.2.1 +django==4.2.2 pytest-asyncio==0.21.0 pytest-bdd==6.1.1 pytest-cov==4.1.0 From b7a142f4babddce76168e9cb00bf5de6b145c0e4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 05:36:28 +0000 Subject: [PATCH 40/62] [pre-commit.ci] pre-commit autoupdate (#11103) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.4.0 → v3.6.0](https://github.com/asottile/pyupgrade/compare/v3.4.0...v3.6.0) 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 0edb859dd..ebf9f9f57 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,7 +42,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py37-plus] - repo: https://github.com/asottile/pyupgrade - rev: v3.4.0 + rev: v3.6.0 hooks: - id: pyupgrade args: [--py37-plus] From b55c02c3c9914a48e00fe8ab1cd7d903ab3b08c0 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 15 Jun 2023 14:17:14 +0200 Subject: [PATCH 41/62] doc: Add ep2023 training (#11113) --- doc/en/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/en/index.rst b/doc/en/index.rst index 872138788..de07831ac 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -2,6 +2,7 @@ .. sidebar:: Next Open Trainings + - `pytest tips and tricks for a better testsuite `_, at `Europython 2023 `_, July 18th (3h), Prague/Remote - `Professional Testing with Python `_, via `Python Academy `_, March 5th to 7th 2024 (3 day in-depth training), Leipzig/Remote Also see :doc:`previous talks and blogposts `. From fa09f6dcc60ba350771d10027061c135a2bdb66d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 18 Jun 2023 09:40:52 -0300 Subject: [PATCH 42/62] [automated] Update plugin list (#11117) Co-authored-by: pytest bot --- doc/en/reference/plugin_list.rst | 278 ++++++++++++++++++------------- 1 file changed, 163 insertions(+), 115 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 40f9b5bae..c882130b0 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -13,7 +13,7 @@ Packages classified as inactive are excluded. creating a PDF, because otherwise the table gets far too wide for the page. -This list contains 1261 plugins. +This list contains 1267 plugins. .. only:: not latex @@ -118,6 +118,7 @@ This list contains 1261 plugins. :pypi:`pytest-bdd-wrappers` Feb 11, 2020 2 - Pre-Alpha N/A :pypi:`pytest-beakerlib` A pytest plugin that reports test results to the BeakerLib framework Mar 17, 2017 5 - Production/Stable pytest :pypi:`pytest-beds` Fixtures for testing Google Appengine (GAE) apps Jun 07, 2016 4 - Beta N/A + :pypi:`pytest-beeprint` use icdiff for better error messages in pytest assertions Jun 09, 2023 4 - Beta N/A :pypi:`pytest-bench` Benchmark utility that plugs into pytest. Jul 21, 2014 3 - Alpha N/A :pypi:`pytest-benchmark` A \`\`pytest\`\` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer. Oct 25, 2022 5 - Production/Stable pytest (>=3.8) :pypi:`pytest-better-datadir` A small example package Mar 13, 2023 N/A N/A @@ -174,7 +175,7 @@ This list contains 1261 plugins. :pypi:`pytest-change-report` turn . into √,turn F into x Sep 14, 2020 N/A pytest :pypi:`pytest-change-xds` turn . into √,turn F into x Apr 16, 2022 N/A pytest :pypi:`pytest-chdir` A pytest fixture for changing current working directory Jan 28, 2020 N/A pytest (>=5.0.0,<6.0.0) - :pypi:`pytest-check` A pytest plugin that allows multiple failures per test. Feb 13, 2023 5 - Production/Stable pytest + :pypi:`pytest-check` A pytest plugin that allows multiple failures per test. Jun 06, 2023 N/A pytest :pypi:`pytest-checkdocs` check the README when running tests Oct 09, 2022 5 - Production/Stable pytest (>=6) ; extra == 'testing' :pypi:`pytest-checkipdb` plugin to check if there are ipdb debugs left Jul 22, 2020 5 - Production/Stable pytest (>=2.9.2) :pypi:`pytest-check-library` check your missing library Jul 17, 2022 N/A N/A @@ -231,7 +232,7 @@ This list contains 1261 plugins. :pypi:`pytest-coveragemarkers` Using pytest markers to track functional coverage and filtering of tests Nov 29, 2022 N/A pytest (>=7.1.2,<8.0.0) :pypi:`pytest-cov-exclude` Pytest plugin for excluding tests based on coverage data Apr 29, 2016 4 - Beta pytest (>=2.8.0,<2.9.0); extra == 'dev' :pypi:`pytest-cpp` Use pytest's runner to discover and execute C++ tests Jan 30, 2023 5 - Production/Stable pytest (>=7.0) - :pypi:`pytest-cppython` A pytest plugin that imports CPPython testing types Jun 02, 2023 N/A N/A + :pypi:`pytest-cppython` A pytest plugin that imports CPPython testing types Jun 14, 2023 N/A N/A :pypi:`pytest-cqase` Custom qase pytest plugin Aug 22, 2022 N/A pytest (>=7.1.2,<8.0.0) :pypi:`pytest-cram` Run cram tests with pytest. Aug 08, 2020 N/A N/A :pypi:`pytest-crate` Manages CrateDB instances during your integration tests May 28, 2019 3 - Alpha pytest (>=4.0) @@ -271,7 +272,7 @@ This list contains 1261 plugins. :pypi:`pytest-db` Session scope fixture "db" for mysql query or change Dec 04, 2019 N/A N/A :pypi:`pytest-dbfixtures` Databases fixtures plugin for py.test. Dec 07, 2016 4 - Beta N/A :pypi:`pytest-db-plugin` Nov 27, 2021 N/A pytest (>=5.0) - :pypi:`pytest-dbt` Unit test dbt models with standard python tooling May 31, 2023 2 - Pre-Alpha pytest (>=7.0.0,<8.0.0) + :pypi:`pytest-dbt` Unit test dbt models with standard python tooling Jun 08, 2023 2 - Pre-Alpha pytest (>=7.0.0,<8.0.0) :pypi:`pytest-dbt-adapter` A pytest plugin for testing dbt adapter plugins Nov 24, 2021 N/A pytest (<7,>=6) :pypi:`pytest-dbt-conventions` A pytest plugin for linting a dbt project's conventions Mar 02, 2022 N/A pytest (>=6.2.5,<7.0.0) :pypi:`pytest-dbt-core` Pytest extension for dbt. May 03, 2023 N/A pytest (>=6.2.5) ; extra == 'test' @@ -342,7 +343,7 @@ This list contains 1261 plugins. :pypi:`pytest-doctest-custom` A py.test plugin for customizing string representations of doctest results. Jul 25, 2016 4 - Beta N/A :pypi:`pytest-doctest-ellipsis-markers` Setup additional values for ELLIPSIS_MARKER for doctests Jan 12, 2018 4 - Beta N/A :pypi:`pytest-doctest-import` A simple pytest plugin to import names and add them to the doctest namespace. Nov 13, 2018 4 - Beta pytest (>=3.3.0) - :pypi:`pytest-doctestplus` Pytest plugin with advanced doctest features. Sep 26, 2022 3 - Alpha pytest (>=4.6) + :pypi:`pytest-doctestplus` Pytest plugin with advanced doctest features. Jun 08, 2023 3 - Alpha pytest (>=4.6) :pypi:`pytest-dolphin` Some extra stuff that we use ininternally Nov 30, 2016 4 - Beta pytest (==3.0.4) :pypi:`pytest-doorstop` A pytest plugin for adding test results into doorstop items. Jun 09, 2020 4 - Beta pytest (>=3.5.0) :pypi:`pytest-dotenv` A py.test plugin that parses environment files before running tests Jun 16, 2020 4 - Beta pytest (>=5.0.0) @@ -355,7 +356,7 @@ This list contains 1261 plugins. :pypi:`pytest-duration-insights` Jun 25, 2021 N/A N/A :pypi:`pytest-durations` Pytest plugin reporting fixtures and test functions execution time. Apr 22, 2022 5 - Production/Stable pytest (>=4.6) :pypi:`pytest-dynamicrerun` A pytest plugin to rerun tests dynamically based off of test outcome and output. Aug 15, 2020 4 - Beta N/A - :pypi:`pytest-dynamodb` DynamoDB fixtures for pytest Mar 27, 2023 5 - Production/Stable pytest + :pypi:`pytest-dynamodb` DynamoDB fixtures for pytest Jun 12, 2023 5 - Production/Stable pytest :pypi:`pytest-easy-addoption` pytest-easy-addoption: Easy way to work with pytest addoption Jan 22, 2020 N/A N/A :pypi:`pytest-easy-api` Simple API testing with pytest Mar 26, 2018 N/A N/A :pypi:`pytest-easyMPI` Package that supports mpi tests in pytest Oct 21, 2020 N/A N/A @@ -370,13 +371,13 @@ This list contains 1261 plugins. :pypi:`pytest-eliot` An eliot plugin for pytest. Aug 31, 2022 1 - Planning pytest (>=5.4.0) :pypi:`pytest-elk-reporter` A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0) :pypi:`pytest-email` Send execution result email Jul 08, 2020 N/A pytest - :pypi:`pytest-embedded` pytest embedded plugin Jun 06, 2023 N/A pytest (>=7.0) - :pypi:`pytest-embedded-arduino` pytest embedded plugin for Arduino projects Jun 06, 2023 5 - Production/Stable N/A - :pypi:`pytest-embedded-idf` pytest embedded plugin for esp-idf project Jun 06, 2023 5 - Production/Stable N/A - :pypi:`pytest-embedded-jtag` pytest embedded plugin for testing with jtag Jun 06, 2023 N/A N/A - :pypi:`pytest-embedded-qemu` pytest embedded plugin for qemu, not target chip Jun 06, 2023 5 - Production/Stable N/A - :pypi:`pytest-embedded-serial` pytest embedded plugin for testing serial ports Jun 06, 2023 N/A N/A - :pypi:`pytest-embedded-serial-esp` pytest embedded plugin for testing espressif boards via serial ports Jun 06, 2023 N/A N/A + :pypi:`pytest-embedded` A pytest plugin that designed for embedded testing. Jun 14, 2023 5 - Production/Stable pytest>=7.0 + :pypi:`pytest-embedded-arduino` Make pytest-embedded plugin work with Arduino. Jun 14, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-idf` Make pytest-embedded plugin work with ESP-IDF. Jun 14, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-jtag` Make pytest-embedded plugin work with JTAG. Jun 14, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-qemu` Make pytest-embedded plugin work with QEMU. Jun 14, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-serial` Make pytest-embedded plugin work with Serial. Jun 14, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-serial-esp` Make pytest-embedded plugin work with Espressif target boards. Jun 14, 2023 5 - Production/Stable N/A :pypi:`pytest-embrace` 💝 Dataclasses-as-tests. Describe the runtime once and multiply coverage with no boilerplate. Mar 25, 2023 N/A pytest (>=7.0,<8.0) :pypi:`pytest-emoji` A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1) :pypi:`pytest-emoji-output` Pytest plugin to represent test output with emoji support Apr 09, 2023 4 - Beta pytest (==7.0.1) @@ -385,7 +386,7 @@ This list contains 1261 plugins. :pypi:`pytest-encode-kane` set your encoding and logger Nov 16, 2021 N/A pytest :pypi:`pytest-enhanced-reports` Enhanced test reports for pytest Dec 15, 2022 N/A N/A :pypi:`pytest-enhancements` Improvements for pytest (rejected upstream) Oct 30, 2019 4 - Beta N/A - :pypi:`pytest-env` py.test plugin that allows you to add environment variables. Oct 23, 2022 5 - Production/Stable pytest>=7.1.3 + :pypi:`pytest-env` py.test plugin that allows you to add environment variables. Jun 15, 2023 5 - Production/Stable pytest>=7.3.1 :pypi:`pytest-envfiles` A py.test plugin that parses environment files before running tests Oct 08, 2015 3 - Alpha N/A :pypi:`pytest-env-info` Push information about the running pytest into envvars Nov 25, 2017 4 - Beta pytest (>=3.1.1) :pypi:`pytest-envraw` py.test plugin that allows you to add environment variables. Aug 27, 2020 4 - Beta pytest (>=2.6.0) @@ -431,7 +432,7 @@ This list contains 1261 plugins. :pypi:`pytest-fantasy` Pytest plugin for Flask Fantasy Framework Mar 14, 2019 N/A N/A :pypi:`pytest-fastapi` Dec 27, 2020 N/A N/A :pypi:`pytest-fastapi-deps` A fixture which allows easy replacement of fastapi dependencies for testing Jul 20, 2022 5 - Production/Stable pytest - :pypi:`pytest-fastest` Use SCM and coverage to run only needed tests Mar 05, 2020 N/A N/A + :pypi:`pytest-fastest` Use SCM and coverage to run only needed tests Jun 15, 2023 4 - Beta pytest (>=4.4) :pypi:`pytest-fast-first` Pytest plugin that runs fast tests first Jan 19, 2023 3 - Alpha pytest :pypi:`pytest-faulthandler` py.test plugin that activates the fault handler module for tests (dummy package) Jul 04, 2019 6 - Mature pytest (>=5.0) :pypi:`pytest-fauxfactory` Integration of fauxfactory into pytest. Dec 06, 2017 5 - Production/Stable pytest (>=3.2) @@ -456,7 +457,7 @@ This list contains 1261 plugins. :pypi:`pytest-fixture-tools` Plugin for pytest which provides tools for fixtures Aug 18, 2020 6 - Mature pytest :pypi:`pytest-fixture-typecheck` A pytest plugin to assert type annotations at runtime. Aug 24, 2021 N/A pytest :pypi:`pytest-flake8` pytest plugin to check FLAKE8 requirements Mar 18, 2022 4 - Beta pytest (>=7.0) - :pypi:`pytest-flake8-path` A pytest fixture for testing flake8 plugins. May 11, 2022 5 - Production/Stable pytest + :pypi:`pytest-flake8-path` A pytest fixture for testing flake8 plugins. Jun 16, 2023 5 - Production/Stable pytest :pypi:`pytest-flake8-v2` pytest plugin to check FLAKE8 requirements Mar 01, 2022 5 - Production/Stable pytest (>=7.0) :pypi:`pytest-flakefinder` Runs tests multiple times to expose flakiness. Oct 26, 2022 4 - Beta pytest (>=2.7.1) :pypi:`pytest-flakes` pytest plugin to check source code with pyflakes Dec 02, 2021 5 - Production/Stable pytest (>=5) @@ -475,7 +476,7 @@ This list contains 1261 plugins. :pypi:`pytest-forward-compatibility` A pytest plugin to shim pytest commandline options for fowards compatibility Sep 29, 2020 N/A N/A :pypi:`pytest-frappe` Pytest Frappe Plugin - A set of pytest fixtures to test Frappe applications May 03, 2023 4 - Beta pytest>=7.0.0 :pypi:`pytest-freezegun` Wrap tests with fixtures in freeze_time Jul 19, 2020 4 - Beta pytest (>=3.0.0) - :pypi:`pytest-freezer` Pytest plugin providing a fixture interface for spulec/freezegun Oct 20, 2022 N/A pytest>=3.6 + :pypi:`pytest-freezer` Pytest plugin providing a fixture interface for spulec/freezegun Jun 17, 2023 N/A pytest>=3.6 :pypi:`pytest-freeze-reqs` Check if requirement files are frozen Apr 29, 2021 N/A N/A :pypi:`pytest-frozen-uuids` Deterministically frozen UUID's for your tests Apr 17, 2022 N/A pytest (>=3.0) :pypi:`pytest-func-cov` Pytest plugin for measuring function coverage Apr 15, 2021 3 - Alpha pytest (>=5) @@ -490,7 +491,7 @@ This list contains 1261 plugins. :pypi:`pytest-gherkin` A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) :pypi:`pytest-gh-log-group` pytest plugin for gh actions Jan 11, 2022 3 - Alpha pytest :pypi:`pytest-ghostinspector` For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A - :pypi:`pytest-girder` A set of pytest fixtures for testing Girder applications. May 31, 2023 N/A N/A + :pypi:`pytest-girder` A set of pytest fixtures for testing Girder applications. Jun 14, 2023 N/A N/A :pypi:`pytest-git` Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest :pypi:`pytest-gitcov` Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A :pypi:`pytest-git-fixtures` Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest @@ -522,10 +523,10 @@ This list contains 1261 plugins. :pypi:`pytest-historic` Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest :pypi:`pytest-historic-hook` Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest :pypi:`pytest-homeassistant` A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A - :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Jun 06, 2023 3 - Alpha pytest (==7.3.1) + :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Jun 16, 2023 3 - Alpha pytest (==7.3.1) :pypi:`pytest-honey` A simple plugin to use with pytest Jan 07, 2022 4 - Beta pytest (>=3.5.0) :pypi:`pytest-honors` Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A - :pypi:`pytest-hot-reloading` May 18, 2023 N/A N/A + :pypi:`pytest-hot-reloading` Jun 16, 2023 N/A N/A :pypi:`pytest-hot-test` A plugin that tracks test changes Dec 10, 2022 4 - Beta pytest (>=3.5.0) :pypi:`pytest-hoverfly` Simplify working with Hoverfly from pytest Jan 30, 2023 N/A pytest (>=5.0) :pypi:`pytest-hoverfly-wrapper` Integrates the Hoverfly HTTP proxy into Pytest Feb 27, 2023 5 - Production/Stable pytest (>=3.7.0) @@ -583,7 +584,7 @@ This list contains 1261 plugins. :pypi:`pytest-ipynb` THIS PROJECT IS ABANDONED Jan 29, 2019 3 - Alpha N/A :pypi:`pytest-isolate` Feb 20, 2023 4 - Beta pytest :pypi:`pytest-isort` py.test plugin to check import ordering using isort Oct 31, 2022 5 - Production/Stable pytest (>=5.0) - :pypi:`pytest-is-running` pytest plugin providing a function to check if pytest is running. Aug 19, 2022 5 - Production/Stable N/A + :pypi:`pytest-is-running` pytest plugin providing a function to check if pytest is running. Jun 16, 2023 5 - Production/Stable N/A :pypi:`pytest-it` Pytest plugin to display test reports as a plaintext spec, inspired by Rspec: https://github.com/mattduck/pytest-it. Jan 22, 2020 4 - Beta N/A :pypi:`pytest-iterassert` Nicer list and iterable assertion messages for pytest May 11, 2020 3 - Alpha N/A :pypi:`pytest-iters` A contextmanager pytest fixture for handling multiple mock iters May 24, 2022 N/A N/A @@ -591,9 +592,9 @@ This list contains 1261 plugins. :pypi:`pytest-jelastic` Pytest plugin defining the necessary command-line options to pass to pytests testing a Jelastic environment. Nov 16, 2022 N/A pytest (>=7.2.0,<8.0.0) :pypi:`pytest-jest` A custom jest-pytest oriented Pytest reporter May 22, 2018 4 - Beta pytest (>=3.3.2) :pypi:`pytest-jinja` A plugin to generate customizable jinja-based HTML reports in pytest Oct 04, 2022 3 - Alpha pytest (>=6.2.5,<7.0.0) - :pypi:`pytest-jira` py.test JIRA integration plugin, using markers Apr 07, 2022 3 - Alpha N/A - :pypi:`pytest-jira-xfail` Plugin skips (xfail) tests if unresolved Jira issue(s) linked Dec 01, 2022 N/A pytest (~=7.2.0) - :pypi:`pytest-jira-xray` pytest plugin to integrate tests with JIRA XRAY May 29, 2023 4 - Beta pytest + :pypi:`pytest-jira` py.test JIRA integration plugin, using markers Jun 12, 2023 3 - Alpha N/A + :pypi:`pytest-jira-xfail` Plugin skips (xfail) tests if unresolved Jira issue(s) linked Jun 14, 2023 N/A pytest (>=7.2.0) + :pypi:`pytest-jira-xray` pytest plugin to integrate tests with JIRA XRAY Jun 06, 2023 4 - Beta pytest :pypi:`pytest-job-selection` A pytest plugin for load balancing test suites Jan 30, 2023 4 - Beta pytest (>=3.5.0) :pypi:`pytest-jobserver` Limit parallel tests with posix jobserver. May 15, 2019 5 - Production/Stable pytest :pypi:`pytest-joke` Test failures are better served with humor. Oct 08, 2019 4 - Beta pytest (>=4.2.1) @@ -604,7 +605,7 @@ This list contains 1261 plugins. :pypi:`pytest-jtr` pytest plugin supporting json test report output Nov 29, 2022 N/A pytest (>=7.1.2,<8.0.0) :pypi:`pytest-jupyter` A pytest plugin for testing Jupyter libraries and extensions. Mar 30, 2023 4 - Beta pytest :pypi:`pytest-jupyterhub` A reusable JupyterHub pytest plugin Apr 25, 2023 5 - Production/Stable pytest - :pypi:`pytest-kafka` Zookeeper, Kafka server, and Kafka consumer fixtures for Pytest Oct 01, 2022 N/A pytest + :pypi:`pytest-kafka` Zookeeper, Kafka server, and Kafka consumer fixtures for Pytest Jun 14, 2023 N/A pytest :pypi:`pytest-kafkavents` A plugin to send pytest events to Kafka Sep 08, 2021 4 - Beta pytest :pypi:`pytest-kasima` Display horizontal lines above and below the captured standard output for easy viewing. Jan 26, 2023 5 - Production/Stable pytest (>=7.2.1,<8.0.0) :pypi:`pytest-keep-together` Pytest plugin to customize test ordering by running all 'related' tests together Dec 07, 2022 5 - Production/Stable pytest @@ -643,7 +644,7 @@ This list contains 1261 plugins. :pypi:`pytest-local-badge` Generate local badges (shields) reporting your test suite status. Jan 15, 2023 N/A pytest (>=6.1.0) :pypi:`pytest-localftpserver` A PyTest plugin which provides an FTP fixture for your tests Oct 04, 2022 5 - Production/Stable pytest :pypi:`pytest-localserver` pytest plugin to test server connections locally. Jan 30, 2023 4 - Beta N/A - :pypi:`pytest-localstack` Pytest plugin for AWS integration tests Oct 17, 2022 4 - Beta pytest (>=6.0.0,<7.0.0) + :pypi:`pytest-localstack` Pytest plugin for AWS integration tests Jun 07, 2023 4 - Beta pytest (>=6.0.0,<7.0.0) :pypi:`pytest-lockable` lockable resource plugin for pytest Jul 20, 2022 5 - Production/Stable pytest :pypi:`pytest-locker` Used to lock object during testing. Essentially changing assertions from being hard coded to asserting that nothing changed Oct 29, 2021 N/A pytest (>=5.4) :pypi:`pytest-log` print log Aug 15, 2021 N/A pytest (>=3.8) @@ -686,20 +687,20 @@ This list contains 1261 plugins. :pypi:`pytest-messenger` Pytest to Slack reporting plugin Nov 24, 2022 5 - Production/Stable N/A :pypi:`pytest-metadata` pytest plugin for test session metadata May 27, 2023 5 - Production/Stable pytest>=7.0.0 :pypi:`pytest-metrics` Custom metrics report for pytest Apr 04, 2020 N/A pytest - :pypi:`pytest-mh` Pytest multihost plugin May 25, 2023 N/A pytest + :pypi:`pytest-mh` Pytest multihost plugin Jun 08, 2023 N/A pytest :pypi:`pytest-mimesis` Mimesis integration with the pytest test runner Mar 21, 2020 5 - Production/Stable pytest (>=4.2) :pypi:`pytest-minecraft` A pytest plugin for running tests against Minecraft releases Apr 06, 2022 N/A pytest (>=6.0.1) :pypi:`pytest-mini` A plugin to test mp Feb 06, 2023 N/A pytest (>=7.2.0,<8.0.0) :pypi:`pytest-missing-fixtures` Pytest plugin that creates missing fixtures Oct 14, 2020 4 - Beta pytest (>=3.5.0) :pypi:`pytest-ml` Test your machine learning! May 04, 2019 4 - Beta N/A :pypi:`pytest-mocha` pytest plugin to display test execution output like a mochajs Apr 02, 2020 4 - Beta pytest (>=5.4.0) - :pypi:`pytest-mock` Thin-wrapper around the mock package for easier use with pytest Oct 05, 2022 5 - Production/Stable pytest (>=5.0) + :pypi:`pytest-mock` Thin-wrapper around the mock package for easier use with pytest Jun 15, 2023 5 - Production/Stable pytest (>=5.0) :pypi:`pytest-mock-api` A mock API server with configurable routes and responses available as a fixture. Feb 13, 2019 1 - Planning pytest (>=4.0.0) :pypi:`pytest-mock-generator` A pytest fixture wrapper for https://pypi.org/project/mock-generator May 16, 2022 5 - Production/Stable N/A :pypi:`pytest-mock-helper` Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest :pypi:`pytest-mockito` Base fixtures for mockito Jul 11, 2018 4 - Beta N/A :pypi:`pytest-mockredis` An in-memory mock of a Redis server that runs in a separate thread. This is to be used for unit-tests that require a Redis database. Jan 02, 2018 2 - Pre-Alpha N/A - :pypi:`pytest-mock-resources` A pytest plugin for easily instantiating reproducible mock resources. May 23, 2023 N/A pytest (>=1.0) + :pypi:`pytest-mock-resources` A pytest plugin for easily instantiating reproducible mock resources. Jun 09, 2023 N/A pytest (>=1.0) :pypi:`pytest-mock-server` Mock server plugin for pytest Jan 09, 2022 4 - Beta pytest (>=3.5.0) :pypi:`pytest-mockservers` A set of fixtures to test your requests to HTTP/UDP servers Mar 31, 2020 N/A pytest (>=4.3.0) :pypi:`pytest-mocktcp` A pytest plugin for testing TCP clients Oct 11, 2022 N/A pytest @@ -741,7 +742,7 @@ This list contains 1261 plugins. :pypi:`pytest-nginx-iplweb` nginx fixture for pytest - iplweb temporary fork Mar 01, 2019 5 - Production/Stable N/A :pypi:`pytest-ngrok` Jan 20, 2022 3 - Alpha pytest :pypi:`pytest-ngsfixtures` pytest ngs fixtures Sep 06, 2019 2 - Pre-Alpha pytest (>=5.0.0) - :pypi:`pytest-nhsd-apim` Pytest plugin accessing NHSDigital's APIM proxies Mar 06, 2023 N/A pytest (==6.2.5) + :pypi:`pytest-nhsd-apim` Pytest plugin accessing NHSDigital's APIM proxies Jun 07, 2023 N/A pytest (==6.2.5) :pypi:`pytest-nice` A pytest plugin that alerts user of failed test cases with screen notifications May 04, 2019 4 - Beta pytest :pypi:`pytest-nice-parametrize` A small snippet for nicer PyTest's Parametrize Apr 17, 2021 5 - Production/Stable N/A :pypi:`pytest-nlcov` Pytest plugin to get the coverage of the new lines (based on git diff) only Jul 07, 2021 N/A N/A @@ -803,7 +804,7 @@ This list contains 1261 plugins. :pypi:`pytest-percent` Change the exit code of pytest test sessions when a required percent of tests pass. May 21, 2020 N/A pytest (>=5.2.0) :pypi:`pytest-perf` Run performance tests against the mainline code. Jun 02, 2023 5 - Production/Stable pytest (>=6) ; extra == 'testing' :pypi:`pytest-performance` A simple plugin to ensure the execution of critical sections of code has not been impacted Sep 11, 2020 5 - Production/Stable pytest (>=3.7.0) - :pypi:`pytest-persistence` Pytest tool for persistent objects May 16, 2023 N/A N/A + :pypi:`pytest-persistence` Pytest tool for persistent objects Jun 14, 2023 N/A N/A :pypi:`pytest-pg` A tiny plugin for pytest which runs PostgreSQL in Docker May 04, 2023 5 - Production/Stable pytest (>=6.0.0) :pypi:`pytest-pgsql` Pytest plugins and helpers for tests using a Postgres database. May 13, 2020 5 - Production/Stable pytest (>=3.0.0) :pypi:`pytest-phmdoctest` pytest plugin to test Python examples in Markdown using phmdoctest. Apr 15, 2022 4 - Beta pytest (>=5.4.3) @@ -846,7 +847,7 @@ This list contains 1261 plugins. :pypi:`pytest-pretty` pytest plugin for printing summary data as I want it Apr 05, 2023 5 - Production/Stable pytest>=7 :pypi:`pytest-pretty-terminal` pytest plugin for generating prettier terminal output Jan 31, 2022 N/A pytest (>=3.4.1) :pypi:`pytest-pride` Minitest-style test colors Apr 02, 2016 3 - Alpha N/A - :pypi:`pytest-print` pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Dec 28, 2021 5 - Production/Stable pytest (>=6) + :pypi:`pytest-print` pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Jun 16, 2023 5 - Production/Stable pytest>=7.3.2 :pypi:`pytest-profiles` pytest plugin for configuration profiles Dec 09, 2021 4 - Beta pytest (>=3.7.0) :pypi:`pytest-profiling` Profiling plugin for py.test May 28, 2019 5 - Production/Stable pytest :pypi:`pytest-progress` pytest plugin for instant test progress status Jan 31, 2022 5 - Production/Stable N/A @@ -867,7 +868,7 @@ This list contains 1261 plugins. :pypi:`pytest-pydocstyle` pytest plugin to run pydocstyle Jan 05, 2023 3 - Alpha N/A :pypi:`pytest-pylint` pytest plugin to check source code with pylint Sep 10, 2022 5 - Production/Stable pytest (>=5.4) :pypi:`pytest-pymysql-autorecord` Record PyMySQL queries and mock with the stored data. Sep 02, 2022 N/A N/A - :pypi:`pytest-pyodide` "Pytest plugin for testing applications that use Pyodide" Jun 02, 2023 N/A pytest + :pypi:`pytest-pyodide` "Pytest plugin for testing applications that use Pyodide" Jun 11, 2023 N/A pytest :pypi:`pytest-pypi` Easily test your HTTP library against a local copy of pypi Mar 04, 2018 3 - Alpha N/A :pypi:`pytest-pypom-navigation` Core engine for cookiecutter-qa and pytest-play packages Feb 18, 2019 4 - Beta pytest (>=3.0.7) :pypi:`pytest-pyppeteer` A plugin to run pyppeteer in pytest Apr 28, 2022 N/A pytest (>=6.2.5,<7.0.0) @@ -885,14 +886,14 @@ This list contains 1261 plugins. :pypi:`pytest-qaseio` Pytest plugin for Qase.io integration May 11, 2023 4 - Beta pytest (>=7.2.2,<8.0.0) :pypi:`pytest-qasync` Pytest support for qasync. Jul 12, 2021 4 - Beta pytest (>=5.4.0) :pypi:`pytest-qatouch` Pytest plugin for uploading test results to your QA Touch Testrun. Feb 14, 2023 4 - Beta pytest (>=6.2.0) - :pypi:`pytest-qgis` A pytest plugin for testing QGIS python plugins May 31, 2023 5 - Production/Stable pytest (>=6.2.5) + :pypi:`pytest-qgis` A pytest plugin for testing QGIS python plugins Jun 09, 2023 5 - Production/Stable pytest (>=6.2.5) :pypi:`pytest-qml` Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) :pypi:`pytest-qr` pytest plugin to generate test result QR codes Nov 25, 2021 4 - Beta N/A :pypi:`pytest-qt` pytest support for PyQt and PySide applications Oct 25, 2022 5 - Production/Stable pytest (>=3.0.0) :pypi:`pytest-qt-app` QT app fixture for py.test Dec 23, 2015 5 - Production/Stable N/A :pypi:`pytest-quarantine` A plugin for pytest to manage expected test failures Nov 24, 2019 5 - Production/Stable pytest (>=4.6) :pypi:`pytest-quickcheck` pytest plugin to generate random data inspired by QuickCheck Nov 05, 2022 4 - Beta pytest (>=4.0) - :pypi:`pytest-rabbitmq` RabbitMQ process and client fixtures for pytest Feb 11, 2022 5 - Production/Stable pytest (>=3.0.0) + :pypi:`pytest-rabbitmq` RabbitMQ process and client fixtures for pytest Jun 16, 2023 5 - Production/Stable pytest (>=6.2) :pypi:`pytest-race` Race conditions tester for pytest Jun 07, 2022 4 - Beta N/A :pypi:`pytest-rage` pytest plugin to implement PEP712 Oct 21, 2011 3 - Alpha N/A :pypi:`pytest-rail` pytest plugin for creating TestRail runs and adding results May 02, 2022 N/A pytest (>=3.6) @@ -938,7 +939,8 @@ This list contains 1261 plugins. :pypi:`pytest-reportlog` Replacement for the --resultlog option, focused in simplicity and extensibility May 22, 2023 3 - Alpha pytest :pypi:`pytest-report-me` A pytest plugin to generate report. Dec 31, 2020 N/A pytest :pypi:`pytest-report-parameters` pytest plugin for adding tests' parameters to junit report Jun 18, 2020 3 - Alpha pytest (>=2.4.2) - :pypi:`pytest-reportportal` Agent for Reporting results of tests to the Report Portal May 24, 2023 N/A pytest (>=3.8.0) + :pypi:`pytest-reportportal` Agent for Reporting results of tests to the Report Portal Jun 08, 2023 N/A pytest (>=3.8.0) + :pypi:`pytest-reports` An interesting python package Jun 07, 2023 N/A N/A :pypi:`pytest-reqs` pytest plugin to check pinned requirements May 12, 2019 N/A pytest (>=2.4.2) :pypi:`pytest-requests` A simple plugin to use with pytest Jun 24, 2019 4 - Beta pytest (>=3.5.0) :pypi:`pytest-requestselapsed` collect and show http requests elapsed time Aug 14, 2022 N/A N/A @@ -956,7 +958,7 @@ This list contains 1261 plugins. :pypi:`pytest-responsemock` Simplified requests calls mocking for pytest Mar 10, 2022 5 - Production/Stable N/A :pypi:`pytest-responses` py.test integration for responses Oct 11, 2022 N/A pytest (>=2.5) :pypi:`pytest-rest-api` Aug 08, 2022 N/A pytest (>=7.1.2,<8.0.0) - :pypi:`pytest-restrict` Pytest plugin to restrict the test types allowed May 11, 2022 5 - Production/Stable pytest + :pypi:`pytest-restrict` Pytest plugin to restrict the test types allowed Jun 16, 2023 5 - Production/Stable pytest :pypi:`pytest-result-log` Write the execution result of the case to the log Apr 17, 2023 N/A pytest>=7.2.0 :pypi:`pytest-result-sender` Apr 20, 2023 N/A pytest>=7.3.1 :pypi:`pytest-resume` A Pytest plugin to resuming from the last run test Apr 22, 2023 4 - Beta pytest (>=7.0) @@ -964,7 +966,7 @@ This list contains 1261 plugins. :pypi:`pytest-retry` Adds the ability to retry flaky tests in CI environments Aug 16, 2022 N/A pytest (>=7.0.0) :pypi:`pytest-retry-class` A pytest plugin to rerun entire class on failure Mar 25, 2023 N/A pytest (>=5.3) :pypi:`pytest-reusable-testcases` Apr 28, 2023 N/A N/A - :pypi:`pytest-reverse` Pytest plugin to reverse test order. May 11, 2022 5 - Production/Stable pytest + :pypi:`pytest-reverse` Pytest plugin to reverse test order. Jun 16, 2023 5 - Production/Stable pytest :pypi:`pytest-rich` Leverage rich for richer test session output Mar 03, 2022 4 - Beta pytest (>=7.0) :pypi:`pytest-rich-reporter` A pytest plugin using Rich for beautiful test result formatting. Feb 17, 2022 1 - Planning pytest (>=5.0.0) :pypi:`pytest-richtrace` Nov 05, 2022 N/A pytest (>=7.2.0,<8.0.0) @@ -978,13 +980,14 @@ This list contains 1261 plugins. :pypi:`pytest-rst` Test code from RST documents with pytest Jan 26, 2023 N/A N/A :pypi:`pytest-rt` pytest data collector plugin for Testgr May 05, 2022 N/A N/A :pypi:`pytest-rts` Coverage-based regression test selection (RTS) plugin for pytest May 17, 2021 N/A pytest - :pypi:`pytest-ruff` pytest plugin to check ruff requirements. Apr 28, 2023 4 - Beta N/A + :pypi:`pytest-ruff` pytest plugin to check ruff requirements. Jun 08, 2023 4 - Beta N/A :pypi:`pytest-run-changed` Pytest plugin that runs changed tests only Apr 02, 2021 3 - Alpha pytest :pypi:`pytest-runfailed` implement a --failed option for pytest Mar 24, 2016 N/A N/A :pypi:`pytest-runner` Invoke py.test as distutils command with dependency resolution Feb 25, 2022 5 - Production/Stable pytest (>=6) ; extra == 'testing' :pypi:`pytest-run-subprocess` Pytest Plugin for running and testing subprocesses. Nov 12, 2022 5 - Production/Stable pytest :pypi:`pytest-runtime-types` Checks type annotations on runtime while running tests. Feb 09, 2023 N/A pytest :pypi:`pytest-runtime-xfail` Call runtime_xfail() to mark running test as xfail. Aug 26, 2021 N/A pytest>=5.0.0 + :pypi:`pytest-runtime-yoyo` run case mark timeout Jun 12, 2023 N/A pytest (>=7.2.0) :pypi:`pytest-ry-demo1` 测试 Mar 26, 2023 N/A N/A :pypi:`pytest-saccharin` pytest-saccharin is a updated fork of pytest-sugar, a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly). Oct 31, 2022 3 - Alpha N/A :pypi:`pytest-salt` Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A @@ -995,14 +998,14 @@ This list contains 1261 plugins. :pypi:`pytest-sanic` a pytest plugin for Sanic Oct 25, 2021 N/A pytest (>=5.2) :pypi:`pytest-sanity` Dec 07, 2020 N/A N/A :pypi:`pytest-sa-pg` May 14, 2019 N/A N/A - :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Jun 02, 2023 5 - Production/Stable N/A + :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Jun 13, 2023 5 - Production/Stable N/A :pypi:`pytest-scenario` pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A :pypi:`pytest-schedule` The job of test scheduling for humans. Jan 07, 2023 5 - Production/Stable N/A :pypi:`pytest-schema` 👍 Validate return values against a schema-like object in testing Mar 14, 2022 5 - Production/Stable pytest (>=3.5.0) :pypi:`pytest-securestore` An encrypted password store for use within pytest cases Nov 08, 2021 4 - Beta N/A :pypi:`pytest-select` A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) :pypi:`pytest-selenium` pytest plugin for Selenium May 28, 2023 5 - Production/Stable pytest>=6.0.0 - :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Jun 02, 2023 5 - Production/Stable N/A + :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Jun 13, 2023 5 - Production/Stable N/A :pypi:`pytest-selenium-enhancer` pytest plugin for Selenium Apr 29, 2022 5 - Production/Stable N/A :pypi:`pytest-selenium-pdiff` A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A :pypi:`pytest-send-email` Send pytest execution result email Dec 04, 2019 N/A N/A @@ -1130,7 +1133,7 @@ This list contains 1261 plugins. :pypi:`pytest-test-groups` A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Oct 25, 2016 5 - Production/Stable N/A :pypi:`pytest-testinfra` Test infrastructures May 21, 2023 5 - Production/Stable pytest (!=3.0.2) :pypi:`pytest-testlink-adaptor` pytest reporting plugin for testlink Dec 20, 2018 4 - Beta pytest (>=2.6) - :pypi:`pytest-testmon` selects tests affected by changed files and methods May 18, 2023 4 - Beta pytest (<8,>=5) + :pypi:`pytest-testmon` selects tests affected by changed files and methods Jun 16, 2023 4 - Beta N/A :pypi:`pytest-testmon-dev` selects tests affected by changed files and methods Mar 30, 2023 4 - Beta pytest (<8,>=5) :pypi:`pytest-testmon-oc` nOly selects tests affected by changed files and methods Jun 01, 2022 4 - Beta pytest (<8,>=5) :pypi:`pytest-testmon-skip-libraries` selects tests affected by changed files and methods Mar 03, 2023 4 - Beta pytest (<8,>=5) @@ -1157,6 +1160,7 @@ This list contains 1261 plugins. :pypi:`pytest-thawgun` Pytest plugin for time travel May 26, 2020 3 - Alpha N/A :pypi:`pytest-threadleak` Detects thread leaks Jul 03, 2022 4 - Beta pytest (>=3.1.1) :pypi:`pytest-tick` Ticking on tests Aug 31, 2021 5 - Production/Stable pytest (>=6.2.5,<7.0.0) + :pypi:`pytest-time` Jun 16, 2023 3 - Alpha pytest :pypi:`pytest-timeit` A pytest plugin to time test function runs Oct 13, 2016 4 - Beta N/A :pypi:`pytest-timeout` pytest plugin to abort hanging tests Jan 18, 2022 5 - Production/Stable pytest (>=5.0.0) :pypi:`pytest-timeouts` Linux-only Pytest plugin to control durations of various test case execution phases Sep 21, 2019 5 - Production/Stable N/A @@ -1193,13 +1197,13 @@ This list contains 1261 plugins. :pypi:`pytest-tspwplib` A simple plugin to use with tspwplib Jan 08, 2021 4 - Beta pytest (>=3.5.0) :pypi:`pytest-tst` Customize pytest options, output and exit code to make it compatible with tst Apr 27, 2022 N/A pytest (>=5.0.0) :pypi:`pytest-tstcls` Test Class Base Mar 23, 2020 5 - Production/Stable N/A - :pypi:`pytest-tui` Text User Interface (TUI) and HTML report for Pytest test runs Apr 06, 2023 4 - Beta N/A + :pypi:`pytest-tui` Text User Interface (TUI) and HTML report for Pytest test runs Jun 12, 2023 4 - Beta N/A :pypi:`pytest-tutorials` Mar 11, 2023 N/A N/A :pypi:`pytest-twilio-conversations-client-mock` Aug 02, 2022 N/A N/A :pypi:`pytest-twisted` A twisted plugin for pytest. Oct 16, 2022 5 - Production/Stable pytest (>=2.3) :pypi:`pytest-typechecker` Run type checkers on specified test files Feb 04, 2022 N/A pytest (>=6.2.5,<7.0.0) :pypi:`pytest-typhoon-config` A Typhoon HIL plugin that facilitates test parameter configuration at runtime Apr 07, 2022 5 - Production/Stable N/A - :pypi:`pytest-typhoon-xray` Typhoon HIL plugin for pytest Nov 04, 2022 4 - Beta N/A + :pypi:`pytest-typhoon-xray` Typhoon HIL plugin for pytest Jun 10, 2023 4 - Beta N/A :pypi:`pytest-tytest` Typhoon HIL plugin for pytest May 25, 2020 4 - Beta pytest (>=5.4.2) :pypi:`pytest-ubersmith` Easily mock calls to ubersmith at the \`requests\` level. Apr 13, 2015 N/A N/A :pypi:`pytest-ui` Text User Interface for running python tests Jul 05, 2021 4 - Beta pytest @@ -1211,6 +1215,7 @@ This list contains 1261 plugins. :pypi:`pytest-unmarked` Run only unmarked tests Aug 27, 2019 5 - Production/Stable N/A :pypi:`pytest-unordered` Test equality of unordered collections in pytest Nov 28, 2022 4 - Beta pytest (>=6.0.0) :pypi:`pytest-unstable` Set a test as unstable to return 0 even if it failed Sep 27, 2022 4 - Beta N/A + :pypi:`pytest-unused-fixtures` A pytest plugin to list unused fixtures after a test run. Jun 15, 2023 4 - Beta pytest (>=7.3.2,<8.0.0) :pypi:`pytest-upload-report` pytest-upload-report is a plugin for pytest that upload your test report for test results. Jun 18, 2021 5 - Production/Stable N/A :pypi:`pytest-utils` Some helpers for pytest. Feb 02, 2023 4 - Beta pytest (>=7.0.0,<8.0.0) :pypi:`pytest-vagrant` A py.test plugin providing access to vagrant. Sep 07, 2021 5 - Production/Stable pytest @@ -1237,7 +1242,7 @@ This list contains 1261 plugins. :pypi:`pytest-wa-e2e-plugin` Pytest plugin for testing whatsapp bots with end to end tests Feb 18, 2020 4 - Beta pytest (>=3.5.0) :pypi:`pytest-wake` May 11, 2023 N/A pytest :pypi:`pytest-watch` Local continuous test runner with pytest and watchdog. May 20, 2018 N/A N/A - :pypi:`pytest-watcher` Automatically rerun your tests on file modifications Jun 03, 2023 4 - Beta N/A + :pypi:`pytest-watcher` Automatically rerun your tests on file modifications Jun 11, 2023 4 - Beta N/A :pypi:`pytest-wdl` Pytest plugin for testing WDL workflows. Nov 17, 2020 5 - Production/Stable N/A :pypi:`pytest-web3-data` Sep 15, 2022 4 - Beta pytest :pypi:`pytest-webdriver` Selenium webdriver fixture for py.test May 28, 2019 5 - Production/Stable pytest @@ -1254,6 +1259,7 @@ This list contains 1261 plugins. :pypi:`pytest-xdist-debug-for-graingert` pytest xdist plugin for distributed testing and loop-on-failing modes Jul 24, 2019 5 - Production/Stable pytest (>=4.4.0) :pypi:`pytest-xdist-forked` forked from pytest-xdist Feb 10, 2020 5 - Production/Stable pytest (>=4.4.0) :pypi:`pytest-xdist-tracker` pytest plugin helps to reproduce failures for particular xdist node Nov 18, 2021 3 - Alpha pytest (>=3.5.1) + :pypi:`pytest-xdist-worker-stats` A pytest plugin to list worker statistics after a xdist run. Jun 15, 2023 4 - Beta pytest (>=7.3.2,<8.0.0) :pypi:`pytest-xfaillist` Maintain a xfaillist in an additional file to avoid merge-conflicts. Sep 17, 2021 N/A pytest (>=6.2.2,<7.0.0) :pypi:`pytest-xfiles` Pytest fixtures providing data read from function, module or package related (x)files. Feb 27, 2018 N/A N/A :pypi:`pytest-xlog` Extended logging for test and decorators May 31, 2020 4 - Beta N/A @@ -1269,7 +1275,7 @@ This list contains 1261 plugins. :pypi:`pytest-yaml-sanmu` pytest plugin for generating test cases by yaml May 28, 2023 N/A pytest>=7.2.0 :pypi:`pytest-yamltree` Create or check file/directory trees described by YAML Mar 02, 2020 4 - Beta pytest (>=3.1.1) :pypi:`pytest-yamlwsgi` Run tests against wsgi apps defined in yaml May 11, 2010 N/A N/A - :pypi:`pytest-yaml-yoyo` http/https API run by yaml May 30, 2023 N/A pytest (>=7.2.0) + :pypi:`pytest-yaml-yoyo` http/https API run by yaml Jun 08, 2023 N/A pytest (>=7.2.0) :pypi:`pytest-yapf` Run yapf Jul 06, 2017 4 - Beta pytest (>=3.1.1) :pypi:`pytest-yapf3` Validate your Python file format with yapf Mar 29, 2023 5 - Production/Stable pytest (>=7) :pypi:`pytest-yield` PyTest plugin to run tests concurrently, each \`yield\` switch context to other one Jan 23, 2019 N/A N/A @@ -1972,6 +1978,13 @@ This list contains 1261 plugins. Fixtures for testing Google Appengine (GAE) apps + :pypi:`pytest-beeprint` + *last release*: Jun 09, 2023, + *status*: 4 - Beta, + *requires*: N/A + + use icdiff for better error messages in pytest assertions + :pypi:`pytest-bench` *last release*: Jul 21, 2014, *status*: 3 - Alpha, @@ -2365,8 +2378,8 @@ This list contains 1261 plugins. A pytest fixture for changing current working directory :pypi:`pytest-check` - *last release*: Feb 13, 2023, - *status*: 5 - Production/Stable, + *last release*: Jun 06, 2023, + *status*: N/A, *requires*: pytest A pytest plugin that allows multiple failures per test. @@ -2764,7 +2777,7 @@ This list contains 1261 plugins. Use pytest's runner to discover and execute C++ tests :pypi:`pytest-cppython` - *last release*: Jun 02, 2023, + *last release*: Jun 14, 2023, *status*: N/A, *requires*: N/A @@ -3044,7 +3057,7 @@ This list contains 1261 plugins. :pypi:`pytest-dbt` - *last release*: May 31, 2023, + *last release*: Jun 08, 2023, *status*: 2 - Pre-Alpha, *requires*: pytest (>=7.0.0,<8.0.0) @@ -3541,7 +3554,7 @@ This list contains 1261 plugins. A simple pytest plugin to import names and add them to the doctest namespace. :pypi:`pytest-doctestplus` - *last release*: Sep 26, 2022, + *last release*: Jun 08, 2023, *status*: 3 - Alpha, *requires*: pytest (>=4.6) @@ -3632,7 +3645,7 @@ This list contains 1261 plugins. A pytest plugin to rerun tests dynamically based off of test outcome and output. :pypi:`pytest-dynamodb` - *last release*: Mar 27, 2023, + *last release*: Jun 12, 2023, *status*: 5 - Production/Stable, *requires*: pytest @@ -3737,53 +3750,53 @@ This list contains 1261 plugins. Send execution result email :pypi:`pytest-embedded` - *last release*: Jun 06, 2023, - *status*: N/A, - *requires*: pytest (>=7.0) + *last release*: Jun 14, 2023, + *status*: 5 - Production/Stable, + *requires*: pytest>=7.0 - pytest embedded plugin + A pytest plugin that designed for embedded testing. :pypi:`pytest-embedded-arduino` - *last release*: Jun 06, 2023, + *last release*: Jun 14, 2023, *status*: 5 - Production/Stable, *requires*: N/A - pytest embedded plugin for Arduino projects + Make pytest-embedded plugin work with Arduino. :pypi:`pytest-embedded-idf` - *last release*: Jun 06, 2023, + *last release*: Jun 14, 2023, *status*: 5 - Production/Stable, *requires*: N/A - pytest embedded plugin for esp-idf project + Make pytest-embedded plugin work with ESP-IDF. :pypi:`pytest-embedded-jtag` - *last release*: Jun 06, 2023, - *status*: N/A, - *requires*: N/A - - pytest embedded plugin for testing with jtag - - :pypi:`pytest-embedded-qemu` - *last release*: Jun 06, 2023, + *last release*: Jun 14, 2023, *status*: 5 - Production/Stable, *requires*: N/A - pytest embedded plugin for qemu, not target chip + Make pytest-embedded plugin work with JTAG. + + :pypi:`pytest-embedded-qemu` + *last release*: Jun 14, 2023, + *status*: 5 - Production/Stable, + *requires*: N/A + + Make pytest-embedded plugin work with QEMU. :pypi:`pytest-embedded-serial` - *last release*: Jun 06, 2023, - *status*: N/A, + *last release*: Jun 14, 2023, + *status*: 5 - Production/Stable, *requires*: N/A - pytest embedded plugin for testing serial ports + Make pytest-embedded plugin work with Serial. :pypi:`pytest-embedded-serial-esp` - *last release*: Jun 06, 2023, - *status*: N/A, + *last release*: Jun 14, 2023, + *status*: 5 - Production/Stable, *requires*: N/A - pytest embedded plugin for testing espressif boards via serial ports + Make pytest-embedded plugin work with Espressif target boards. :pypi:`pytest-embrace` *last release*: Mar 25, 2023, @@ -3842,9 +3855,9 @@ This list contains 1261 plugins. Improvements for pytest (rejected upstream) :pypi:`pytest-env` - *last release*: Oct 23, 2022, + *last release*: Jun 15, 2023, *status*: 5 - Production/Stable, - *requires*: pytest>=7.1.3 + *requires*: pytest>=7.3.1 py.test plugin that allows you to add environment variables. @@ -4164,9 +4177,9 @@ This list contains 1261 plugins. A fixture which allows easy replacement of fastapi dependencies for testing :pypi:`pytest-fastest` - *last release*: Mar 05, 2020, - *status*: N/A, - *requires*: N/A + *last release*: Jun 15, 2023, + *status*: 4 - Beta, + *requires*: pytest (>=4.4) Use SCM and coverage to run only needed tests @@ -4339,7 +4352,7 @@ This list contains 1261 plugins. pytest plugin to check FLAKE8 requirements :pypi:`pytest-flake8-path` - *last release*: May 11, 2022, + *last release*: Jun 16, 2023, *status*: 5 - Production/Stable, *requires*: pytest @@ -4472,7 +4485,7 @@ This list contains 1261 plugins. Wrap tests with fixtures in freeze_time :pypi:`pytest-freezer` - *last release*: Oct 20, 2022, + *last release*: Jun 17, 2023, *status*: N/A, *requires*: pytest>=3.6 @@ -4577,7 +4590,7 @@ This list contains 1261 plugins. For finding/executing Ghost Inspector tests :pypi:`pytest-girder` - *last release*: May 31, 2023, + *last release*: Jun 14, 2023, *status*: N/A, *requires*: N/A @@ -4801,7 +4814,7 @@ This list contains 1261 plugins. A pytest plugin for use with homeassistant custom components. :pypi:`pytest-homeassistant-custom-component` - *last release*: Jun 06, 2023, + *last release*: Jun 16, 2023, *status*: 3 - Alpha, *requires*: pytest (==7.3.1) @@ -4822,7 +4835,7 @@ This list contains 1261 plugins. Report on tests that honor constraints, and guard against regressions :pypi:`pytest-hot-reloading` - *last release*: May 18, 2023, + *last release*: Jun 16, 2023, *status*: N/A, *requires*: N/A @@ -5228,7 +5241,7 @@ This list contains 1261 plugins. py.test plugin to check import ordering using isort :pypi:`pytest-is-running` - *last release*: Aug 19, 2022, + *last release*: Jun 16, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -5284,21 +5297,21 @@ This list contains 1261 plugins. A plugin to generate customizable jinja-based HTML reports in pytest :pypi:`pytest-jira` - *last release*: Apr 07, 2022, + *last release*: Jun 12, 2023, *status*: 3 - Alpha, *requires*: N/A py.test JIRA integration plugin, using markers :pypi:`pytest-jira-xfail` - *last release*: Dec 01, 2022, + *last release*: Jun 14, 2023, *status*: N/A, - *requires*: pytest (~=7.2.0) + *requires*: pytest (>=7.2.0) Plugin skips (xfail) tests if unresolved Jira issue(s) linked :pypi:`pytest-jira-xray` - *last release*: May 29, 2023, + *last release*: Jun 06, 2023, *status*: 4 - Beta, *requires*: pytest @@ -5375,7 +5388,7 @@ This list contains 1261 plugins. A reusable JupyterHub pytest plugin :pypi:`pytest-kafka` - *last release*: Oct 01, 2022, + *last release*: Jun 14, 2023, *status*: N/A, *requires*: pytest @@ -5648,7 +5661,7 @@ This list contains 1261 plugins. pytest plugin to test server connections locally. :pypi:`pytest-localstack` - *last release*: Oct 17, 2022, + *last release*: Jun 07, 2023, *status*: 4 - Beta, *requires*: pytest (>=6.0.0,<7.0.0) @@ -5949,7 +5962,7 @@ This list contains 1261 plugins. Custom metrics report for pytest :pypi:`pytest-mh` - *last release*: May 25, 2023, + *last release*: Jun 08, 2023, *status*: N/A, *requires*: pytest @@ -5998,7 +6011,7 @@ This list contains 1261 plugins. pytest plugin to display test execution output like a mochajs :pypi:`pytest-mock` - *last release*: Oct 05, 2022, + *last release*: Jun 15, 2023, *status*: 5 - Production/Stable, *requires*: pytest (>=5.0) @@ -6040,7 +6053,7 @@ This list contains 1261 plugins. An in-memory mock of a Redis server that runs in a separate thread. This is to be used for unit-tests that require a Redis database. :pypi:`pytest-mock-resources` - *last release*: May 23, 2023, + *last release*: Jun 09, 2023, *status*: N/A, *requires*: pytest (>=1.0) @@ -6334,7 +6347,7 @@ This list contains 1261 plugins. pytest ngs fixtures :pypi:`pytest-nhsd-apim` - *last release*: Mar 06, 2023, + *last release*: Jun 07, 2023, *status*: N/A, *requires*: pytest (==6.2.5) @@ -6768,7 +6781,7 @@ This list contains 1261 plugins. A simple plugin to ensure the execution of critical sections of code has not been impacted :pypi:`pytest-persistence` - *last release*: May 16, 2023, + *last release*: Jun 14, 2023, *status*: N/A, *requires*: N/A @@ -7069,9 +7082,9 @@ This list contains 1261 plugins. Minitest-style test colors :pypi:`pytest-print` - *last release*: Dec 28, 2021, + *last release*: Jun 16, 2023, *status*: 5 - Production/Stable, - *requires*: pytest (>=6) + *requires*: pytest>=7.3.2 pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) @@ -7216,7 +7229,7 @@ This list contains 1261 plugins. Record PyMySQL queries and mock with the stored data. :pypi:`pytest-pyodide` - *last release*: Jun 02, 2023, + *last release*: Jun 11, 2023, *status*: N/A, *requires*: pytest @@ -7342,7 +7355,7 @@ This list contains 1261 plugins. Pytest plugin for uploading test results to your QA Touch Testrun. :pypi:`pytest-qgis` - *last release*: May 31, 2023, + *last release*: Jun 09, 2023, *status*: 5 - Production/Stable, *requires*: pytest (>=6.2.5) @@ -7391,9 +7404,9 @@ This list contains 1261 plugins. pytest plugin to generate random data inspired by QuickCheck :pypi:`pytest-rabbitmq` - *last release*: Feb 11, 2022, + *last release*: Jun 16, 2023, *status*: 5 - Production/Stable, - *requires*: pytest (>=3.0.0) + *requires*: pytest (>=6.2) RabbitMQ process and client fixtures for pytest @@ -7713,12 +7726,19 @@ This list contains 1261 plugins. pytest plugin for adding tests' parameters to junit report :pypi:`pytest-reportportal` - *last release*: May 24, 2023, + *last release*: Jun 08, 2023, *status*: N/A, *requires*: pytest (>=3.8.0) Agent for Reporting results of tests to the Report Portal + :pypi:`pytest-reports` + *last release*: Jun 07, 2023, + *status*: N/A, + *requires*: N/A + + An interesting python package + :pypi:`pytest-reqs` *last release*: May 12, 2019, *status*: N/A, @@ -7839,7 +7859,7 @@ This list contains 1261 plugins. :pypi:`pytest-restrict` - *last release*: May 11, 2022, + *last release*: Jun 16, 2023, *status*: 5 - Production/Stable, *requires*: pytest @@ -7895,7 +7915,7 @@ This list contains 1261 plugins. :pypi:`pytest-reverse` - *last release*: May 11, 2022, + *last release*: Jun 16, 2023, *status*: 5 - Production/Stable, *requires*: pytest @@ -7993,7 +8013,7 @@ This list contains 1261 plugins. Coverage-based regression test selection (RTS) plugin for pytest :pypi:`pytest-ruff` - *last release*: Apr 28, 2023, + *last release*: Jun 08, 2023, *status*: 4 - Beta, *requires*: N/A @@ -8041,6 +8061,13 @@ This list contains 1261 plugins. Call runtime_xfail() to mark running test as xfail. + :pypi:`pytest-runtime-yoyo` + *last release*: Jun 12, 2023, + *status*: N/A, + *requires*: pytest (>=7.2.0) + + run case mark timeout + :pypi:`pytest-ry-demo1` *last release*: Mar 26, 2023, *status*: N/A, @@ -8112,7 +8139,7 @@ This list contains 1261 plugins. :pypi:`pytest-sbase` - *last release*: Jun 02, 2023, + *last release*: Jun 13, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -8161,7 +8188,7 @@ This list contains 1261 plugins. pytest plugin for Selenium :pypi:`pytest-seleniumbase` - *last release*: Jun 02, 2023, + *last release*: Jun 13, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -9057,9 +9084,9 @@ This list contains 1261 plugins. pytest reporting plugin for testlink :pypi:`pytest-testmon` - *last release*: May 18, 2023, + *last release*: Jun 16, 2023, *status*: 4 - Beta, - *requires*: pytest (<8,>=5) + *requires*: N/A selects tests affected by changed files and methods @@ -9245,6 +9272,13 @@ This list contains 1261 plugins. Ticking on tests + :pypi:`pytest-time` + *last release*: Jun 16, 2023, + *status*: 3 - Alpha, + *requires*: pytest + + + :pypi:`pytest-timeit` *last release*: Oct 13, 2016, *status*: 4 - Beta, @@ -9498,7 +9532,7 @@ This list contains 1261 plugins. Test Class Base :pypi:`pytest-tui` - *last release*: Apr 06, 2023, + *last release*: Jun 12, 2023, *status*: 4 - Beta, *requires*: N/A @@ -9540,7 +9574,7 @@ This list contains 1261 plugins. A Typhoon HIL plugin that facilitates test parameter configuration at runtime :pypi:`pytest-typhoon-xray` - *last release*: Nov 04, 2022, + *last release*: Jun 10, 2023, *status*: 4 - Beta, *requires*: N/A @@ -9623,6 +9657,13 @@ This list contains 1261 plugins. Set a test as unstable to return 0 even if it failed + :pypi:`pytest-unused-fixtures` + *last release*: Jun 15, 2023, + *status*: 4 - Beta, + *requires*: pytest (>=7.3.2,<8.0.0) + + A pytest plugin to list unused fixtures after a test run. + :pypi:`pytest-upload-report` *last release*: Jun 18, 2021, *status*: 5 - Production/Stable, @@ -9806,7 +9847,7 @@ This list contains 1261 plugins. Local continuous test runner with pytest and watchdog. :pypi:`pytest-watcher` - *last release*: Jun 03, 2023, + *last release*: Jun 11, 2023, *status*: 4 - Beta, *requires*: N/A @@ -9924,6 +9965,13 @@ This list contains 1261 plugins. pytest plugin helps to reproduce failures for particular xdist node + :pypi:`pytest-xdist-worker-stats` + *last release*: Jun 15, 2023, + *status*: 4 - Beta, + *requires*: pytest (>=7.3.2,<8.0.0) + + A pytest plugin to list worker statistics after a xdist run. + :pypi:`pytest-xfaillist` *last release*: Sep 17, 2021, *status*: N/A, @@ -10030,7 +10078,7 @@ This list contains 1261 plugins. Run tests against wsgi apps defined in yaml :pypi:`pytest-yaml-yoyo` - *last release*: May 30, 2023, + *last release*: Jun 08, 2023, *status*: N/A, *requires*: pytest (>=7.2.0) From 1eb83706b6c7a4c2789719711ececaa0b8c5a9f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 04:15:50 +0000 Subject: [PATCH 43/62] build(deps): Bump pytest-mock in /testing/plugins_integration (#11119) Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.10.0 to 3.11.1. - [Release notes](https://github.com/pytest-dev/pytest-mock/releases) - [Changelog](https://github.com/pytest-dev/pytest-mock/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.10.0...v3.11.1) --- updated-dependencies: - dependency-name: pytest-mock dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index f9ca76547..d56720c02 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -6,7 +6,7 @@ pytest-cov==4.1.0 pytest-django==4.5.2 pytest-flakes==4.0.5 pytest-html==3.2.0 -pytest-mock==3.10.0 +pytest-mock==3.11.1 pytest-rerunfailures==11.1.2 pytest-sugar==0.9.7 pytest-trio==0.7.0 From 8528052a9514b1d81103d9c0d4aad5792849a1b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 19:22:49 -0300 Subject: [PATCH 44/62] build(deps): Bump peter-evans/create-pull-request from 5.0.0 to 5.0.2 (#11120) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 5.0.0 to 5.0.2. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5...153407881ec5c347639a548ade7d8ad1d6740e38) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request 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/update-plugin-list.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-plugin-list.yml b/.github/workflows/update-plugin-list.yml index ef92fef88..e7cbc4e96 100644 --- a/.github/workflows/update-plugin-list.yml +++ b/.github/workflows/update-plugin-list.yml @@ -38,7 +38,7 @@ jobs: run: python scripts/update-plugin-list.py - name: Create Pull Request - uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 + uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 with: commit-message: '[automated] Update plugin list' author: 'pytest bot ' From 5b355183891962fe466b227ce15d1b34714b8eef Mon Sep 17 00:00:00 2001 From: Zac Hatfield-Dodds Date: Mon, 19 Jun 2023 20:06:21 -0700 Subject: [PATCH 45/62] Apply suggestions from code review Co-authored-by: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com> --- src/_pytest/logging.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 92a0cb6c6..ff16e0ff1 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -447,12 +447,9 @@ class LogCaptureFixture: self.handler.reset() def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None: - """Sets the threshold for this logger to level. + """Set the threshold level of a logger for the duration of a test. - Logging messages which are less severe than level will be ignored; logging messages - which have severity level or higher will be emitted by whichever handler or handlers - service this logger, unless a handler’s level has been set to a higher severity level - than `level`. + Logging messages which are less severe than this level will not be captured. .. versionchanged:: 3.4 The levels of the loggers changed by this function will be From 797b924fc44189d0b9c2ad905410f0bd89461ab7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 06:38:36 +0000 Subject: [PATCH 46/62] [pre-commit.ci] pre-commit autoupdate (#11124) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/blacken-docs: 1.13.0 → 1.14.0](https://github.com/asottile/blacken-docs/compare/1.13.0...1.14.0) - [github.com/asottile/reorder-python-imports: v3.9.0 → v3.10.0](https://github.com/asottile/reorder-python-imports/compare/v3.9.0...v3.10.0) - [github.com/asottile/pyupgrade: v3.6.0 → v3.7.0](https://github.com/asottile/pyupgrade/compare/v3.6.0...v3.7.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .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 ebf9f9f57..b5089e129 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: - id: black args: [--safe, --quiet] - repo: https://github.com/asottile/blacken-docs - rev: 1.13.0 + rev: 1.14.0 hooks: - id: blacken-docs additional_dependencies: [black==23.1.0] @@ -37,12 +37,12 @@ repos: - flake8-typing-imports==1.12.0 - flake8-docstrings==1.5.0 - repo: https://github.com/asottile/reorder-python-imports - rev: v3.9.0 + rev: v3.10.0 hooks: - id: reorder-python-imports args: ['--application-directories=.:src', --py37-plus] - repo: https://github.com/asottile/pyupgrade - rev: v3.6.0 + rev: v3.7.0 hooks: - id: pyupgrade args: [--py37-plus] From a704605cf182e1142c1e9b1da4c7cad1a1749376 Mon Sep 17 00:00:00 2001 From: nondescryptid Date: Tue, 20 Jun 2023 04:55:39 -0700 Subject: [PATCH 47/62] Fix encoding warnings --- testing/acceptance_test.py | 10 ++-- testing/python/fixtures.py | 94 +++++++++++++++++++++++------------ testing/python/metafunc.py | 14 ++++-- testing/test_assertrewrite.py | 21 +++++--- testing/test_collection.py | 54 +++++++++++++------- testing/test_config.py | 31 ++++++++---- testing/test_conftest.py | 71 +++++++++++++++++--------- testing/test_doctest.py | 13 +++-- testing/test_monkeypatch.py | 12 +++-- testing/test_pathlib.py | 6 +-- tox.ini | 4 ++ 11 files changed, 218 insertions(+), 112 deletions(-) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 680afe133..0046d05b8 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -267,7 +267,7 @@ class TestGeneralUsage: def test_issue109_sibling_conftests_not_loaded(self, pytester: Pytester) -> None: sub1 = pytester.mkdir("sub1") sub2 = pytester.mkdir("sub2") - sub1.joinpath("conftest.py").write_text("assert 0") + sub1.joinpath("conftest.py").write_text("assert 0", encoding="utf-8") result = pytester.runpytest(sub2) assert result.ret == ExitCode.NO_TESTS_COLLECTED sub2.joinpath("__init__.py").touch() @@ -467,7 +467,7 @@ class TestGeneralUsage: assert "invalid" in str(excinfo.value) p = pytester.path.joinpath("test_test_plugins_given_as_strings.py") - p.write_text("def test_foo(): pass") + p.write_text("def test_foo(): pass", encoding="utf-8") mod = types.ModuleType("myplugin") monkeypatch.setitem(sys.modules, "myplugin", mod) assert pytest.main(args=[str(pytester.path)], plugins=["myplugin"]) == 0 @@ -587,7 +587,7 @@ class TestInvocationVariants: def test_pyargs_importerror(self, pytester: Pytester, monkeypatch) -> None: monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", False) path = pytester.mkpydir("tpkg") - path.joinpath("test_hello.py").write_text("raise ImportError") + path.joinpath("test_hello.py").write_text("raise ImportError", encoding="utf-8") result = pytester.runpytest("--pyargs", "tpkg.test_hello", syspathinsert=True) assert result.ret != 0 @@ -597,10 +597,10 @@ class TestInvocationVariants: def test_pyargs_only_imported_once(self, pytester: Pytester) -> None: pkg = pytester.mkpydir("foo") pkg.joinpath("test_foo.py").write_text( - "print('hello from test_foo')\ndef test(): pass" + "print('hello from test_foo')\ndef test(): pass", encoding="utf-8" ) pkg.joinpath("conftest.py").write_text( - "def pytest_configure(config): print('configuring')" + "def pytest_configure(config): print('configuring')", encoding="utf-8" ) result = pytester.runpytest( diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index d996f80bb..e62db8c26 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -287,7 +287,8 @@ class TestFillFixtures: def spam(): return 'spam' """ - ) + ), + encoding="utf-8", ) testfile = subdir.joinpath("test_spam.py") testfile.write_text( @@ -296,7 +297,8 @@ class TestFillFixtures: def test_spam(spam): assert spam == "spam" """ - ) + ), + encoding="utf-8", ) result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 passed*"]) @@ -359,7 +361,8 @@ class TestFillFixtures: def spam(request): return request.param """ - ) + ), + encoding="utf-8", ) testfile = subdir.joinpath("test_spam.py") testfile.write_text( @@ -371,7 +374,8 @@ class TestFillFixtures: assert spam == params['spam'] params['spam'] += 1 """ - ) + ), + encoding="utf-8", ) result = pytester.runpytest() result.stdout.fnmatch_lines(["*3 passed*"]) @@ -403,7 +407,8 @@ class TestFillFixtures: def spam(request): return request.param """ - ) + ), + encoding="utf-8", ) testfile = subdir.joinpath("test_spam.py") testfile.write_text( @@ -415,7 +420,8 @@ class TestFillFixtures: assert spam == params['spam'] params['spam'] += 1 """ - ) + ), + encoding="utf-8", ) result = pytester.runpytest() result.stdout.fnmatch_lines(["*3 passed*"]) @@ -1037,10 +1043,11 @@ class TestRequestBasic: def arg1(): pass """ - ) + ), + encoding="utf-8", ) p = b.joinpath("test_module.py") - p.write_text("def test_func(arg1): pass") + p.write_text("def test_func(arg1): pass", encoding="utf-8") result = pytester.runpytest(p, "--fixtures") assert result.ret == 0 result.stdout.fnmatch_lines( @@ -1617,7 +1624,8 @@ class TestFixtureManagerParseFactories: def one(): return 1 """ - ) + ), + encoding="utf-8", ) package.joinpath("test_x.py").write_text( textwrap.dedent( @@ -1625,7 +1633,8 @@ class TestFixtureManagerParseFactories: def test_x(one): assert one == 1 """ - ) + ), + encoding="utf-8", ) sub = package.joinpath("sub") sub.mkdir() @@ -1638,7 +1647,8 @@ class TestFixtureManagerParseFactories: def one(): return 2 """ - ) + ), + encoding="utf-8", ) sub.joinpath("test_y.py").write_text( textwrap.dedent( @@ -1646,7 +1656,8 @@ class TestFixtureManagerParseFactories: def test_x(one): assert one == 2 """ - ) + ), + encoding="utf-8", ) reprec = pytester.inline_run() reprec.assertoutcome(passed=2) @@ -1671,7 +1682,8 @@ class TestFixtureManagerParseFactories: def teardown_module(): values[:] = [] """ - ) + ), + encoding="utf-8", ) package.joinpath("test_x.py").write_text( textwrap.dedent( @@ -1680,7 +1692,8 @@ class TestFixtureManagerParseFactories: def test_x(): assert values == ["package"] """ - ) + ), + encoding="utf-8", ) package = pytester.mkdir("package2") package.joinpath("__init__.py").write_text( @@ -1692,7 +1705,8 @@ class TestFixtureManagerParseFactories: def teardown_module(): values[:] = [] """ - ) + ), + encoding="utf-8", ) package.joinpath("test_x.py").write_text( textwrap.dedent( @@ -1701,7 +1715,8 @@ class TestFixtureManagerParseFactories: def test_x(): assert values == ["package2"] """ - ) + ), + encoding="utf-8", ) reprec = pytester.inline_run() reprec.assertoutcome(passed=2) @@ -1714,7 +1729,7 @@ class TestFixtureManagerParseFactories: ) pytester.syspathinsert(pytester.path.name) package = pytester.mkdir("package") - package.joinpath("__init__.py").write_text("") + package.joinpath("__init__.py").write_text("", encoding="utf-8") package.joinpath("conftest.py").write_text( textwrap.dedent( """\ @@ -1731,7 +1746,8 @@ class TestFixtureManagerParseFactories: yield values values.pop() """ - ) + ), + encoding="utf-8", ) package.joinpath("test_x.py").write_text( textwrap.dedent( @@ -1742,7 +1758,8 @@ class TestFixtureManagerParseFactories: def test_package(one): assert values == ["package-auto", "package"] """ - ) + ), + encoding="utf-8", ) reprec = pytester.inline_run() reprec.assertoutcome(passed=2) @@ -1892,8 +1909,12 @@ class TestAutouseDiscovery: """ ) conftest.rename(a.joinpath(conftest.name)) - a.joinpath("test_something.py").write_text("def test_func(): pass") - b.joinpath("test_otherthing.py").write_text("def test_func(): pass") + a.joinpath("test_something.py").write_text( + "def test_func(): pass", encoding="utf-8" + ) + b.joinpath("test_otherthing.py").write_text( + "def test_func(): pass", encoding="utf-8" + ) result = pytester.runpytest() result.stdout.fnmatch_lines( """ @@ -1939,7 +1960,8 @@ class TestAutouseManagement: import sys sys._myapp = "hello" """ - ) + ), + encoding="utf-8", ) sub = pkgdir.joinpath("tests") sub.mkdir() @@ -1952,7 +1974,8 @@ class TestAutouseManagement: def test_app(): assert sys._myapp == "hello" """ - ) + ), + encoding="utf-8", ) reprec = pytester.inline_run("-s") reprec.assertoutcome(passed=1) @@ -2882,7 +2905,7 @@ class TestFixtureMarker: def browser(request): def finalize(): - sys.stdout.write_text('Finalized') + sys.stdout.write_text('Finalized', encoding='utf-8') request.addfinalizer(finalize) return {} """ @@ -2900,7 +2923,8 @@ class TestFixtureMarker: def test_browser(browser): assert browser['visited'] is True """ - ) + ), + encoding="utf-8", ) reprec = pytester.runpytest("-s") for test in ["test_browser"]: @@ -3855,7 +3879,8 @@ class TestParameterizedSubRequest: def fix_with_param(request): return request.param """ - ) + ), + encoding="utf-8", ) testfile = tests_dir.joinpath("test_foos.py") @@ -3867,7 +3892,8 @@ class TestParameterizedSubRequest: def test_foo(request): request.getfixturevalue('fix_with_param') """ - ) + ), + encoding="utf-8", ) os.chdir(tests_dir) @@ -4196,7 +4222,7 @@ class TestScopeOrdering: └── test_2.py """ root = pytester.mkdir("root") - root.joinpath("__init__.py").write_text("values = []") + root.joinpath("__init__.py").write_text("values = []", encoding="utf-8") sub1 = root.joinpath("sub1") sub1.mkdir() sub1.joinpath("__init__.py").touch() @@ -4211,7 +4237,8 @@ class TestScopeOrdering: yield values assert values.pop() == "pre-sub1" """ - ) + ), + encoding="utf-8", ) sub1.joinpath("test_1.py").write_text( textwrap.dedent( @@ -4220,7 +4247,8 @@ class TestScopeOrdering: def test_1(fix): assert values == ["pre-sub1"] """ - ) + ), + encoding="utf-8", ) sub2 = root.joinpath("sub2") sub2.mkdir() @@ -4236,7 +4264,8 @@ class TestScopeOrdering: yield values assert values.pop() == "pre-sub2" """ - ) + ), + encoding="utf-8", ) sub2.joinpath("test_2.py").write_text( textwrap.dedent( @@ -4245,7 +4274,8 @@ class TestScopeOrdering: def test_2(fix): assert values == ["pre-sub2"] """ - ) + ), + encoding="utf-8", ) reprec = pytester.inline_run() reprec.assertoutcome(passed=2) diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index c1cc9c3d3..a9e9b5269 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -1443,7 +1443,8 @@ class TestMetafuncFunctional: def pytest_generate_tests(metafunc): assert metafunc.function.__name__ == "test_1" """ - ) + ), + encoding="utf-8", ) sub2.joinpath("conftest.py").write_text( textwrap.dedent( @@ -1451,10 +1452,15 @@ class TestMetafuncFunctional: def pytest_generate_tests(metafunc): assert metafunc.function.__name__ == "test_2" """ - ) + ), + encoding="utf-8", + ) + sub1.joinpath("test_in_sub1.py").write_text( + "def test_1(): pass", encoding="utf-8" + ) + sub2.joinpath("test_in_sub2.py").write_text( + "def test_2(): pass", encoding="utf-8" ) - sub1.joinpath("test_in_sub1.py").write_text("def test_1(): pass") - sub2.joinpath("test_in_sub2.py").write_text("def test_2(): pass") result = pytester.runpytest("--keep-duplicates", "-v", "-s", sub1, sub2, sub1) result.assert_outcomes(passed=3) diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 245241af2..057b609ac 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -160,7 +160,8 @@ class TestAssertionRewrite: "def special_asserter():\n" " def special_assert(x, y):\n" " assert x == y\n" - " return special_assert\n" + " return special_assert\n", + encoding="utf-8", ) pytester.makeconftest('pytest_plugins = ["plugin"]') pytester.makepyfile("def test(special_asserter): special_asserter(1, 2)\n") @@ -173,7 +174,9 @@ class TestAssertionRewrite: pytester.makepyfile(test_y="x = 1") xdir = pytester.mkdir("x") pytester.mkpydir(str(xdir.joinpath("test_Y"))) - xdir.joinpath("test_Y").joinpath("__init__.py").write_text("x = 2") + xdir.joinpath("test_Y").joinpath("__init__.py").write_text( + "x = 2", encoding="utf-8" + ) pytester.makepyfile( "import test_y\n" "import test_Y\n" @@ -726,7 +729,7 @@ class TestAssertionRewrite: class TestRewriteOnImport: def test_pycache_is_a_file(self, pytester: Pytester) -> None: - pytester.path.joinpath("__pycache__").write_text("Hello") + pytester.path.joinpath("__pycache__").write_text("Hello", encoding="utf-8") pytester.makepyfile( """ def test_rewritten(): @@ -903,7 +906,8 @@ def test_rewritten(): pkg.joinpath("test_blah.py").write_text( """ def test_rewritten(): - assert "@py_builtins" in globals()""" + assert "@py_builtins" in globals()""", + encoding="utf-8", ) assert pytester.runpytest().ret == 0 @@ -1066,7 +1070,7 @@ class TestAssertionRewriteHookDetails: source = tmp_path / "source.py" pyc = Path(str(source) + "c") - source.write_text("def test(): pass") + source.write_text("def test(): pass", encoding="utf-8") py_compile.compile(str(source), str(pyc)) contents = pyc.read_bytes() @@ -1092,7 +1096,7 @@ class TestAssertionRewriteHookDetails: fn = tmp_path / "source.py" pyc = Path(str(fn) + "c") - fn.write_text("def test(): assert True") + fn.write_text("def test(): assert True", encoding="utf-8") source_stat, co = _rewrite_test(fn, config) _write_pyc(state, co, source_stat, pyc) @@ -1187,9 +1191,10 @@ class TestAssertionRewriteHookDetails: data = pkgutil.get_data('foo.test_foo', 'data.txt') assert data == b'Hey' """ - ) + ), + encoding="utf-8", ) - path.joinpath("data.txt").write_text("Hey") + path.joinpath("data.txt").write_text("Hey", encoding="utf-8") result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 passed*"]) diff --git a/testing/test_collection.py b/testing/test_collection.py index 302139872..3e1a04443 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -140,7 +140,7 @@ class TestCollectFS: ensure_file(tmp_path / ".bzr" / "test_notfound.py") ensure_file(tmp_path / "normal" / "test_found.py") for x in tmp_path.rglob("test_*.py"): - x.write_text("def test_hello(): pass", "utf-8") + x.write_text("def test_hello(): pass", encoding="utf-8") result = pytester.runpytest("--collect-only") s = result.stdout.str() @@ -162,7 +162,7 @@ class TestCollectFS: bindir = "Scripts" if sys.platform.startswith("win") else "bin" ensure_file(pytester.path / "virtual" / bindir / fname) testfile = ensure_file(pytester.path / "virtual" / "test_invenv.py") - testfile.write_text("def test_hello(): pass") + testfile.write_text("def test_hello(): pass", encoding="utf-8") # by default, ignore tests inside a virtualenv result = pytester.runpytest() @@ -192,7 +192,7 @@ class TestCollectFS: # norecursedirs takes priority ensure_file(pytester.path / ".virtual" / bindir / fname) testfile = ensure_file(pytester.path / ".virtual" / "test_invenv.py") - testfile.write_text("def test_hello(): pass") + testfile.write_text("def test_hello(): pass", encoding="utf-8") result = pytester.runpytest("--collect-in-virtualenv") result.stdout.no_fnmatch_line("*test_invenv*") # ...unless the virtualenv is explicitly given on the CLI @@ -231,10 +231,14 @@ class TestCollectFS: ) tmp_path = pytester.path ensure_file(tmp_path / "mydir" / "test_hello.py").write_text( - "def test_1(): pass" + "def test_1(): pass", encoding="utf-8" + ) + ensure_file(tmp_path / "xyz123" / "test_2.py").write_text( + "def test_2(): 0/0", encoding="utf-8" + ) + ensure_file(tmp_path / "xy" / "test_ok.py").write_text( + "def test_3(): pass", encoding="utf-8" ) - ensure_file(tmp_path / "xyz123" / "test_2.py").write_text("def test_2(): 0/0") - ensure_file(tmp_path / "xy" / "test_ok.py").write_text("def test_3(): pass") rec = pytester.inline_run() rec.assertoutcome(passed=1) rec = pytester.inline_run("xyz123/test_2.py") @@ -248,12 +252,14 @@ class TestCollectFS: """ ) tmp_path = pytester.path - ensure_file(tmp_path / "a" / "test_1.py").write_text("def test_a(): pass") + ensure_file(tmp_path / "a" / "test_1.py").write_text( + "def test_a(): pass", encoding="utf-8" + ) ensure_file(tmp_path / "b" / "tests" / "test_2.py").write_text( - "def test_b(): pass" + "def test_b(): pass", encoding="utf-8" ) ensure_file(tmp_path / "c" / "tests" / "test_3.py").write_text( - "def test_c(): pass" + "def test_c(): pass", encoding="utf-8" ) # executing from rootdir only tests from `testpaths` directories @@ -349,8 +355,8 @@ class TestCustomConftests: """ ) sub = pytester.mkdir("xy123") - ensure_file(sub / "test_hello.py").write_text("syntax error") - sub.joinpath("conftest.py").write_text("syntax error") + ensure_file(sub / "test_hello.py").write_text("syntax error", encoding="utf-8") + sub.joinpath("conftest.py").write_text("syntax error", encoding="utf-8") pytester.makepyfile("def test_hello(): pass") pytester.makepyfile(test_one="syntax error") result = pytester.runpytest("--fulltrace") @@ -1060,13 +1066,18 @@ def test_fixture_scope_sibling_conftests(pytester: Pytester) -> None: def fix(): return 1 """ - ) + ), + encoding="utf-8", + ) + foo_path.joinpath("test_foo.py").write_text( + "def test_foo(fix): assert fix == 1", encoding="utf-8" ) - foo_path.joinpath("test_foo.py").write_text("def test_foo(fix): assert fix == 1") # Tests in `food/` should not see the conftest fixture from `foo/` food_path = pytester.mkpydir("food") - food_path.joinpath("test_food.py").write_text("def test_food(fix): assert fix == 1") + food_path.joinpath("test_food.py").write_text( + "def test_food(fix): assert fix == 1", encoding="utf-8" + ) res = pytester.runpytest() assert res.ret == 1 @@ -1197,7 +1208,8 @@ def test_collect_with_chdir_during_import(pytester: Pytester) -> None: os.chdir(%r) """ % (str(subdir),) - ) + ), + encoding="utf-8", ) pytester.makepyfile( """ @@ -1227,8 +1239,12 @@ def test_collect_pyargs_with_testpaths( ) -> None: testmod = pytester.mkdir("testmod") # NOTE: __init__.py is not collected since it does not match python_files. - testmod.joinpath("__init__.py").write_text("def test_func(): pass") - testmod.joinpath("test_file.py").write_text("def test_func(): pass") + testmod.joinpath("__init__.py").write_text( + "def test_func(): pass", encoding="utf-8" + ) + testmod.joinpath("test_file.py").write_text( + "def test_func(): pass", encoding="utf-8" + ) root = pytester.mkdir("root") root.joinpath("pytest.ini").write_text( @@ -1238,7 +1254,8 @@ def test_collect_pyargs_with_testpaths( addopts = --pyargs testpaths = testmod """ - ) + ), + encoding="utf-8", ) monkeypatch.setenv("PYTHONPATH", str(pytester.path), prepend=os.pathsep) with monkeypatch.context() as mp: @@ -1323,6 +1340,7 @@ def test_collect_symlink_out_of_tree(pytester: Pytester) -> None: assert request.node.nodeid == "test_real.py::test_nodeid" """ ), + encoding="utf-8", ) out_of_tree = pytester.mkdir("out_of_tree") diff --git a/testing/test_config.py b/testing/test_config.py index cdeb67ace..3aec5d763 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -87,7 +87,8 @@ class TestParseIni: [pytest] addopts = --verbose """ - ) + ), + encoding="utf-8", ) config = pytester.parseconfig(tmp_path) assert config.option.color == "no" @@ -127,7 +128,8 @@ class TestParseIni: """.format( section=section ) - ) + ), + encoding="utf-8", ) config = pytester.parseconfig() assert config.getini("minversion") == "3.36" @@ -150,7 +152,8 @@ class TestParseIni: [pytest] minversion = 2.0 """ - ) + ), + encoding="utf-8", ) pytester.path.joinpath("pytest.ini").write_text( textwrap.dedent( @@ -158,13 +161,16 @@ class TestParseIni: [pytest] minversion = 1.5 """ - ) + ), + encoding="utf-8", ) config = pytester.parseconfigure(sub) assert config.getini("minversion") == "2.0" def test_ini_parse_error(self, pytester: Pytester) -> None: - pytester.path.joinpath("pytest.ini").write_text("addopts = -x") + pytester.path.joinpath("pytest.ini").write_text( + "addopts = -x", encoding="utf-8" + ) result = pytester.runpytest() assert result.ret != 0 result.stderr.fnmatch_lines("ERROR: *pytest.ini:1: no section header defined") @@ -634,7 +640,7 @@ class TestConfigAPI: def test_getconftest_pathlist(self, pytester: Pytester, tmp_path: Path) -> None: somepath = tmp_path.joinpath("x", "y", "z") p = tmp_path.joinpath("conftest.py") - p.write_text(f"mylist = {['.', str(somepath)]}") + p.write_text(f"mylist = {['.', str(somepath)]}", encoding="utf-8") config = pytester.parseconfigure(p) assert ( config._getconftest_pathlist("notexist", path=tmp_path, rootpath=tmp_path) @@ -910,7 +916,8 @@ class TestConfigFromdictargs: [pytest] name = value """ - ) + ), + encoding="utf-8", ) inifilename = "../../foo/bar.ini" @@ -927,7 +934,8 @@ class TestConfigFromdictargs: name = wrong-value should_not_be_set = true """ - ) + ), + encoding="utf-8", ) with MonkeyPatch.context() as mp: mp.chdir(cwd) @@ -1387,7 +1395,7 @@ class TestRootdir: ) def test_with_ini(self, tmp_path: Path, name: str, contents: str) -> None: inipath = tmp_path / name - inipath.write_text(contents, "utf-8") + inipath.write_text(contents, encoding="utf-8") a = tmp_path / "a" a.mkdir() @@ -1446,7 +1454,7 @@ class TestRootdir: ) -> None: p = tmp_path / name p.touch() - p.write_text(contents, "utf-8") + p.write_text(contents, encoding="utf-8") rootpath, inipath, ini_config = determine_setup(str(p), [str(tmp_path)]) assert rootpath == tmp_path assert inipath == p @@ -1542,7 +1550,8 @@ class TestOverrideIniArgs: custom = 1.0""".format( section=section ) - ) + ), + encoding="utf-8", ) pytester.makeconftest( """ diff --git a/testing/test_conftest.py b/testing/test_conftest.py index f857cde04..7ec9feb8b 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -47,8 +47,12 @@ class TestConftestValueAccessGlobal: ) -> Generator[Path, None, None]: 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("a=1 ; Directory = 3") - tmp_path.joinpath("adir/b/conftest.py").write_text("b=2 ; a = 1.5") + tmp_path.joinpath("adir/conftest.py").write_text( + "a=1 ; Directory = 3", encoding="utf-8" + ) + tmp_path.joinpath("adir/b/conftest.py").write_text( + "b=2 ; a = 1.5", encoding="utf-8" + ) if request.param == "inpackage": tmp_path.joinpath("adir/__init__.py").touch() tmp_path.joinpath("adir/b/__init__.py").touch() @@ -123,8 +127,12 @@ class TestConftestValueAccessGlobal: def test_conftest_in_nonpkg_with_init(tmp_path: Path, _sys_snapshot) -> None: tmp_path.joinpath("adir-1.0/b").mkdir(parents=True) - tmp_path.joinpath("adir-1.0/conftest.py").write_text("a=1 ; Directory = 3") - tmp_path.joinpath("adir-1.0/b/conftest.py").write_text("b=2 ; a = 1.5") + tmp_path.joinpath("adir-1.0/conftest.py").write_text( + "a=1 ; Directory = 3", encoding="utf-8" + ) + tmp_path.joinpath("adir-1.0/b/conftest.py").write_text( + "b=2 ; a = 1.5", encoding="utf-8" + ) tmp_path.joinpath("adir-1.0/b/__init__.py").touch() tmp_path.joinpath("adir-1.0/__init__.py").touch() ConftestWithSetinitial(tmp_path.joinpath("adir-1.0", "b")) @@ -167,7 +175,7 @@ def test_conftest_global_import(pytester: Pytester) -> None: sub = Path("sub") sub.mkdir() subconf = sub / "conftest.py" - subconf.write_text("y=4") + subconf.write_text("y=4", encoding="utf-8") mod2 = conf._importconftest(subconf, importmode="prepend", rootpath=Path.cwd()) assert mod != mod2 assert mod2.y == 4 @@ -246,7 +254,8 @@ def test_conftest_confcutdir(pytester: Pytester) -> None: def pytest_addoption(parser): parser.addoption("--xyz", action="store_true") """ - ) + ), + encoding="utf-8", ) result = pytester.runpytest("-h", "--confcutdir=%s" % x, x) result.stdout.fnmatch_lines(["*--xyz*"]) @@ -274,9 +283,12 @@ def test_installed_conftest_is_picked_up(pytester: Pytester, tmp_path: Path) -> @pytest.fixture def fix(): return None """ - ) + ), + encoding="utf-8", + ) + tmp_path.joinpath("foo", "test_it.py").write_text( + "def test_it(fix): pass", encoding="utf-8" ) - tmp_path.joinpath("foo", "test_it.py").write_text("def test_it(fix): pass") result = pytester.runpytest("--pyargs", "foo") assert result.ret == 0 @@ -401,7 +413,8 @@ def test_conftest_existing_junitxml(pytester: Pytester) -> None: def pytest_addoption(parser): parser.addoption("--xyz", action="store_true") """ - ) + ), + encoding="utf-8", ) pytester.makefile(ext=".xml", junit="") # Writes junit.xml result = pytester.runpytest("-h", "--junitxml", "junit.xml") @@ -412,7 +425,7 @@ def test_conftest_import_order(pytester: Pytester, monkeypatch: MonkeyPatch) -> ct1 = pytester.makeconftest("") sub = pytester.mkdir("sub") ct2 = sub / "conftest.py" - ct2.write_text("") + ct2.write_text("", encoding="utf-8") def impct(p, importmode, root): return p @@ -450,7 +463,8 @@ def test_fixture_dependency(pytester: Pytester) -> None: def bar(foo): return 'bar' """ - ) + ), + encoding="utf-8", ) subsub = sub.joinpath("subsub") subsub.mkdir() @@ -467,7 +481,8 @@ def test_fixture_dependency(pytester: Pytester) -> None: def test_event_fixture(bar): assert bar == 'sub bar' """ - ) + ), + encoding="utf-8", ) result = pytester.runpytest("sub") result.stdout.fnmatch_lines(["*1 passed*"]) @@ -481,10 +496,11 @@ def test_conftest_found_with_double_dash(pytester: Pytester) -> None: def pytest_addoption(parser): parser.addoption("--hello-world", action="store_true") """ - ) + ), + encoding="utf-8", ) p = sub.joinpath("test_hello.py") - p.write_text("def test_hello(): pass") + p.write_text("def test_hello(): pass", encoding="utf-8") result = pytester.runpytest(str(p) + "::test_hello", "-h") result.stdout.fnmatch_lines( """ @@ -508,7 +524,8 @@ class TestConftestVisibility: def fxtr(): return "from-package" """ - ) + ), + encoding="utf-8", ) package.joinpath("test_pkgroot.py").write_text( textwrap.dedent( @@ -516,7 +533,8 @@ class TestConftestVisibility: def test_pkgroot(fxtr): assert fxtr == "from-package" """ - ) + ), + encoding="utf-8", ) swc = package.joinpath("swc") @@ -530,7 +548,8 @@ class TestConftestVisibility: def fxtr(): return "from-swc" """ - ) + ), + encoding="utf-8", ) swc.joinpath("test_with_conftest.py").write_text( textwrap.dedent( @@ -538,7 +557,8 @@ class TestConftestVisibility: def test_with_conftest(fxtr): assert fxtr == "from-swc" """ - ) + ), + encoding="utf-8", ) snc = package.joinpath("snc") @@ -551,7 +571,8 @@ class TestConftestVisibility: assert fxtr == "from-package" # No local conftest.py, so should # use value from parent dir's """ - ) + ), + encoding="utf-8", ) print("created directory structure:") for x in pytester.path.glob("**/"): @@ -625,7 +646,8 @@ def test_search_conftest_up_to_inifile( @pytest.fixture def fix1(): pass """ - ) + ), + encoding="utf-8", ) src.joinpath("test_foo.py").write_text( textwrap.dedent( @@ -635,7 +657,8 @@ def test_search_conftest_up_to_inifile( def test_2(out_of_reach): pass """ - ) + ), + encoding="utf-8", ) root.joinpath("conftest.py").write_text( textwrap.dedent( @@ -644,7 +667,8 @@ def test_search_conftest_up_to_inifile( @pytest.fixture def out_of_reach(): pass """ - ) + ), + encoding="utf-8", ) args = [str(src)] @@ -727,7 +751,8 @@ def test_required_option_help(pytester: Pytester) -> None: def pytest_addoption(parser): parser.addoption("--xyz", action="store_true", required=True) """ - ) + ), + encoding="utf-8", ) result = pytester.runpytest("-h", x) result.stdout.no_fnmatch_line("*argument --xyz is required*") diff --git a/testing/test_doctest.py b/testing/test_doctest.py index d2944fa2b..dfe569987 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -357,7 +357,8 @@ class TestDoctests: >>> 1/0 ''' """ - ) + ), + encoding="utf-8", ) result = pytester.runpytest("--doctest-modules") result.stdout.fnmatch_lines( @@ -448,7 +449,8 @@ class TestDoctests: """\ import asdalsdkjaslkdjasd """ - ) + ), + encoding="utf-8", ) pytester.maketxtfile( """ @@ -492,7 +494,8 @@ class TestDoctests: 2 ''' """ - ) + ), + encoding="utf-8", ) result = pytester.runpytest(p, "--doctest-modules") result.stdout.fnmatch_lines( @@ -1566,7 +1569,9 @@ def test_warning_on_unwrap_of_broken_object( def test_is_setup_py_not_named_setup_py(tmp_path: Path) -> None: not_setup_py = tmp_path.joinpath("not_setup.py") - not_setup_py.write_text('from setuptools import setup; setup(name="foo")') + not_setup_py.write_text( + 'from setuptools import setup; setup(name="foo")', encoding="utf-8" + ) assert not _is_setup_py(not_setup_py) diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index 500e24453..8a9dd600b 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -324,7 +324,8 @@ def test_importerror(pytester: Pytester) -> None: x = 1 """ - ) + ), + encoding="utf-8", ) pytester.path.joinpath("test_importerror.py").write_text( textwrap.dedent( @@ -332,7 +333,8 @@ def test_importerror(pytester: Pytester) -> None: def test_importerror(monkeypatch): monkeypatch.setattr('package.a.x', 2) """ - ) + ), + encoding="utf-8", ) result = pytester.runpytest() result.stdout.fnmatch_lines( @@ -434,11 +436,13 @@ def test_syspath_prepend_with_namespace_packages( ns = d.joinpath("ns_pkg") ns.mkdir() ns.joinpath("__init__.py").write_text( - "__import__('pkg_resources').declare_namespace(__name__)" + "__import__('pkg_resources').declare_namespace(__name__)", encoding="utf-8" ) lib = ns.joinpath(dirname) lib.mkdir() - lib.joinpath("__init__.py").write_text("def check(): return %r" % dirname) + lib.joinpath("__init__.py").write_text( + "def check(): return %r" % dirname, encoding="utf-8" + ) monkeypatch.syspath_prepend("hello") import ns_pkg.hello diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index 481d7a606..0fd372b51 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -100,13 +100,13 @@ class TestImportPath: def setuptestfs(self, path: Path) -> None: # print "setting up test fs for", repr(path) samplefile = path / "samplefile" - samplefile.write_text("samplefile\n") + samplefile.write_text("samplefile\n", encoding="utf-8") execfile = path / "execfile" - execfile.write_text("x=42") + execfile.write_text("x=42", encoding="utf-8") execfilepy = path / "execfile.py" - execfilepy.write_text("x=42") + execfilepy.write_text("x=42", encoding="utf-8") d = {1: 2, "hello": "world", "answer": 42} path.joinpath("samplepickle").write_bytes(pickle.dumps(d, 1)) diff --git a/tox.ini b/tox.ini index 88ae16dea..05c1842af 100644 --- a/tox.ini +++ b/tox.ini @@ -38,6 +38,10 @@ passenv = setenv = _PYTEST_TOX_DEFAULT_POSARGS={env:_PYTEST_TOX_POSARGS_DOCTESTING:} {env:_PYTEST_TOX_POSARGS_LSOF:} {env:_PYTEST_TOX_POSARGS_XDIST:} + # See https://docs.python.org/3/library/io.html#io-encoding-warning + # If we don't enable this, neither can any of our downstream users! + PYTHONWARNDEFAULTENCODING=1 + # Configuration to run with coverage similar to CI, e.g. # "tox -e py37-coverage". coverage: _PYTEST_TOX_COVERAGE_RUN=coverage run -m From 7e510769b4a9b67ef3802a894b36e7978fa222f8 Mon Sep 17 00:00:00 2001 From: Zac Hatfield-Dodds Date: Tue, 20 Jun 2023 04:55:39 -0700 Subject: [PATCH 48/62] Encoding for subprocess.run --- src/_pytest/pytester.py | 2 ++ testing/test_parseopt.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index a9299944d..3df52ebe8 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -6,6 +6,7 @@ import collections.abc import contextlib import gc import importlib +import locale import os import platform import re @@ -129,6 +130,7 @@ class LsofFdLeakChecker: stderr=subprocess.DEVNULL, check=True, text=True, + encoding=locale.getpreferredencoding(False), ).stdout def isopen(line: str) -> bool: diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index 992f49bc5..c051ec338 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -1,4 +1,5 @@ import argparse +import locale import os import shlex import subprocess @@ -289,6 +290,10 @@ class TestParser: def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None: + try: + encoding = locale.getencoding() # New in Python 3.11, ignores utf-8 mode + except AttributeError: + encoding = locale.getpreferredencoding(False) try: bash_version = subprocess.run( ["bash", "--version"], @@ -296,6 +301,7 @@ def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None: stderr=subprocess.DEVNULL, check=True, text=True, + encoding=encoding, ).stdout except (OSError, subprocess.CalledProcessError): pytest.skip("bash is not available") From 661b938fcad0ece7d017d76d412fff220e43a865 Mon Sep 17 00:00:00 2001 From: Zac Hatfield-Dodds Date: Tue, 20 Jun 2023 04:55:40 -0700 Subject: [PATCH 49/62] Add encoding in more tests --- scripts/towncrier-draft-to-file.py | 4 +- testing/_py/test_local.py | 98 ++++++++++++++++++------------ testing/acceptance_test.py | 23 ++++--- testing/code/test_excinfo.py | 6 +- testing/code/test_source.py | 2 +- testing/logging/test_reporting.py | 18 +++--- testing/python/collect.py | 47 +++++++++----- testing/test_assertion.py | 8 +-- testing/test_assertrewrite.py | 2 +- testing/test_cacheprovider.py | 13 ++-- testing/test_capture.py | 13 ++-- testing/test_collection.py | 21 ++++--- testing/test_conftest.py | 2 +- testing/test_junitxml.py | 12 ++-- testing/test_link_resolve.py | 3 +- testing/test_monkeypatch.py | 2 +- testing/test_parseopt.py | 2 +- testing/test_pathlib.py | 26 +++++--- testing/test_pluginmanager.py | 2 +- testing/test_pytester.py | 2 +- testing/test_reports.py | 2 +- testing/test_session.py | 12 ++-- testing/test_skipping.py | 21 ++++--- testing/test_stepwise.py | 2 +- testing/test_terminal.py | 8 ++- testing/test_tmpdir.py | 2 +- testing/test_warnings.py | 4 +- 27 files changed, 213 insertions(+), 144 deletions(-) diff --git a/scripts/towncrier-draft-to-file.py b/scripts/towncrier-draft-to-file.py index 81507b40b..1f1068689 100644 --- a/scripts/towncrier-draft-to-file.py +++ b/scripts/towncrier-draft-to-file.py @@ -7,7 +7,9 @@ def main(): Platform agnostic wrapper script for towncrier. Fixes the issue (#7251) where windows users are unable to natively run tox -e docs to build pytest docs. """ - with open("doc/en/_changelog_towncrier_draft.rst", "w") as draft_file: + with open( + "doc/en/_changelog_towncrier_draft.rst", "w", encoding="utf-8" + ) as draft_file: return call(("towncrier", "--draft"), stdout=draft_file) diff --git a/testing/_py/test_local.py b/testing/_py/test_local.py index b463d769d..348682b53 100644 --- a/testing/_py/test_local.py +++ b/testing/_py/test_local.py @@ -1,7 +1,9 @@ +import contextlib import multiprocessing import os import sys import time +import warnings from unittest import mock import pytest @@ -9,6 +11,14 @@ from py import error from py.path import local +@contextlib.contextmanager +def ignore_encoding_warning(): + with warnings.catch_warnings(): + with contextlib.suppress(NameError): # new in 3.10 + warnings.simplefilter("ignore", EncodingWarning) + yield + + class CommonFSTests: def test_constructor_equality(self, path1): p = path1.__class__(path1) @@ -223,7 +233,8 @@ class CommonFSTests: assert not (path1 < path1) def test_simple_read(self, path1): - x = path1.join("samplefile").read("r") + with ignore_encoding_warning(): + x = path1.join("samplefile").read("r") assert x == "samplefile\n" def test_join_div_operator(self, path1): @@ -265,12 +276,14 @@ class CommonFSTests: def test_readlines(self, path1): fn = path1.join("samplefile") - contents = fn.readlines() + with ignore_encoding_warning(): + contents = fn.readlines() assert contents == ["samplefile\n"] def test_readlines_nocr(self, path1): fn = path1.join("samplefile") - contents = fn.readlines(cr=0) + with ignore_encoding_warning(): + contents = fn.readlines(cr=0) assert contents == ["samplefile", ""] def test_file(self, path1): @@ -362,8 +375,8 @@ class CommonFSTests: initpy.copy(copied) try: assert copied.check() - s1 = initpy.read() - s2 = copied.read() + s1 = initpy.read_text(encoding="utf-8") + s2 = copied.read_text(encoding="utf-8") assert s1 == s2 finally: if copied.check(): @@ -376,8 +389,8 @@ class CommonFSTests: otherdir.copy(copied) assert copied.check(dir=1) assert copied.join("__init__.py").check(file=1) - s1 = otherdir.join("__init__.py").read() - s2 = copied.join("__init__.py").read() + s1 = otherdir.join("__init__.py").read_text(encoding="utf-8") + s2 = copied.join("__init__.py").read_text(encoding="utf-8") assert s1 == s2 finally: if copied.check(dir=1): @@ -463,13 +476,13 @@ def setuptestfs(path): return # print "setting up test fs for", repr(path) samplefile = path.ensure("samplefile") - samplefile.write("samplefile\n") + samplefile.write_text("samplefile\n", encoding="utf-8") execfile = path.ensure("execfile") - execfile.write("x=42") + execfile.write_text("x=42", encoding="utf-8") execfilepy = path.ensure("execfile.py") - execfilepy.write("x=42") + execfilepy.write_text("x=42", encoding="utf-8") d = {1: 2, "hello": "world", "answer": 42} path.ensure("samplepickle").dump(d) @@ -481,22 +494,24 @@ def setuptestfs(path): otherdir.ensure("__init__.py") module_a = otherdir.ensure("a.py") - module_a.write("from .b import stuff as result\n") + module_a.write_text("from .b import stuff as result\n", encoding="utf-8") module_b = otherdir.ensure("b.py") - module_b.write('stuff="got it"\n') + module_b.write_text('stuff="got it"\n', encoding="utf-8") module_c = otherdir.ensure("c.py") - module_c.write( + module_c.write_text( """import py; import otherdir.a value = otherdir.a.result -""" +""", + encoding="utf-8", ) module_d = otherdir.ensure("d.py") - module_d.write( + module_d.write_text( """import py; from otherdir import a value2 = a.result -""" +""", + encoding="utf-8", ) @@ -534,9 +549,11 @@ def batch_make_numbered_dirs(rootdir, repeats): for i in range(repeats): dir_ = local.make_numbered_dir(prefix="repro-", rootdir=rootdir) file_ = dir_.join("foo") - file_.write("%s" % i) - actual = int(file_.read()) - assert actual == i, f"int(file_.read()) is {actual} instead of {i}" + file_.write_text("%s" % i, encoding="utf-8") + actual = int(file_.read_text(encoding="utf-8")) + assert ( + actual == i + ), f"int(file_.read_text(encoding='utf-8')) is {actual} instead of {i}" dir_.join(".lock").remove(ignore_errors=True) return True @@ -692,14 +709,14 @@ class TestLocalPath(CommonFSTests): def test_open_and_ensure(self, path1): p = path1.join("sub1", "sub2", "file") - with p.open("w", ensure=1) as f: + with p.open("w", ensure=1, encoding="utf-8") as f: f.write("hello") - assert p.read() == "hello" + assert p.read_text(encoding="utf-8") == "hello" def test_write_and_ensure(self, path1): p = path1.join("sub1", "sub2", "file") - p.write("hello", ensure=1) - assert p.read() == "hello" + p.write_text("hello", ensure=1, encoding="utf-8") + assert p.read_text(encoding="utf-8") == "hello" @pytest.mark.parametrize("bin", (False, True)) def test_dump(self, tmpdir, bin): @@ -770,9 +787,9 @@ class TestLocalPath(CommonFSTests): newfile = tmpdir.join("test1", "test") newfile.ensure() assert newfile.check(file=1) - newfile.write("42") + newfile.write_text("42", encoding="utf-8") newfile.ensure() - s = newfile.read() + s = newfile.read_text(encoding="utf-8") assert s == "42" def test_ensure_filepath_withoutdir(self, tmpdir): @@ -806,9 +823,9 @@ class TestLocalPath(CommonFSTests): newfilename = "/test" * 60 # type:ignore[unreachable] l1 = tmpdir.join(newfilename) l1.ensure(file=True) - l1.write("foo") + l1.write_text("foo", encoding="utf-8") l2 = tmpdir.join(newfilename) - assert l2.read() == "foo" + assert l2.read_text(encoding="utf-8") == "foo" def test_visit_depth_first(self, tmpdir): tmpdir.ensure("a", "1") @@ -1278,14 +1295,14 @@ class TestPOSIXLocalPath: def test_hardlink(self, tmpdir): linkpath = tmpdir.join("test") filepath = tmpdir.join("file") - filepath.write("Hello") + filepath.write_text("Hello", encoding="utf-8") nlink = filepath.stat().nlink linkpath.mklinkto(filepath) assert filepath.stat().nlink == nlink + 1 def test_symlink_are_identical(self, tmpdir): filepath = tmpdir.join("file") - filepath.write("Hello") + filepath.write_text("Hello", encoding="utf-8") linkpath = tmpdir.join("test") linkpath.mksymlinkto(filepath) assert linkpath.readlink() == str(filepath) @@ -1293,7 +1310,7 @@ class TestPOSIXLocalPath: def test_symlink_isfile(self, tmpdir): linkpath = tmpdir.join("test") filepath = tmpdir.join("file") - filepath.write("") + filepath.write_text("", encoding="utf-8") linkpath.mksymlinkto(filepath) assert linkpath.check(file=1) assert not linkpath.check(link=0, file=1) @@ -1302,10 +1319,12 @@ class TestPOSIXLocalPath: def test_symlink_relative(self, tmpdir): linkpath = tmpdir.join("test") filepath = tmpdir.join("file") - filepath.write("Hello") + filepath.write_text("Hello", encoding="utf-8") linkpath.mksymlinkto(filepath, absolute=False) assert linkpath.readlink() == "file" - assert filepath.read() == linkpath.read() + assert filepath.read_text(encoding="utf-8") == linkpath.read_text( + encoding="utf-8" + ) def test_symlink_not_existing(self, tmpdir): linkpath = tmpdir.join("testnotexisting") @@ -1338,7 +1357,7 @@ class TestPOSIXLocalPath: def test_realpath_file(self, tmpdir): linkpath = tmpdir.join("test") filepath = tmpdir.join("file") - filepath.write("") + filepath.write_text("", encoding="utf-8") linkpath.mksymlinkto(filepath) realpath = linkpath.realpath() assert realpath.basename == "file" @@ -1383,7 +1402,7 @@ class TestPOSIXLocalPath: atime1 = path.atime() # we could wait here but timer resolution is very # system dependent - path.read() + path.read_binary() time.sleep(ATIME_RESOLUTION) atime2 = path.atime() time.sleep(ATIME_RESOLUTION) @@ -1467,7 +1486,7 @@ class TestPOSIXLocalPath: test_files = ["a", "b", "c"] src = tmpdir.join("src") for f in test_files: - src.join(f).write(f, ensure=True) + src.join(f).write_text(f, ensure=True, encoding="utf-8") dst = tmpdir.join("dst") # a small delay before the copy time.sleep(ATIME_RESOLUTION) @@ -1521,10 +1540,11 @@ class TestUnicodePy2Py3: def test_read_write(self, tmpdir): x = tmpdir.join("hello") part = "hällo" - x.write(part) - assert x.read() == part - x.write(part.encode(sys.getdefaultencoding())) - assert x.read() == part.encode(sys.getdefaultencoding()) + with ignore_encoding_warning(): + x.write(part) + assert x.read() == part + x.write(part.encode(sys.getdefaultencoding())) + assert x.read() == part.encode(sys.getdefaultencoding()) class TestBinaryAndTextMethods: diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 0046d05b8..5658f2fd6 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -613,7 +613,7 @@ class TestInvocationVariants: def test_pyargs_filename_looks_like_module(self, pytester: Pytester) -> None: pytester.path.joinpath("conftest.py").touch() - pytester.path.joinpath("t.py").write_text("def test(): pass") + pytester.path.joinpath("t.py").write_text("def test(): pass", encoding="utf-8") result = pytester.runpytest("--pyargs", "t.py") assert result.ret == ExitCode.OK @@ -622,8 +622,12 @@ class TestInvocationVariants: monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", False) path = pytester.mkpydir("tpkg") - path.joinpath("test_hello.py").write_text("def test_hello(): pass") - path.joinpath("test_world.py").write_text("def test_world(): pass") + path.joinpath("test_hello.py").write_text( + "def test_hello(): pass", encoding="utf-8" + ) + path.joinpath("test_world.py").write_text( + "def test_world(): pass", encoding="utf-8" + ) result = pytester.runpytest("--pyargs", "tpkg") assert result.ret == 0 result.stdout.fnmatch_lines(["*2 passed*"]) @@ -662,13 +666,15 @@ class TestInvocationVariants: ns = d.joinpath("ns_pkg") ns.mkdir() ns.joinpath("__init__.py").write_text( - "__import__('pkg_resources').declare_namespace(__name__)" + "__import__('pkg_resources').declare_namespace(__name__)", + encoding="utf-8", ) lib = ns.joinpath(dirname) lib.mkdir() lib.joinpath("__init__.py").touch() lib.joinpath(f"test_{dirname}.py").write_text( - f"def test_{dirname}(): pass\ndef test_other():pass" + f"def test_{dirname}(): pass\ndef test_other():pass", + encoding="utf-8", ) # The structure of the test directory is now: @@ -754,10 +760,10 @@ class TestInvocationVariants: lib.mkdir() lib.joinpath("__init__.py").touch() lib.joinpath("test_bar.py").write_text( - "def test_bar(): pass\ndef test_other(a_fixture):pass" + "def test_bar(): pass\ndef test_other(a_fixture):pass", encoding="utf-8" ) lib.joinpath("conftest.py").write_text( - "import pytest\n@pytest.fixture\ndef a_fixture():pass" + "import pytest\n@pytest.fixture\ndef a_fixture():pass", encoding="utf-8" ) d_local = pytester.mkdir("symlink_root") @@ -1276,8 +1282,7 @@ def test_tee_stdio_captures_and_live_prints(pytester: Pytester) -> None: result.stderr.fnmatch_lines(["*@this is stderr@*"]) # now ensure the output is in the junitxml - with open(pytester.path.joinpath("output.xml")) as f: - fullXml = f.read() + fullXml = pytester.path.joinpath("output.xml").read_text(encoding="utf-8") assert "@this is stdout@\n" in fullXml assert "@this is stderr@\n" in fullXml diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 88aa5f0f1..e5c030c4d 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -374,7 +374,7 @@ def test_excinfo_no_sourcecode(): def test_excinfo_no_python_sourcecode(tmp_path: Path) -> None: # XXX: simplified locally testable version - tmp_path.joinpath("test.txt").write_text("{{ h()}}:") + tmp_path.joinpath("test.txt").write_text("{{ h()}}:", encoding="utf-8") jinja2 = pytest.importorskip("jinja2") loader = jinja2.FileSystemLoader(str(tmp_path)) @@ -451,7 +451,7 @@ class TestFormattedExcinfo: source = textwrap.dedent(source) modpath = tmp_path.joinpath("mod.py") tmp_path.joinpath("__init__.py").touch() - modpath.write_text(source) + modpath.write_text(source, encoding="utf-8") importlib.invalidate_caches() return import_path(modpath, root=tmp_path) @@ -1023,7 +1023,7 @@ raise ValueError() """ ) excinfo = pytest.raises(ValueError, mod.f) - tmp_path.joinpath("mod.py").write_text("asdf") + tmp_path.joinpath("mod.py").write_text("asdf", encoding="utf-8") excinfo.traceback = excinfo.traceback.filter(excinfo) repr = excinfo.getrepr() repr.toterminal(tw_mock) diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 52417f2f8..dc35c9496 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -294,7 +294,7 @@ def test_source_of_class_at_eof_without_newline(_sys_snapshot, tmp_path: Path) - """ ) path = tmp_path.joinpath("a.py") - path.write_text(str(source)) + path.write_text(str(source), encoding="utf-8") mod: Any = import_path(path, root=tmp_path) s2 = Source(mod.A) assert str(source).strip() == str(s2).strip() diff --git a/testing/logging/test_reporting.py b/testing/logging/test_reporting.py index 14b77236a..0c8e3fd08 100644 --- a/testing/logging/test_reporting.py +++ b/testing/logging/test_reporting.py @@ -81,7 +81,7 @@ def test_root_logger_affected(pytester: Pytester) -> None: # not the info one, because the default level of the root logger is # WARNING. assert os.path.isfile(log_file) - with open(log_file) as rfh: + with open(log_file, encoding="utf-8") as rfh: contents = rfh.read() assert "info text going to logger" not in contents assert "warning text going to logger" in contents @@ -656,7 +656,7 @@ def test_log_file_cli(pytester: Pytester) -> None: # make sure that we get a '0' exit code for the testsuite assert result.ret == 0 assert os.path.isfile(log_file) - with open(log_file) as rfh: + with open(log_file, encoding="utf-8") as rfh: contents = rfh.read() assert "This log message will be shown" in contents assert "This log message won't be shown" not in contents @@ -687,7 +687,7 @@ def test_log_file_cli_level(pytester: Pytester) -> None: # make sure that we get a '0' exit code for the testsuite assert result.ret == 0 assert os.path.isfile(log_file) - with open(log_file) as rfh: + with open(log_file, encoding="utf-8") as rfh: contents = rfh.read() assert "This log message will be shown" in contents assert "This log message won't be shown" not in contents @@ -738,7 +738,7 @@ def test_log_file_ini(pytester: Pytester) -> None: # make sure that we get a '0' exit code for the testsuite assert result.ret == 0 assert os.path.isfile(log_file) - with open(log_file) as rfh: + with open(log_file, encoding="utf-8") as rfh: contents = rfh.read() assert "This log message will be shown" in contents assert "This log message won't be shown" not in contents @@ -777,7 +777,7 @@ def test_log_file_ini_level(pytester: Pytester) -> None: # make sure that we get a '0' exit code for the testsuite assert result.ret == 0 assert os.path.isfile(log_file) - with open(log_file) as rfh: + with open(log_file, encoding="utf-8") as rfh: contents = rfh.read() assert "This log message will be shown" in contents assert "This log message won't be shown" not in contents @@ -985,7 +985,7 @@ def test_log_in_hooks(pytester: Pytester) -> None: ) result = pytester.runpytest() result.stdout.fnmatch_lines(["*sessionstart*", "*runtestloop*", "*sessionfinish*"]) - with open(log_file) as rfh: + with open(log_file, encoding="utf-8") as rfh: contents = rfh.read() assert "sessionstart" in contents assert "runtestloop" in contents @@ -1021,7 +1021,7 @@ def test_log_in_runtest_logreport(pytester: Pytester) -> None: """ ) pytester.runpytest() - with open(log_file) as rfh: + with open(log_file, encoding="utf-8") as rfh: contents = rfh.read() assert contents.count("logreport") == 3 @@ -1065,11 +1065,11 @@ def test_log_set_path(pytester: Pytester) -> None: """ ) pytester.runpytest() - with open(os.path.join(report_dir_base, "test_first")) as rfh: + with open(os.path.join(report_dir_base, "test_first"), encoding="utf-8") as rfh: content = rfh.read() assert "message from test 1" in content - with open(os.path.join(report_dir_base, "test_second")) as rfh: + with open(os.path.join(report_dir_base, "test_second"), encoding="utf-8") as rfh: content = rfh.read() assert "message from test 2" in content diff --git a/testing/python/collect.py b/testing/python/collect.py index de10ce408..9bf6e00d1 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -60,7 +60,8 @@ class TestModule: """.format( str(root2) ) - ) + ), + encoding="utf-8", ) with monkeypatch.context() as mp: mp.chdir(root2) @@ -832,7 +833,8 @@ class TestConftestCustomization: mod = outcome.get_result() mod.obj.hello = "world" """ - ) + ), + encoding="utf-8", ) b.joinpath("test_module.py").write_text( textwrap.dedent( @@ -840,7 +842,8 @@ class TestConftestCustomization: def test_hello(): assert hello == "world" """ - ) + ), + encoding="utf-8", ) reprec = pytester.inline_run() reprec.assertoutcome(passed=1) @@ -861,7 +864,8 @@ class TestConftestCustomization: for func in result: func._some123 = "world" """ - ) + ), + encoding="utf-8", ) b.joinpath("test_module.py").write_text( textwrap.dedent( @@ -874,7 +878,8 @@ class TestConftestCustomization: def test_hello(obj): assert obj == "world" """ - ) + ), + encoding="utf-8", ) reprec = pytester.inline_run() reprec.assertoutcome(passed=1) @@ -974,7 +979,8 @@ def test_setup_only_available_in_subdir(pytester: Pytester) -> None: def pytest_runtest_teardown(item): assert item.path.stem == "test_in_sub1" """ - ) + ), + encoding="utf-8", ) sub2.joinpath("conftest.py").write_text( textwrap.dedent( @@ -987,10 +993,11 @@ def test_setup_only_available_in_subdir(pytester: Pytester) -> None: def pytest_runtest_teardown(item): assert item.path.stem == "test_in_sub2" """ - ) + ), + encoding="utf-8", ) - sub1.joinpath("test_in_sub1.py").write_text("def test_1(): pass") - sub2.joinpath("test_in_sub2.py").write_text("def test_2(): pass") + sub1.joinpath("test_in_sub1.py").write_text("def test_1(): pass", encoding="utf-8") + sub2.joinpath("test_in_sub2.py").write_text("def test_2(): pass", encoding="utf-8") result = pytester.runpytest("-v", "-s") result.assert_outcomes(passed=2) @@ -1378,7 +1385,8 @@ def test_skip_duplicates_by_default(pytester: Pytester) -> None: def test_real(): pass """ - ) + ), + encoding="utf-8", ) result = pytester.runpytest(str(a), str(a)) result.stdout.fnmatch_lines(["*collected 1 item*"]) @@ -1398,7 +1406,8 @@ def test_keep_duplicates(pytester: Pytester) -> None: def test_real(): pass """ - ) + ), + encoding="utf-8", ) result = pytester.runpytest("--keep-duplicates", str(a), str(a)) result.stdout.fnmatch_lines(["*collected 2 item*"]) @@ -1443,8 +1452,12 @@ def test_package_with_modules(pytester: Pytester) -> None: sub2_test = sub2.joinpath("test") sub2_test.mkdir(parents=True) - sub1_test.joinpath("test_in_sub1.py").write_text("def test_1(): pass") - sub2_test.joinpath("test_in_sub2.py").write_text("def test_2(): pass") + sub1_test.joinpath("test_in_sub1.py").write_text( + "def test_1(): pass", encoding="utf-8" + ) + sub2_test.joinpath("test_in_sub2.py").write_text( + "def test_2(): pass", encoding="utf-8" + ) # Execute from . result = pytester.runpytest("-v", "-s") @@ -1488,9 +1501,11 @@ def test_package_ordering(pytester: Pytester) -> None: sub2_test = sub2.joinpath("test") sub2_test.mkdir(parents=True) - root.joinpath("Test_root.py").write_text("def test_1(): pass") - sub1.joinpath("Test_sub1.py").write_text("def test_2(): pass") - sub2_test.joinpath("test_sub2.py").write_text("def test_3(): pass") + root.joinpath("Test_root.py").write_text("def test_1(): pass", encoding="utf-8") + sub1.joinpath("Test_sub1.py").write_text("def test_2(): pass", encoding="utf-8") + sub2_test.joinpath("test_sub2.py").write_text( + "def test_3(): pass", encoding="utf-8" + ) # Execute from . result = pytester.runpytest("-v", "-s") diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 473ae44d9..7119b3b5a 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -1392,14 +1392,14 @@ def test_sequence_comparison_uses_repr(pytester: Pytester) -> None: def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None: pytester.makepyfile(test_base=["def test_base(): assert 1 == 2"]) a = pytester.mkdir("a") - a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2") + a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2", encoding="utf-8") a.joinpath("conftest.py").write_text( - 'def pytest_assertrepr_compare(): return ["summary a"]' + 'def pytest_assertrepr_compare(): return ["summary a"]', encoding="utf-8" ) b = pytester.mkdir("b") - b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2") + b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2", encoding="utf-8") b.joinpath("conftest.py").write_text( - 'def pytest_assertrepr_compare(): return ["summary b"]' + 'def pytest_assertrepr_compare(): return ["summary b"]', encoding="utf-8" ) result = pytester.runpytest() diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 057b609ac..778f843e6 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1161,7 +1161,7 @@ class TestAssertionRewriteHookDetails: return False def rewrite_self(): - with open(__file__, 'w') as self: + with open(__file__, 'w', encoding='utf-8') as self: self.write('def reloaded(): return True') """, test_fun=""" diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 6f3cccbf1..e2e195ca7 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -38,7 +38,9 @@ class TestNewAPI: @pytest.mark.filterwarnings("ignore:could not create cache path") def test_cache_writefail_cachfile_silent(self, pytester: Pytester) -> None: pytester.makeini("[pytest]") - pytester.path.joinpath(".pytest_cache").write_text("gone wrong") + pytester.path.joinpath(".pytest_cache").write_text( + "gone wrong", encoding="utf-8" + ) config = pytester.parseconfigure() cache = config.cache assert cache is not None @@ -1134,7 +1136,9 @@ class TestNewFirst: ["*test_2/test_2.py::test_1 PASSED*", "*test_1/test_1.py::test_1 PASSED*"] ) - p1.write_text("def test_1(): assert 1\n" "def test_2(): assert 1\n") + p1.write_text( + "def test_1(): assert 1\n" "def test_2(): assert 1\n", encoding="utf-8" + ) os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9))) result = pytester.runpytest("--nf", "--collect-only", "-q") @@ -1207,7 +1211,8 @@ class TestNewFirst: p1.write_text( "import pytest\n" "@pytest.mark.parametrize('num', [1, 2, 3])\n" - "def test_1(num): assert num\n" + "def test_1(num): assert num\n", + encoding="utf-8", ) os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9))) @@ -1259,7 +1264,7 @@ def test_gitignore(pytester: Pytester) -> None: assert gitignore_path.read_text(encoding="UTF-8") == msg # Does not overwrite existing/custom one. - gitignore_path.write_text("custom") + gitignore_path.write_text("custom", encoding="utf-8") cache.set("something", "else") assert gitignore_path.read_text(encoding="UTF-8") == "custom" diff --git a/testing/test_capture.py b/testing/test_capture.py index 5d6ef64ef..b6ea81613 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -750,9 +750,10 @@ def test_setup_failure_does_not_kill_capturing(pytester: Pytester) -> None: def pytest_runtest_setup(item): raise ValueError(42) """ - ) + ), + encoding="utf-8", ) - sub1.joinpath("test_mod.py").write_text("def test_func1(): pass") + sub1.joinpath("test_mod.py").write_text("def test_func1(): pass", encoding="utf-8") result = pytester.runpytest(pytester.path, "--traceconfig") result.stdout.fnmatch_lines(["*ValueError(42)*", "*1 error*"]) @@ -1523,9 +1524,9 @@ def test_global_capture_with_live_logging(pytester: Pytester) -> None: def pytest_runtest_logreport(report): if "test_global" in report.nodeid: if report.when == "teardown": - with open("caplog", "w") as f: + with open("caplog", "w", encoding="utf-8") as f: f.write(report.caplog) - with open("capstdout", "w") as f: + with open("capstdout", "w", encoding="utf-8") as f: f.write(report.capstdout) """ ) @@ -1555,14 +1556,14 @@ def test_global_capture_with_live_logging(pytester: Pytester) -> None: result = pytester.runpytest_subprocess("--log-cli-level=INFO") assert result.ret == 0 - with open("caplog") as f: + with open("caplog", encoding="utf-8") as f: caplog = f.read() assert "fix setup" in caplog assert "something in test" in caplog assert "fix teardown" in caplog - with open("capstdout") as f: + with open("capstdout", encoding="utf-8") as f: capstdout = f.read() assert "fix setup" in capstdout diff --git a/testing/test_collection.py b/testing/test_collection.py index 3e1a04443..8b0a1ab36 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1273,7 +1273,8 @@ def test_initial_conftests_with_testpaths(pytester: Pytester) -> None: def pytest_sessionstart(session): raise Exception("pytest_sessionstart hook successfully run") """ - ) + ), + encoding="utf-8", ) pytester.makeini( """ @@ -1369,12 +1370,16 @@ def test_collect_symlink_dir(pytester: Pytester) -> None: def test_collectignore_via_conftest(pytester: Pytester) -> None: """collect_ignore in parent conftest skips importing child (issue #4592).""" tests = pytester.mkpydir("tests") - tests.joinpath("conftest.py").write_text("collect_ignore = ['ignore_me']") + tests.joinpath("conftest.py").write_text( + "collect_ignore = ['ignore_me']", encoding="utf-8" + ) ignore_me = tests.joinpath("ignore_me") ignore_me.mkdir() ignore_me.joinpath("__init__.py").touch() - ignore_me.joinpath("conftest.py").write_text("assert 0, 'should_not_be_called'") + ignore_me.joinpath("conftest.py").write_text( + "assert 0, 'should_not_be_called'", encoding="utf-8" + ) result = pytester.runpytest() assert result.ret == ExitCode.NO_TESTS_COLLECTED @@ -1383,9 +1388,9 @@ def test_collectignore_via_conftest(pytester: Pytester) -> None: def test_collect_pkg_init_and_file_in_args(pytester: Pytester) -> None: subdir = pytester.mkdir("sub") init = subdir.joinpath("__init__.py") - init.write_text("def test_init(): pass") + init.write_text("def test_init(): pass", encoding="utf-8") p = subdir.joinpath("test_file.py") - p.write_text("def test_file(): pass") + p.write_text("def test_file(): pass", encoding="utf-8") # NOTE: without "-o python_files=*.py" this collects test_file.py twice. # This changed/broke with "Add package scoped fixtures #2283" (2b1410895) @@ -1412,7 +1417,7 @@ def test_collect_pkg_init_and_file_in_args(pytester: Pytester) -> None: def test_collect_pkg_init_only(pytester: Pytester) -> None: subdir = pytester.mkdir("sub") init = subdir.joinpath("__init__.py") - init.write_text("def test_init(): pass") + init.write_text("def test_init(): pass", encoding="utf-8") result = pytester.runpytest(str(init)) result.stdout.fnmatch_lines(["*no tests ran in*"]) @@ -1427,7 +1432,7 @@ def test_collect_sub_with_symlinks(use_pkg: bool, pytester: Pytester) -> None: sub = pytester.mkdir("sub") if use_pkg: sub.joinpath("__init__.py").touch() - sub.joinpath("test_file.py").write_text("def test_file(): pass") + sub.joinpath("test_file.py").write_text("def test_file(): pass", encoding="utf-8") # Create a broken symlink. symlink_or_skip("test_doesnotexist.py", sub.joinpath("test_broken.py")) @@ -1465,7 +1470,7 @@ def test_collector_respects_tbstyle(pytester: Pytester) -> None: def test_does_not_eagerly_collect_packages(pytester: Pytester) -> None: pytester.makepyfile("def test(): pass") pydir = pytester.mkpydir("foopkg") - pydir.joinpath("__init__.py").write_text("assert False") + pydir.joinpath("__init__.py").write_text("assert False", encoding="utf-8") result = pytester.runpytest() assert result.ret == ExitCode.OK diff --git a/testing/test_conftest.py b/testing/test_conftest.py index 7ec9feb8b..427831507 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -638,7 +638,7 @@ def test_search_conftest_up_to_inifile( root = pytester.path src = root.joinpath("src") src.mkdir() - src.joinpath("pytest.ini").write_text("[pytest]") + src.joinpath("pytest.ini").write_text("[pytest]", encoding="utf-8") src.joinpath("conftest.py").write_text( textwrap.dedent( """\ diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 90804c619..690830329 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -28,7 +28,7 @@ from _pytest.stash import Stash def schema() -> xmlschema.XMLSchema: """Return an xmlschema.XMLSchema object for the junit-10.xsd file.""" fn = Path(__file__).parent / "example_scripts/junit-10.xsd" - with fn.open() as f: + with fn.open(encoding="utf-8") as f: return xmlschema.XMLSchema(f) @@ -45,7 +45,7 @@ class RunAndParse: xml_path = self.pytester.path.joinpath("junit.xml") result = self.pytester.runpytest("--junitxml=%s" % xml_path, *args) if family == "xunit2": - with xml_path.open() as f: + with xml_path.open(encoding="utf-8") as f: self.schema.validate(f) xmldoc = minidom.parse(str(xml_path)) return result, DomNode(xmldoc) @@ -469,7 +469,7 @@ class TestPython: self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: p = pytester.mkdir("sub").joinpath("test_hello.py") - p.write_text("def test_func(): 0/0") + p.write_text("def test_func(): 0/0", encoding="utf-8") result, dom = run_and_parse(family=xunit_family) assert result.ret node = dom.find_first_by_tag("testsuite") @@ -987,7 +987,7 @@ class TestNonPython: return "custom item runtest failed" """ ) - pytester.path.joinpath("myfile.xyz").write_text("hello") + pytester.path.joinpath("myfile.xyz").write_text("hello", encoding="utf-8") result, dom = run_and_parse(family=xunit_family) assert result.ret node = dom.find_first_by_tag("testsuite") @@ -1013,7 +1013,7 @@ def test_nullbyte(pytester: Pytester, junit_logging: str) -> None: ) xmlf = pytester.path.joinpath("junit.xml") pytester.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging) - text = xmlf.read_text() + text = xmlf.read_text(encoding="utf-8") assert "\x00" not in text if junit_logging == "system-out": assert "#x00" in text @@ -1035,7 +1035,7 @@ def test_nullbyte_replace(pytester: Pytester, junit_logging: str) -> None: ) xmlf = pytester.path.joinpath("junit.xml") pytester.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging) - text = xmlf.read_text() + text = xmlf.read_text(encoding="utf-8") if junit_logging == "system-out": assert "#x0" in text if junit_logging == "no": diff --git a/testing/test_link_resolve.py b/testing/test_link_resolve.py index 60a86ada3..1ac3afd09 100644 --- a/testing/test_link_resolve.py +++ b/testing/test_link_resolve.py @@ -59,7 +59,8 @@ def test_link_resolve(pytester: Pytester) -> None: def test_foo(): raise AssertionError() """ - ) + ), + encoding="utf-8", ) subst = subst_path_linux diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index 8a9dd600b..8175b5f0f 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -461,5 +461,5 @@ def test_syspath_prepend_with_namespace_packages( # Should invalidate caches via importlib.invalidate_caches. modules_tmpdir = pytester.mkdir("modules_tmpdir") monkeypatch.syspath_prepend(str(modules_tmpdir)) - modules_tmpdir.joinpath("main_app.py").write_text("app = True") + modules_tmpdir.joinpath("main_app.py").write_text("app = True", encoding="utf-8") from main_app import app # noqa: F401 diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index c051ec338..1899abe15 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -311,7 +311,7 @@ def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None: script = str(pytester.path.joinpath("test_argcomplete")) - with open(str(script), "w") as fp: + with open(str(script), "w", encoding="utf-8") as fp: # redirect output from argcomplete to stdin and stderr is not trivial # http://stackoverflow.com/q/12589419/1307905 # so we use bash diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index 0fd372b51..56c54e484 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -120,9 +120,9 @@ class TestImportPath: otherdir.joinpath("__init__.py").touch() module_a = otherdir / "a.py" - module_a.write_text("from .b import stuff as result\n") + module_a.write_text("from .b import stuff as result\n", encoding="utf-8") module_b = otherdir / "b.py" - module_b.write_text('stuff="got it"\n') + module_b.write_text('stuff="got it"\n', encoding="utf-8") module_c = otherdir / "c.py" module_c.write_text( dedent( @@ -131,7 +131,8 @@ class TestImportPath: import otherdir.a value = otherdir.a.result """ - ) + ), + encoding="utf-8", ) module_d = otherdir / "d.py" module_d.write_text( @@ -141,7 +142,8 @@ class TestImportPath: from otherdir import a value2 = a.result """ - ) + ), + encoding="utf-8", ) def test_smoke_test(self, path1: Path) -> None: @@ -283,7 +285,7 @@ class TestImportPath: def simple_module(self, tmp_path: Path) -> Path: fn = tmp_path / "_src/tests/mymod.py" fn.parent.mkdir(parents=True) - fn.write_text("def foo(x): return 40 + x") + fn.write_text("def foo(x): return 40 + x", encoding="utf-8") return fn def test_importmode_importlib(self, simple_module: Path, tmp_path: Path) -> None: @@ -447,7 +449,7 @@ def test_samefile_false_negatives(tmp_path: Path, monkeypatch: MonkeyPatch) -> N return False, even when they are clearly equal. """ module_path = tmp_path.joinpath("my_module.py") - module_path.write_text("def foo(): return 42") + module_path.write_text("def foo(): return 42", encoding="utf-8") monkeypatch.syspath_prepend(tmp_path) with monkeypatch.context() as mp: @@ -473,7 +475,8 @@ class TestImportLibMode: class Data: value: str """ - ) + ), + encoding="utf-8", ) module = import_path(fn, mode="importlib", root=tmp_path) @@ -498,7 +501,8 @@ class TestImportLibMode: s = pickle.dumps(_action) return pickle.loads(s) """ - ) + ), + encoding="utf-8", ) module = import_path(fn, mode="importlib", root=tmp_path) @@ -525,7 +529,8 @@ class TestImportLibMode: class Data: x: int = 42 """ - ) + ), + encoding="utf-8", ) fn2 = tmp_path.joinpath("_src/m2/tests/test.py") @@ -540,7 +545,8 @@ class TestImportLibMode: class Data: x: str = "" """ - ) + ), + encoding="utf-8", ) import pickle diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 9fe23d177..c6f518b1d 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -347,7 +347,7 @@ class TestPytestPluginManager: pytest.raises(ImportError, pytestpm.import_plugin, "pytest_qweqwex.y") pytester.syspathinsert() - pytester.mkpydir("pkg").joinpath("plug.py").write_text("x=3") + pytester.mkpydir("pkg").joinpath("plug.py").write_text("x=3", encoding="utf-8") pluginname = "pkg.plug" pytestpm.import_plugin(pluginname) mod = pytestpm.get_plugin("pkg.plug") diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 62dad9858..8f8b4d291 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -222,7 +222,7 @@ class TestInlineRunModulesCleanup: result = pytester.inline_run(str(test_mod)) assert result.ret == ExitCode.OK # rewrite module, now test should fail if module was re-imported - test_mod.write_text("def test_foo(): assert False") + test_mod.write_text("def test_foo(): assert False", encoding="utf-8") result2 = pytester.inline_run(str(test_mod)) assert result2.ret == ExitCode.TESTS_FAILED diff --git a/testing/test_reports.py b/testing/test_reports.py index e101b51da..387d2e807 100644 --- a/testing/test_reports.py +++ b/testing/test_reports.py @@ -410,7 +410,7 @@ class TestReportSerialization: ) -> None: sub_dir = pytester.path.joinpath("ns") sub_dir.mkdir() - sub_dir.joinpath("conftest.py").write_text("import unknown") + sub_dir.joinpath("conftest.py").write_text("import unknown", encoding="utf-8") result = pytester.runpytest_subprocess(".") result.stdout.fnmatch_lines(["E *Error: No module named 'unknown'"]) diff --git a/testing/test_session.py b/testing/test_session.py index f73dc89ef..48dc08e8c 100644 --- a/testing/test_session.py +++ b/testing/test_session.py @@ -265,9 +265,9 @@ def test_plugin_already_exists(pytester: Pytester) -> None: def test_exclude(pytester: Pytester) -> None: hellodir = pytester.mkdir("hello") - hellodir.joinpath("test_hello.py").write_text("x y syntaxerror") + hellodir.joinpath("test_hello.py").write_text("x y syntaxerror", encoding="utf-8") hello2dir = pytester.mkdir("hello2") - hello2dir.joinpath("test_hello2.py").write_text("x y syntaxerror") + hello2dir.joinpath("test_hello2.py").write_text("x y syntaxerror", encoding="utf-8") pytester.makepyfile(test_ok="def test_pass(): pass") result = pytester.runpytest("--ignore=hello", "--ignore=hello2") assert result.ret == 0 @@ -276,13 +276,13 @@ def test_exclude(pytester: Pytester) -> None: def test_exclude_glob(pytester: Pytester) -> None: hellodir = pytester.mkdir("hello") - hellodir.joinpath("test_hello.py").write_text("x y syntaxerror") + hellodir.joinpath("test_hello.py").write_text("x y syntaxerror", encoding="utf-8") hello2dir = pytester.mkdir("hello2") - hello2dir.joinpath("test_hello2.py").write_text("x y syntaxerror") + hello2dir.joinpath("test_hello2.py").write_text("x y syntaxerror", encoding="utf-8") hello3dir = pytester.mkdir("hallo3") - hello3dir.joinpath("test_hello3.py").write_text("x y syntaxerror") + hello3dir.joinpath("test_hello3.py").write_text("x y syntaxerror", encoding="utf-8") subdir = pytester.mkdir("sub") - subdir.joinpath("test_hello4.py").write_text("x y syntaxerror") + subdir.joinpath("test_hello4.py").write_text("x y syntaxerror", encoding="utf-8") pytester.makepyfile(test_ok="def test_pass(): pass") result = pytester.runpytest("--ignore-glob=*h[ea]llo*") assert result.ret == 0 diff --git a/testing/test_skipping.py b/testing/test_skipping.py index 892ed8547..6b8034610 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -195,7 +195,8 @@ class TestEvaluation: def pytest_markeval_namespace(): return {"arg": "root"} """ - ) + ), + encoding="utf-8", ) root.joinpath("test_root.py").write_text( textwrap.dedent( @@ -206,7 +207,8 @@ class TestEvaluation: def test_root(): assert False """ - ) + ), + encoding="utf-8", ) foo = root.joinpath("foo") foo.mkdir() @@ -219,7 +221,8 @@ class TestEvaluation: def pytest_markeval_namespace(): return {"arg": "foo"} """ - ) + ), + encoding="utf-8", ) foo.joinpath("test_foo.py").write_text( textwrap.dedent( @@ -230,7 +233,8 @@ class TestEvaluation: def test_foo(): assert False """ - ) + ), + encoding="utf-8", ) bar = root.joinpath("bar") bar.mkdir() @@ -243,7 +247,8 @@ class TestEvaluation: def pytest_markeval_namespace(): return {"arg": "bar"} """ - ) + ), + encoding="utf-8", ) bar.joinpath("test_bar.py").write_text( textwrap.dedent( @@ -254,7 +259,8 @@ class TestEvaluation: def test_bar(): assert False """ - ) + ), + encoding="utf-8", ) reprec = pytester.inline_run("-vs", "--capture=no") @@ -629,7 +635,8 @@ class TestXFail: @pytest.mark.xfail(reason='unsupported feature', strict=%s) def test_foo(): - with open('foo_executed', 'w'): pass # make sure test executes + with open('foo_executed', 'w', encoding='utf-8'): + pass # make sure test executes """ % strict ) diff --git a/testing/test_stepwise.py b/testing/test_stepwise.py index 2094abc4e..85e38c7d5 100644 --- a/testing/test_stepwise.py +++ b/testing/test_stepwise.py @@ -352,6 +352,6 @@ def test_one(): assert result.ret == 0 assert Path(stepwise_cache_file).exists() - with stepwise_cache_file.open() as file_handle: + with stepwise_cache_file.open(encoding="utf-8") as file_handle: observed_value = file_handle.readlines() assert [expected_value] == observed_value diff --git a/testing/test_terminal.py b/testing/test_terminal.py index c0acb6006..7c2f7c94a 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -244,7 +244,8 @@ class TestTerminal: def test_method(self): pass """ - ) + ), + encoding="utf-8", ) result = pytester.runpytest("-vv") assert result.ret == 0 @@ -1567,7 +1568,8 @@ class TestGenericReporting: """ def pytest_report_header(config, start_path): return ["line1", str(start_path)] -""" +""", + encoding="utf-8", ) result = pytester.runpytest("a") result.stdout.fnmatch_lines(["*hello: 42*", "line1", str(pytester.path)]) @@ -1671,7 +1673,7 @@ def test_fdopen_kept_alive_issue124(pytester: Pytester) -> None: import os, sys k = [] def test_open_file_and_keep_alive(capfd): - stdout = os.fdopen(1, 'w', 1) + stdout = os.fdopen(1, 'w', buffering=1, encoding='utf-8') k.append(stdout) def test_close_kept_alive_file(): diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 110a68b27..1e1446af1 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -561,7 +561,7 @@ def test_basetemp_with_read_only_files(pytester: Pytester) -> None: def test(tmp_path): fn = tmp_path / 'foo.txt' - fn.write_text('hello') + fn.write_text('hello', encoding='utf-8') mode = os.stat(str(fn)).st_mode os.chmod(str(fn), mode & ~stat.S_IREAD) """ diff --git a/testing/test_warnings.py b/testing/test_warnings.py index a1ecba247..03846cb30 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -810,12 +810,12 @@ def test_resource_warning(pytester: Pytester, monkeypatch: pytest.MonkeyPatch) - pytester.makepyfile( """ def open_file(p): - f = p.open("r") + f = p.open("r", encoding="utf-8") assert p.read_text() == "hello" def test_resource_warning(tmp_path): p = tmp_path.joinpath("foo.txt") - p.write_text("hello") + p.write_text("hello", encoding="utf-8") open_file(p) """ ) From f6b995e9d59a0b7e1e52fb64185e9e1d1dae367d Mon Sep 17 00:00:00 2001 From: Zac Hatfield-Dodds Date: Tue, 20 Jun 2023 04:55:40 -0700 Subject: [PATCH 50/62] Use utf-8 debug file --- changelog/7781.bugfix.rst | 1 + src/_pytest/helpconfig.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog/7781.bugfix.rst diff --git a/changelog/7781.bugfix.rst b/changelog/7781.bugfix.rst new file mode 100644 index 000000000..191fcd4da --- /dev/null +++ b/changelog/7781.bugfix.rst @@ -0,0 +1 @@ +Fix writing non-encodable text to log file when using ``--debug``. diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py index 6b6718a70..430870608 100644 --- a/src/_pytest/helpconfig.py +++ b/src/_pytest/helpconfig.py @@ -105,7 +105,7 @@ def pytest_cmdline_parse(): if config.option.debug: # --debug | --debug was provided. path = config.option.debug - debugfile = open(path, "w") + debugfile = open(path, "w", encoding="utf-8") debugfile.write( "versions pytest-%s, " "python-%s\ncwd=%s\nargs=%s\n\n" From d97d44a97af2303eb3f3aea1f16fd834f5415509 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 20 Jun 2023 20:52:59 +0300 Subject: [PATCH 51/62] config: extract initial paths/nodeids args logic to a function Will be reused in the next commit. --- src/_pytest/config/__init__.py | 76 +++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 85d8830e7..686be1277 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1223,6 +1223,49 @@ class Config: return args + def _decide_args( + self, + *, + args: List[str], + pyargs: List[str], + testpaths: List[str], + invocation_dir: Path, + rootpath: Path, + warn: bool, + ) -> Tuple[List[str], ArgsSource]: + """Decide the args (initial paths/nodeids) to use given the relevant inputs. + + :param warn: Whether can issue warnings. + """ + if args: + source = Config.ArgsSource.ARGS + result = args + else: + if invocation_dir == rootpath: + source = Config.ArgsSource.TESTPATHS + if pyargs: + result = testpaths + else: + result = [] + for path in testpaths: + result.extend(sorted(glob.iglob(path, recursive=True))) + if testpaths and not result: + if warn: + warning_text = ( + "No files were found in testpaths; " + "consider removing or adjusting your testpaths configuration. " + "Searching recursively from the current directory instead." + ) + self.issue_config_time_warning( + PytestConfigWarning(warning_text), stacklevel=3 + ) + else: + result = [] + if not result: + source = Config.ArgsSource.INCOVATION_DIR + result = [str(invocation_dir)] + return result, source + def _preparse(self, args: List[str], addopts: bool = True) -> None: if addopts: env_addopts = os.environ.get("PYTEST_ADDOPTS", "") @@ -1371,34 +1414,17 @@ class Config: self.hook.pytest_cmdline_preparse(config=self, args=args) self._parser.after_preparse = True # type: ignore try: - source = Config.ArgsSource.ARGS args = self._parser.parse_setoption( args, self.option, namespace=self.option ) - if not args: - if self.invocation_params.dir == self.rootpath: - source = Config.ArgsSource.TESTPATHS - testpaths: List[str] = self.getini("testpaths") - if self.known_args_namespace.pyargs: - args = testpaths - else: - args = [] - for path in testpaths: - args.extend(sorted(glob.iglob(path, recursive=True))) - if testpaths and not args: - warning_text = ( - "No files were found in testpaths; " - "consider removing or adjusting your testpaths configuration. " - "Searching recursively from the current directory instead." - ) - self.issue_config_time_warning( - PytestConfigWarning(warning_text), stacklevel=3 - ) - if not args: - source = Config.ArgsSource.INCOVATION_DIR - args = [str(self.invocation_params.dir)] - self.args = args - self.args_source = source + self.args, self.args_source = self._decide_args( + args=args, + pyargs=self.known_args_namespace.pyargs, + testpaths=self.getini("testpaths"), + invocation_dir=self.invocation_params.dir, + rootpath=self.rootpath, + warn=True, + ) except PrintHelp: pass From 14890329dcdc37085a8ac9c53ce332df9faead30 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 20 Jun 2023 20:59:22 +0300 Subject: [PATCH 52/62] config: fix the paths considered for initial conftest discovery Fixes #11104. See the issue for a description of the problem. Now, we use the same logic for initial conftest paths as we do for deciding the initial args, which was the idea behind checking `namespace.file_or_dir` and `testpaths` previously. This fixes the issue of `testpaths` being considered for initial conftests even when it's not used for the args. (Another issue in faeb16146b811488ebbcbd17ef6f9102314065b2 was that the `testpaths` were not glob-expanded, this is also fixed.) --- changelog/11104.bugfix.rst | 3 +++ src/_pytest/config/__init__.py | 47 ++++++++++++++++++++++------------ testing/test_collection.py | 7 +++++ testing/test_conftest.py | 25 +++++++++--------- 4 files changed, 53 insertions(+), 29 deletions(-) create mode 100644 changelog/11104.bugfix.rst diff --git a/changelog/11104.bugfix.rst b/changelog/11104.bugfix.rst new file mode 100644 index 000000000..10f0db925 --- /dev/null +++ b/changelog/11104.bugfix.rst @@ -0,0 +1,3 @@ +Fixed a regression in pytest 7.3.2 which caused to :confval:`testpaths` to be considered for loading initial conftests, +even when it was not utilized (e.g. when explicit paths were given on the command line). +Now the ``testpaths`` are only considered when they are in use. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 686be1277..c9a4b7f63 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -527,9 +527,12 @@ class PytestPluginManager(PluginManager): # def _set_initial_conftests( self, - namespace: argparse.Namespace, + args: Sequence[Union[str, Path]], + pyargs: bool, + noconftest: bool, rootpath: Path, - testpaths_ini: Sequence[str], + confcutdir: Optional[Path], + importmode: Union[ImportMode, str], ) -> None: """Load initial conftest files given a preparsed "namespace". @@ -539,17 +542,12 @@ class PytestPluginManager(PluginManager): common options will not confuse our logic here. """ current = Path.cwd() - self._confcutdir = ( - absolutepath(current / namespace.confcutdir) - if namespace.confcutdir - else None - ) - self._noconftest = namespace.noconftest - self._using_pyargs = namespace.pyargs - testpaths = namespace.file_or_dir + testpaths_ini + self._confcutdir = absolutepath(current / confcutdir) if confcutdir else None + self._noconftest = noconftest + self._using_pyargs = pyargs foundanchor = False - for testpath in testpaths: - path = str(testpath) + for intitial_path in args: + path = str(intitial_path) # remove node-id syntax i = path.find("::") if i != -1: @@ -563,10 +561,10 @@ class PytestPluginManager(PluginManager): except OSError: # pragma: no cover anchor_exists = False if anchor_exists: - self._try_load_conftest(anchor, namespace.importmode, rootpath) + self._try_load_conftest(anchor, importmode, rootpath) foundanchor = True if not foundanchor: - self._try_load_conftest(current, namespace.importmode, rootpath) + self._try_load_conftest(current, importmode, rootpath) def _is_in_confcutdir(self, path: Path) -> bool: """Whether a path is within the confcutdir. @@ -1140,10 +1138,25 @@ class Config: @hookimpl(trylast=True) def pytest_load_initial_conftests(self, early_config: "Config") -> None: - self.pluginmanager._set_initial_conftests( - early_config.known_args_namespace, + # We haven't fully parsed the command line arguments yet, so + # early_config.args it not set yet. But we need it for + # discovering the initial conftests. So "pre-run" the logic here. + # It will be done for real in `parse()`. + args, args_source = early_config._decide_args( + args=early_config.known_args_namespace.file_or_dir, + pyargs=early_config.known_args_namespace.pyargs, + testpaths=early_config.getini("testpaths"), + invocation_dir=early_config.invocation_params.dir, rootpath=early_config.rootpath, - testpaths_ini=self.getini("testpaths"), + warn=False, + ) + self.pluginmanager._set_initial_conftests( + args=args, + pyargs=early_config.known_args_namespace.pyargs, + noconftest=early_config.known_args_namespace.noconftest, + rootpath=early_config.rootpath, + confcutdir=early_config.known_args_namespace.confcutdir, + importmode=early_config.known_args_namespace.importmode, ) def _initini(self, args: Sequence[str]) -> None: diff --git a/testing/test_collection.py b/testing/test_collection.py index bbcb358b6..302139872 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1264,11 +1264,18 @@ def test_initial_conftests_with_testpaths(pytester: Pytester) -> None: testpaths = some_path """ ) + + # No command line args - falls back to testpaths. result = pytester.runpytest() + assert result.ret == ExitCode.INTERNAL_ERROR result.stdout.fnmatch_lines( "INTERNALERROR* Exception: pytest_sessionstart hook successfully run" ) + # No fallback. + result = pytester.runpytest(".") + assert result.ret == ExitCode.NO_TESTS_COLLECTED + def test_large_option_breaks_initial_conftests(pytester: Pytester) -> None: """Long option values do not break initial conftests handling (#10169).""" diff --git a/testing/test_conftest.py b/testing/test_conftest.py index c64bd11d4..f857cde04 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -1,4 +1,3 @@ -import argparse import os import textwrap from pathlib import Path @@ -7,6 +6,8 @@ from typing import Dict from typing import Generator from typing import List from typing import Optional +from typing import Sequence +from typing import Union import pytest from _pytest.config import ExitCode @@ -24,18 +25,18 @@ def ConftestWithSetinitial(path) -> PytestPluginManager: def conftest_setinitial( - conftest: PytestPluginManager, args, confcutdir: Optional["os.PathLike[str]"] = None + conftest: PytestPluginManager, + args: Sequence[Union[str, Path]], + confcutdir: Optional[Path] = None, ) -> None: - class Namespace: - def __init__(self) -> None: - self.file_or_dir = args - self.confcutdir = os.fspath(confcutdir) if confcutdir is not None else None - self.noconftest = False - self.pyargs = False - self.importmode = "prepend" - - namespace = cast(argparse.Namespace, Namespace()) - conftest._set_initial_conftests(namespace, rootpath=Path(args[0]), testpaths_ini=[]) + conftest._set_initial_conftests( + args=args, + pyargs=False, + noconftest=False, + rootpath=Path(args[0]), + confcutdir=confcutdir, + importmode="prepend", + ) @pytest.mark.usefixtures("_sys_snapshot") From 1e8135df16fff350d0097a41b739745a9e14edec Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Thu, 22 Jun 2023 15:45:20 +0300 Subject: [PATCH 53/62] reference: add note that `pythonpath` does not affect `-p` Fix #11118. --- doc/en/reference/reference.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 7107218b3..c6629db84 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -1703,6 +1703,11 @@ passed multiple times. The expected format is ``name=value``. For example:: [pytest] pythonpath = src1 src2 + .. note:: + + ``pythonpath`` does not affect some imports that happen very early, + most notably plugins loaded using the ``-p`` command line option. + .. confval:: required_plugins From a14745550a07057f6dab798fccfb26403262253a Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 23 Jun 2023 14:18:43 +0300 Subject: [PATCH 54/62] Merge pull request #11131 from pytest-dev/release-7.4.0 Prepare release 7.4.0 (cherry picked from commit fb03d1388b002f63973c663cb4929908a2ca81f5) --- changelog/10831.bugfix.rst | 1 - changelog/10872.improvement.rst | 1 - changelog/10901.feature.rst | 2 - changelog/10907.improvement.rst | 5 -- changelog/10940.improvement.rst | 3 - changelog/10991.improvement.rst | 1 - changelog/11005.improvement.rst | 1 - changelog/11013.improvement.rst | 1 - changelog/11031.trivial.rst | 1 - changelog/11043.improvement.rst | 3 - changelog/11068.bugfix.rst | 1 - changelog/11081.improvement.rst | 7 --- changelog/11104.bugfix.rst | 3 - changelog/1904.bugfix.rst | 1 - changelog/7781.bugfix.rst | 1 - changelog/8711.improvement.rst | 3 - changelog/9146.doc.rst | 1 - doc/en/announce/index.rst | 1 + doc/en/announce/release-7.4.0.rst | 49 ++++++++++++++++ doc/en/builtin.rst | 6 +- doc/en/changelog.rst | 92 +++++++++++++++++++++++++++++++ doc/en/getting-started.rst | 2 +- doc/en/reference/reference.rst | 4 +- 23 files changed, 148 insertions(+), 42 deletions(-) delete mode 100644 changelog/10831.bugfix.rst delete mode 100644 changelog/10872.improvement.rst delete mode 100644 changelog/10901.feature.rst delete mode 100644 changelog/10907.improvement.rst delete mode 100644 changelog/10940.improvement.rst delete mode 100644 changelog/10991.improvement.rst delete mode 100644 changelog/11005.improvement.rst delete mode 100644 changelog/11013.improvement.rst delete mode 100644 changelog/11031.trivial.rst delete mode 100644 changelog/11043.improvement.rst delete mode 100644 changelog/11068.bugfix.rst delete mode 100644 changelog/11081.improvement.rst delete mode 100644 changelog/11104.bugfix.rst delete mode 100644 changelog/1904.bugfix.rst delete mode 100644 changelog/7781.bugfix.rst delete mode 100644 changelog/8711.improvement.rst delete mode 100644 changelog/9146.doc.rst create mode 100644 doc/en/announce/release-7.4.0.rst diff --git a/changelog/10831.bugfix.rst b/changelog/10831.bugfix.rst deleted file mode 100644 index ea641dee6..000000000 --- a/changelog/10831.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Terminal Reporting: Fixed bug when running in ``--tb=line`` mode where ``pytest.fail(pytrace=False)`` tests report ``None``. diff --git a/changelog/10872.improvement.rst b/changelog/10872.improvement.rst deleted file mode 100644 index fe0c01a02..000000000 --- a/changelog/10872.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -Update test log report annotation to named tuple and fixed inconsistency in docs for :hook:`pytest_report_teststatus` hook. diff --git a/changelog/10901.feature.rst b/changelog/10901.feature.rst deleted file mode 100644 index 0d99d66f6..000000000 --- a/changelog/10901.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added :func:`ExceptionInfo.from_exception() `, a simpler way to create an :class:`~pytest.ExceptionInfo` from an exception. -This can replace :func:`ExceptionInfo.from_exc_info() ` for most uses. diff --git a/changelog/10907.improvement.rst b/changelog/10907.improvement.rst deleted file mode 100644 index 7f011a827..000000000 --- a/changelog/10907.improvement.rst +++ /dev/null @@ -1,5 +0,0 @@ -When an exception traceback to be displayed is completely filtered out (by mechanisms such as ``__tracebackhide__``, internal frames, and similar), now only the exception string and the following message are shown: - -"All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames.". - -Previously, the last frame of the traceback was shown, even though it was hidden. diff --git a/changelog/10940.improvement.rst b/changelog/10940.improvement.rst deleted file mode 100644 index f3e33cbc6..000000000 --- a/changelog/10940.improvement.rst +++ /dev/null @@ -1,3 +0,0 @@ -Improved verbose output (``-vv``) of ``skip`` and ``xfail`` reasons by performing text wrapping while leaving a clear margin for progress output. - -Added :func:`TerminalReporter.wrap_write() ` as a helper for that. diff --git a/changelog/10991.improvement.rst b/changelog/10991.improvement.rst deleted file mode 100644 index 768c08e55..000000000 --- a/changelog/10991.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -Added handling of ``%f`` directive to print microseconds in log format options, such as ``log-date-format``. diff --git a/changelog/11005.improvement.rst b/changelog/11005.improvement.rst deleted file mode 100644 index 295252514..000000000 --- a/changelog/11005.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -Added underlying exception to cache provider path creation and write warning messages. diff --git a/changelog/11013.improvement.rst b/changelog/11013.improvement.rst deleted file mode 100644 index fe3ece93c..000000000 --- a/changelog/11013.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -Added warning when :confval:`testpaths` is set, but paths are not found by glob. In this case, pytest will fall back to searching from the current directory. diff --git a/changelog/11031.trivial.rst b/changelog/11031.trivial.rst deleted file mode 100644 index fad2cf24d..000000000 --- a/changelog/11031.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Enhanced the CLI flag for ``-c`` to now include ``--config-file`` to make it clear that this flag applies to the usage of a custom config file. diff --git a/changelog/11043.improvement.rst b/changelog/11043.improvement.rst deleted file mode 100644 index 1fe0361d7..000000000 --- a/changelog/11043.improvement.rst +++ /dev/null @@ -1,3 +0,0 @@ -When `--confcutdir` is not specified, and there is no config file present, the conftest cutoff directory (`--confcutdir`) is now set to the :ref:`rootdir`. -Previously in such cases, `conftest.py` files would be probed all the way to the root directory of the filesystem. -If you are badly affected by this change, consider adding an empty config file to your desired cutoff directory, or explicitly set `--confcutdir`. diff --git a/changelog/11068.bugfix.rst b/changelog/11068.bugfix.rst deleted file mode 100644 index 45cdb105f..000000000 --- a/changelog/11068.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed the ``--last-failed`` whole-file skipping functionality ("skipped N files") for :ref:`non-python test files `. diff --git a/changelog/11081.improvement.rst b/changelog/11081.improvement.rst deleted file mode 100644 index ccfaf6016..000000000 --- a/changelog/11081.improvement.rst +++ /dev/null @@ -1,7 +0,0 @@ -The :confval:`norecursedir` check is now performed in a :hook:`pytest_ignore_collect` implementation, so plugins can affect it. - -If after updating to this version you see that your `norecursedir` setting is not being respected, -it means that a conftest or a plugin you use has a bad `pytest_ignore_collect` implementation. -Most likely, your hook returns `False` for paths it does not want to ignore, -which ends the processing and doesn't allow other plugins, including pytest itself, to ignore the path. -The fix is to return `None` instead of `False` for paths your hook doesn't want to ignore. diff --git a/changelog/11104.bugfix.rst b/changelog/11104.bugfix.rst deleted file mode 100644 index 10f0db925..000000000 --- a/changelog/11104.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixed a regression in pytest 7.3.2 which caused to :confval:`testpaths` to be considered for loading initial conftests, -even when it was not utilized (e.g. when explicit paths were given on the command line). -Now the ``testpaths`` are only considered when they are in use. diff --git a/changelog/1904.bugfix.rst b/changelog/1904.bugfix.rst deleted file mode 100644 index 3e1a29215..000000000 --- a/changelog/1904.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed traceback entries hidden with ``__tracebackhide__ = True`` still being shown for chained exceptions (parts after "... the above exception ..." message). diff --git a/changelog/7781.bugfix.rst b/changelog/7781.bugfix.rst deleted file mode 100644 index 191fcd4da..000000000 --- a/changelog/7781.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix writing non-encodable text to log file when using ``--debug``. diff --git a/changelog/8711.improvement.rst b/changelog/8711.improvement.rst deleted file mode 100644 index 20805e9f9..000000000 --- a/changelog/8711.improvement.rst +++ /dev/null @@ -1,3 +0,0 @@ -:func:`_pytest.logging.LogCaptureFixture.set_level` and :func:`_pytest.logging.LogCaptureFixture.at_level` -will temporarily enable the requested ``level`` if ``level`` was disabled globally via -``logging.disable(LEVEL)``. diff --git a/changelog/9146.doc.rst b/changelog/9146.doc.rst deleted file mode 100644 index 95189b96d..000000000 --- a/changelog/9146.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Improve Documentation for `caplog.set_level`. diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index bcc0669a6..914e763bd 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-7.4.0 release-7.3.2 release-7.3.1 release-7.3.0 diff --git a/doc/en/announce/release-7.4.0.rst b/doc/en/announce/release-7.4.0.rst new file mode 100644 index 000000000..5a0d18267 --- /dev/null +++ b/doc/en/announce/release-7.4.0.rst @@ -0,0 +1,49 @@ +pytest-7.4.0 +======================================= + +The pytest team is proud to announce the 7.4.0 release! + +This release contains new features, improvements, and bug fixes, +the full list of changes is available in the changelog: + + https://docs.pytest.org/en/stable/changelog.html + +For complete documentation, please visit: + + https://docs.pytest.org/en/stable/ + +As usual, you can upgrade from PyPI via: + + pip install -U pytest + +Thanks to all of the contributors to this release: + +* Adam J. Stewart +* Alessio Izzo +* Alex +* Alex Lambson +* Brian Larsen +* Bruno Oliveira +* Bryan Ricker +* Chris Mahoney +* Facundo Batista +* Florian Bruhin +* Jarrett Keifer +* Kenny Y +* Miro Hrončok +* Ran Benita +* Roberto Aldera +* Ronny Pfannschmidt +* Sergey Kim +* Stefanie Molin +* Vijay Arora +* Ville Skyttä +* Zac Hatfield-Dodds +* bzoracler +* leeyueh +* nondescryptid +* theirix + + +Happy testing, +The pytest Development Team diff --git a/doc/en/builtin.rst b/doc/en/builtin.rst index 7ae185f6c..53305eecd 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:510 + cache -- .../_pytest/cacheprovider.py:528 Return a cache object that can persist state between testing sessions. cache.get(key, default) @@ -119,7 +119,7 @@ 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:1360 + pytestconfig [session scope] -- .../_pytest/fixtures.py:1353 Session-scoped fixture that returns the session's :class:`pytest.Config` object. @@ -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:498 + caplog -- .../_pytest/logging.py:570 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 cdc9e66f6..391721df3 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -28,6 +28,98 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 7.4.0 (2023-06-23) +========================= + +Features +-------- + +- `#10901 `_: Added :func:`ExceptionInfo.from_exception() `, a simpler way to create an :class:`~pytest.ExceptionInfo` from an exception. + This can replace :func:`ExceptionInfo.from_exc_info() ` for most uses. + + + +Improvements +------------ + +- `#10872 `_: Update test log report annotation to named tuple and fixed inconsistency in docs for :hook:`pytest_report_teststatus` hook. + + +- `#10907 `_: When an exception traceback to be displayed is completely filtered out (by mechanisms such as ``__tracebackhide__``, internal frames, and similar), now only the exception string and the following message are shown: + + "All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames.". + + Previously, the last frame of the traceback was shown, even though it was hidden. + + +- `#10940 `_: Improved verbose output (``-vv``) of ``skip`` and ``xfail`` reasons by performing text wrapping while leaving a clear margin for progress output. + + Added ``TerminalReporter.wrap_write()`` as a helper for that. + + +- `#10991 `_: Added handling of ``%f`` directive to print microseconds in log format options, such as ``log-date-format``. + + +- `#11005 `_: Added the underlying exception to the cache provider's path creation and write warning messages. + + +- `#11013 `_: Added warning when :confval:`testpaths` is set, but paths are not found by glob. In this case, pytest will fall back to searching from the current directory. + + +- `#11043 `_: When `--confcutdir` is not specified, and there is no config file present, the conftest cutoff directory (`--confcutdir`) is now set to the :ref:`rootdir `. + Previously in such cases, `conftest.py` files would be probed all the way to the root directory of the filesystem. + If you are badly affected by this change, consider adding an empty config file to your desired cutoff directory, or explicitly set `--confcutdir`. + + +- `#11081 `_: The :confval:`norecursedirs` check is now performed in a :hook:`pytest_ignore_collect` implementation, so plugins can affect it. + + If after updating to this version you see that your `norecursedirs` setting is not being respected, + it means that a conftest or a plugin you use has a bad `pytest_ignore_collect` implementation. + Most likely, your hook returns `False` for paths it does not want to ignore, + which ends the processing and doesn't allow other plugins, including pytest itself, to ignore the path. + The fix is to return `None` instead of `False` for paths your hook doesn't want to ignore. + + +- `#8711 `_: :func:`caplog.set_level() ` and :func:`caplog.at_level() ` + will temporarily enable the requested ``level`` if ``level`` was disabled globally via + ``logging.disable(LEVEL)``. + + + +Bug Fixes +--------- + +- `#10831 `_: Terminal Reporting: Fixed bug when running in ``--tb=line`` mode where ``pytest.fail(pytrace=False)`` tests report ``None``. + + +- `#11068 `_: Fixed the ``--last-failed`` whole-file skipping functionality ("skipped N files") for :ref:`non-python test files `. + + +- `#11104 `_: Fixed a regression in pytest 7.3.2 which caused to :confval:`testpaths` to be considered for loading initial conftests, + even when it was not utilized (e.g. when explicit paths were given on the command line). + Now the ``testpaths`` are only considered when they are in use. + + +- `#1904 `_: Fixed traceback entries hidden with ``__tracebackhide__ = True`` still being shown for chained exceptions (parts after "... the above exception ..." message). + + +- `#7781 `_: Fix writing non-encodable text to log file when using ``--debug``. + + + +Improved Documentation +---------------------- + +- `#9146 `_: Improved documentation for :func:`caplog.set_level() `. + + + +Trivial/Internal Changes +------------------------ + +- `#11031 `_: Enhanced the CLI flag for ``-c`` to now include ``--config-file`` to make it clear that this flag applies to the usage of a custom config file. + + pytest 7.3.2 (2023-06-10) ========================= diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 2dbf7d387..e295c1804 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 7.3.2 + pytest 7.4.0 .. _`simpletest`: diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index c6629db84..83bbccbcb 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -1923,9 +1923,9 @@ All the command-line flags can be obtained by running ``pytest --help``:: --strict-markers Markers not registered in the `markers` section of the configuration file raise errors --strict (Deprecated) alias to --strict-markers - -c, --config-file FILE + -c FILE, --config-file=FILE Load configuration from `FILE` instead of trying to - locate one of the implicit configuration files + locate one of the implicit configuration files. --continue-on-collection-errors Force test execution even if collection errors occur --rootdir=ROOTDIR Define root directory for tests. Can be relative From a4a189ad99b2a7c3536de30009bb8111a0cf176e Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 23 Jun 2023 14:36:48 +0300 Subject: [PATCH 55/62] Change PytestRemovedIn8Warning to error by default Per our backward compatibility policy. --- changelog/7363.breaking.rst | 22 ++++++++++++++++++++++ src/_pytest/warnings.py | 2 ++ testing/acceptance_test.py | 6 ++---- testing/deprecated_test.py | 14 +++++++------- testing/test_config.py | 2 +- testing/test_nose.py | 22 ++++++++++++++++------ testing/test_warnings.py | 3 ++- 7 files changed, 52 insertions(+), 19 deletions(-) create mode 100644 changelog/7363.breaking.rst diff --git a/changelog/7363.breaking.rst b/changelog/7363.breaking.rst new file mode 100644 index 000000000..93d87b1b1 --- /dev/null +++ b/changelog/7363.breaking.rst @@ -0,0 +1,22 @@ +**PytestRemovedIn8Warning deprecation warnings are now errors by default.** + +Following our plan to remove deprecated features with as little disruption as +possible, all warnings of type ``PytestRemovedIn8Warning`` now generate errors +instead of warning messages by default. + +**The affected features will be effectively removed in pytest 8.1**, so please consult the +:ref:`deprecations` section in the docs for directions on how to update existing code. + +In the pytest ``8.0.X`` series, it is possible to change the errors back into warnings as a +stopgap measure by adding this to your ``pytest.ini`` file: + +.. code-block:: ini + + [pytest] + filterwarnings = + ignore::pytest.PytestRemovedIn8Warning + +But this will stop working when pytest ``8.1`` is released. + +**If you have concerns** about the removal of a specific feature, please add a +comment to :issue:`7363`. diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index 4aaa94452..bb293ec08 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -49,6 +49,8 @@ def catch_warnings_for_item( warnings.filterwarnings("always", category=DeprecationWarning) warnings.filterwarnings("always", category=PendingDeprecationWarning) + warnings.filterwarnings("error", category=pytest.PytestRemovedIn8Warning) + apply_warning_filters(config_filters, cmdline_filters) # apply filters from "filterwarnings" marks diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 5658f2fd6..de9e92d00 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1164,7 +1164,6 @@ def test_usage_error_code(pytester: Pytester) -> None: assert result.ret == ExitCode.USAGE_ERROR -@pytest.mark.filterwarnings("default::pytest.PytestUnhandledCoroutineWarning") def test_warn_on_async_function(pytester: Pytester) -> None: # In the below we .close() the coroutine only to avoid # "RuntimeWarning: coroutine 'test_2' was never awaited" @@ -1181,7 +1180,7 @@ def test_warn_on_async_function(pytester: Pytester) -> None: return coro """ ) - result = pytester.runpytest() + result = pytester.runpytest("-Wdefault") result.stdout.fnmatch_lines( [ "test_async.py::test_1", @@ -1197,7 +1196,6 @@ def test_warn_on_async_function(pytester: Pytester) -> None: ) -@pytest.mark.filterwarnings("default::pytest.PytestUnhandledCoroutineWarning") def test_warn_on_async_gen_function(pytester: Pytester) -> None: pytester.makepyfile( test_async=""" @@ -1209,7 +1207,7 @@ def test_warn_on_async_gen_function(pytester: Pytester) -> None: return test_2() """ ) - result = pytester.runpytest() + result = pytester.runpytest("-Wdefault") result.stdout.fnmatch_lines( [ "test_async.py::test_1", diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 3ceed7f5a..50eedb83c 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -103,7 +103,7 @@ def test_strict_option_is_deprecated(pytester: Pytester) -> None: def test_foo(): pass """ ) - result = pytester.runpytest("--strict") + result = pytester.runpytest("--strict", "-Wdefault::pytest.PytestRemovedIn8Warning") result.stdout.fnmatch_lines( [ "'unknown' not found in `markers` configuration option", @@ -189,7 +189,7 @@ class TestSkipMsgArgumentDeprecated: pytest.skip(msg="skippedmsg") """ ) - result = pytester.runpytest(p) + result = pytester.runpytest(p, "-Wdefault::pytest.PytestRemovedIn8Warning") result.stdout.fnmatch_lines( [ "*PytestRemovedIn8Warning: pytest.skip(msg=...) is now deprecated, " @@ -208,7 +208,7 @@ class TestSkipMsgArgumentDeprecated: pytest.fail(msg="failedmsg") """ ) - result = pytester.runpytest(p) + result = pytester.runpytest(p, "-Wdefault::pytest.PytestRemovedIn8Warning") result.stdout.fnmatch_lines( [ "*PytestRemovedIn8Warning: pytest.fail(msg=...) is now deprecated, " @@ -227,7 +227,7 @@ class TestSkipMsgArgumentDeprecated: pytest.exit(msg="exitmsg") """ ) - result = pytester.runpytest(p) + result = pytester.runpytest(p, "-Wdefault::pytest.PytestRemovedIn8Warning") result.stdout.fnmatch_lines( [ "*PytestRemovedIn8Warning: pytest.exit(msg=...) is now deprecated, " @@ -245,7 +245,7 @@ def test_deprecation_of_cmdline_preparse(pytester: Pytester) -> None: """ ) - result = pytester.runpytest() + result = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning") result.stdout.fnmatch_lines( [ "*PytestRemovedIn8Warning: The pytest_cmdline_preparse hook is deprecated*", @@ -299,7 +299,7 @@ def test_nose_deprecated_with_setup(pytester: Pytester) -> None: ... """ ) - output = pytester.runpytest() + output = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning") message = [ "*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.", "*test_nose_deprecated_with_setup.py::test_omits_warnings is using nose method: `setup_fn_no_op` (setup)", @@ -327,7 +327,7 @@ def test_nose_deprecated_setup_teardown(pytester: Pytester) -> None: ... """ ) - output = pytester.runpytest() + output = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning") message = [ "*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.", "*test_nose_deprecated_setup_teardown.py::Test::test is using nose-specific method: `setup(self)`", diff --git a/testing/test_config.py b/testing/test_config.py index 3aec5d763..257d696fa 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1184,7 +1184,7 @@ def test_cmdline_processargs_simple(pytester: Pytester) -> None: args.append("-h") """ ) - result = pytester.runpytest() + result = pytester.runpytest("-Wignore::pytest.PytestRemovedIn8Warning") result.stdout.fnmatch_lines(["*pytest*", "*-h*"]) diff --git a/testing/test_nose.py b/testing/test_nose.py index e838e79dd..cc79eb45b 100644 --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -23,7 +23,9 @@ def test_nose_setup(pytester: Pytester) -> None: test_hello.teardown = lambda: values.append(2) """ ) - result = pytester.runpytest(p, "-p", "nose") + result = pytester.runpytest( + p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning" + ) result.assert_outcomes(passed=2) @@ -76,7 +78,9 @@ def test_nose_setup_func(pytester: Pytester) -> None: """ ) - result = pytester.runpytest(p, "-p", "nose") + result = pytester.runpytest( + p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning" + ) result.assert_outcomes(passed=2) @@ -100,7 +104,9 @@ def test_nose_setup_func_failure(pytester: Pytester) -> None: """ ) - result = pytester.runpytest(p, "-p", "nose") + result = pytester.runpytest( + p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning" + ) result.stdout.fnmatch_lines(["*TypeError: ()*"]) @@ -154,7 +160,9 @@ def test_nose_setup_partial(pytester: Pytester) -> None: test_hello.teardown = my_teardown_partial """ ) - result = pytester.runpytest(p, "-p", "nose") + result = pytester.runpytest( + p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning" + ) result.stdout.fnmatch_lines(["*2 passed*"]) @@ -193,7 +201,9 @@ def test_module_level_setup(pytester: Pytester) -> None: assert items["setup2"] == ["up", "down", "up"] """ ) - result = pytester.runpytest("-p", "nose") + result = pytester.runpytest( + "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning" + ) result.stdout.fnmatch_lines(["*4 passed*"]) @@ -278,7 +288,7 @@ def test_nose_setup_ordering(pytester: Pytester) -> None: assert self.visited_cls """ ) - result = pytester.runpytest() + result = pytester.runpytest("-Wignore::pytest.PytestRemovedIn8Warning") result.stdout.fnmatch_lines(["*1 passed*"]) diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 03846cb30..96ecad6f6 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -518,7 +518,8 @@ class TestDeprecationWarningsByDefault: assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() -@pytest.mark.skip("not relevant until pytest 8.0") +# In 8.1, uncomment below and change RemovedIn8 -> RemovedIn9. +# @pytest.mark.skip("not relevant until pytest 9.0") @pytest.mark.parametrize("change_default", [None, "ini", "cmdline"]) def test_removed_in_x_warning_as_error(pytester: Pytester, change_default) -> None: """This ensures that PytestRemovedInXWarnings raised by pytest are turned into errors. From 1b7896f83d5c7ed822f5efc1d62fb33325ce2471 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 23 Jun 2023 15:25:52 +0300 Subject: [PATCH 56/62] Add PytestRemovedIn9Warning It's good to have it available already in all 8.* versions, even if it's not used yet. --- doc/en/reference/reference.rst | 3 +++ src/_pytest/warning_types.py | 6 ++++++ src/pytest/__init__.py | 2 ++ 3 files changed, 11 insertions(+) diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 83bbccbcb..d5ab94bc3 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -1153,6 +1153,9 @@ Custom warnings generated in some situations such as improper usage or deprecate .. autoclass:: pytest.PytestRemovedIn8Warning :show-inheritance: +.. autoclass:: pytest.PytestRemovedIn9Warning + :show-inheritance: + .. autoclass:: pytest.PytestUnhandledCoroutineWarning :show-inheritance: diff --git a/src/_pytest/warning_types.py b/src/_pytest/warning_types.py index bd5f41873..f6b0a3a69 100644 --- a/src/_pytest/warning_types.py +++ b/src/_pytest/warning_types.py @@ -56,6 +56,12 @@ class PytestRemovedIn8Warning(PytestDeprecationWarning): __module__ = "pytest" +class PytestRemovedIn9Warning(PytestDeprecationWarning): + """Warning class for features that will be removed in pytest 9.""" + + __module__ = "pytest" + + class PytestReturnNotNoneWarning(PytestRemovedIn8Warning): """Warning emitted when a test function is returning value other than None.""" diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index 9c1d5d204..58ddb3288 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -71,6 +71,7 @@ from _pytest.warning_types import PytestConfigWarning from _pytest.warning_types import PytestDeprecationWarning from _pytest.warning_types import PytestExperimentalApiWarning from _pytest.warning_types import PytestRemovedIn8Warning +from _pytest.warning_types import PytestRemovedIn9Warning from _pytest.warning_types import PytestReturnNotNoneWarning from _pytest.warning_types import PytestUnhandledCoroutineWarning from _pytest.warning_types import PytestUnhandledThreadExceptionWarning @@ -131,6 +132,7 @@ __all__ = [ "PytestDeprecationWarning", "PytestExperimentalApiWarning", "PytestRemovedIn8Warning", + "PytestRemovedIn9Warning", "PytestReturnNotNoneWarning", "Pytester", "PytestPluginManager", From c8b1790ee783c89d1dbda837f2a67001c71da741 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 27 May 2023 14:42:19 +0300 Subject: [PATCH 57/62] python: change `pytest pkg/__init__.py` to only collect the `__init__.py` Module Previously it would collect the entire package, but this is not what users expect. Refs #3749 Fixes #8976 Fixes #9263 Fixes #9313 --- changelog/8976.breaking.rst | 5 ++++ doc/en/deprecations.rst | 18 +++++++++++-- src/_pytest/python.py | 4 ++- .../package_init_given_as_arg/pkg/__init__.py | 2 ++ .../package_init_given_as_arg/pkg/test_foo.py | 2 +- testing/python/collect.py | 11 +++++--- testing/test_collection.py | 25 +++++++++++++------ testing/test_doctest.py | 4 +-- testing/test_nose.py | 2 +- 9 files changed, 56 insertions(+), 17 deletions(-) create mode 100644 changelog/8976.breaking.rst diff --git a/changelog/8976.breaking.rst b/changelog/8976.breaking.rst new file mode 100644 index 000000000..bd9a63982 --- /dev/null +++ b/changelog/8976.breaking.rst @@ -0,0 +1,5 @@ +Running `pytest pkg/__init__.py` now collects the `pkg/__init__.py` file (module) only. +Previously, it collected the entire `pkg` package, including other test files in the directory, but excluding tests in the `__init__.py` file itself +(unless :confval:`python_files` was changed to allow `__init__.py` file). + +To collect the entire package, specify just the directory: `pytest pkg`. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 4f7830a27..810f0062b 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -467,12 +467,26 @@ The ``yield_fixture`` function/decorator It has been so for a very long time, so can be search/replaced safely. -Removed Features ----------------- +Removed Features and Breaking Changes +------------------------------------- As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after an appropriate period of deprecation has passed. +Some breaking changes which could not be deprecated are also listed. + + +Collecting ``__init__.py`` files no longer collects package +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionremoved:: 8.0 + +Running `pytest pkg/__init__.py` now collects the `pkg/__init__.py` file (module) only. +Previously, it collected the entire `pkg` package, including other test files in the directory, but excluding tests in the `__init__.py` file itself +(unless :confval:`python_files` was changed to allow `__init__.py` file). + +To collect the entire package, specify just the directory: `pytest pkg`. + The ``pytest.collect`` module ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/_pytest/python.py b/src/_pytest/python.py index ad847c8af..6ba568c0f 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -736,7 +736,9 @@ class Package(Module): this_path = self.path.parent # Always collect the __init__ first. - if path_matches_patterns(self.path, self.config.getini("python_files")): + if self.session.isinitpath(self.path) or path_matches_patterns( + self.path, self.config.getini("python_files") + ): yield Module.from_parent(self, path=self.path) pkg_prefixes: Set[Path] = set() diff --git a/testing/example_scripts/collect/package_init_given_as_arg/pkg/__init__.py b/testing/example_scripts/collect/package_init_given_as_arg/pkg/__init__.py index e69de29bb..9cd366295 100644 --- a/testing/example_scripts/collect/package_init_given_as_arg/pkg/__init__.py +++ b/testing/example_scripts/collect/package_init_given_as_arg/pkg/__init__.py @@ -0,0 +1,2 @@ +def test_init(): + pass diff --git a/testing/example_scripts/collect/package_init_given_as_arg/pkg/test_foo.py b/testing/example_scripts/collect/package_init_given_as_arg/pkg/test_foo.py index f17482385..8f2d73cfa 100644 --- a/testing/example_scripts/collect/package_init_given_as_arg/pkg/test_foo.py +++ b/testing/example_scripts/collect/package_init_given_as_arg/pkg/test_foo.py @@ -1,2 +1,2 @@ -def test(): +def test_foo(): pass diff --git a/testing/python/collect.py b/testing/python/collect.py index 9bf6e00d1..8de216d8f 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1420,10 +1420,15 @@ def test_package_collection_infinite_recursion(pytester: Pytester) -> None: def test_package_collection_init_given_as_argument(pytester: Pytester) -> None: - """Regression test for #3749""" + """Regression test for #3749, #8976, #9263, #9313. + + Specifying an __init__.py file directly should collect only the __init__.py + Module, not the entire package. + """ p = pytester.copy_example("collect/package_init_given_as_arg") - result = pytester.runpytest(p / "pkg" / "__init__.py") - result.stdout.fnmatch_lines(["*1 passed*"]) + items, hookrecorder = pytester.inline_genitems(p / "pkg" / "__init__.py") + assert len(items) == 1 + assert items[0].name == "test_init" def test_package_with_modules(pytester: Pytester) -> None: diff --git a/testing/test_collection.py b/testing/test_collection.py index 8b0a1ab36..c370951b5 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1392,19 +1392,27 @@ def test_collect_pkg_init_and_file_in_args(pytester: Pytester) -> None: p = subdir.joinpath("test_file.py") p.write_text("def test_file(): pass", encoding="utf-8") - # NOTE: without "-o python_files=*.py" this collects test_file.py twice. - # This changed/broke with "Add package scoped fixtures #2283" (2b1410895) - # initially (causing a RecursionError). - result = pytester.runpytest("-v", str(init), str(p)) + # Just the package directory, the __init__.py module is filtered out. + result = pytester.runpytest("-v", subdir) result.stdout.fnmatch_lines( [ "sub/test_file.py::test_file PASSED*", + "*1 passed in*", + ] + ) + + # But it's included if specified directly. + result = pytester.runpytest("-v", init, p) + result.stdout.fnmatch_lines( + [ + "sub/__init__.py::test_init PASSED*", "sub/test_file.py::test_file PASSED*", "*2 passed in*", ] ) - result = pytester.runpytest("-v", "-o", "python_files=*.py", str(init), str(p)) + # Or if the pattern allows it. + result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir) result.stdout.fnmatch_lines( [ "sub/__init__.py::test_init PASSED*", @@ -1419,10 +1427,13 @@ def test_collect_pkg_init_only(pytester: Pytester) -> None: init = subdir.joinpath("__init__.py") init.write_text("def test_init(): pass", encoding="utf-8") - result = pytester.runpytest(str(init)) + result = pytester.runpytest(subdir) result.stdout.fnmatch_lines(["*no tests ran in*"]) - result = pytester.runpytest("-v", "-o", "python_files=*.py", str(init)) + result = pytester.runpytest("-v", init) + result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + + result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir) result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) diff --git a/testing/test_doctest.py b/testing/test_doctest.py index dfe569987..f189e8645 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -114,7 +114,7 @@ class TestDoctests: reprec.assertoutcome(failed=1) def test_importmode(self, pytester: Pytester): - p = pytester.makepyfile( + pytester.makepyfile( **{ "namespacepkg/innerpkg/__init__.py": "", "namespacepkg/innerpkg/a.py": """ @@ -132,7 +132,7 @@ class TestDoctests: """, } ) - reprec = pytester.inline_run(p, "--doctest-modules", "--import-mode=importlib") + reprec = pytester.inline_run("--doctest-modules", "--import-mode=importlib") reprec.assertoutcome(passed=1) def test_new_pattern(self, pytester: Pytester): diff --git a/testing/test_nose.py b/testing/test_nose.py index cc79eb45b..7ec4026f2 100644 --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -504,7 +504,7 @@ def test_nose_setup_skipped_if_non_callable(pytester: Pytester) -> None: pass """, ) - result = pytester.runpytest(p, "-p", "nose") + result = pytester.runpytest(p.parent, "-p", "nose") assert result.ret == 0 From 1306b84241349cb2b7c65425969c2560fa8e98af Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 25 Jun 2023 00:27:02 +0000 Subject: [PATCH 58/62] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 138 ++++++++++++++++++++----------- 1 file changed, 89 insertions(+), 49 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index c882130b0..f4e7570da 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -13,7 +13,7 @@ Packages classified as inactive are excluded. creating a PDF, because otherwise the table gets far too wide for the page. -This list contains 1267 plugins. +This list contains 1272 plugins. .. only:: not latex @@ -36,7 +36,7 @@ This list contains 1267 plugins. :pypi:`pytest-aiogram` May 06, 2023 N/A N/A :pypi:`pytest-aiohttp` Pytest plugin for aiohttp support Feb 12, 2022 4 - Beta pytest (>=6.1.0) :pypi:`pytest-aiohttp-client` Pytest \`client\` fixture for the Aiohttp Jan 10, 2023 N/A pytest (>=7.2.0,<8.0.0) - :pypi:`pytest-aiomoto` pytest-aiomoto Nov 09, 2022 N/A pytest (>=7.0,<8.0) + :pypi:`pytest-aiomoto` pytest-aiomoto Jun 24, 2023 N/A pytest (>=7.0,<8.0) :pypi:`pytest-aioresponses` py.test integration for aioresponses Jul 29, 2021 4 - Beta pytest (>=3.5.0) :pypi:`pytest-aioworkers` A plugin to test aioworkers project with pytest May 01, 2023 5 - Production/Stable pytest>=6.1.0 :pypi:`pytest-airflow` pytest support for airflow. Apr 03, 2019 3 - Alpha pytest (>=4.4.0) @@ -146,7 +146,7 @@ This list contains 1267 plugins. :pypi:`pytest-browsermob-proxy` BrowserMob proxy plugin for py.test. Jun 11, 2013 4 - Beta N/A :pypi:`pytest-browserstack-local` \`\`py.test\`\` plugin to run \`\`BrowserStackLocal\`\` in background. Feb 09, 2018 N/A N/A :pypi:`pytest-budosystems` Budo Systems is a martial arts school management system. This module is the Budo Systems Pytest Plugin. May 07, 2023 3 - Alpha pytest - :pypi:`pytest-bug` Pytest plugin for marking tests as a bug Jan 29, 2023 5 - Production/Stable pytest (>=6.2.0) + :pypi:`pytest-bug` Pytest plugin for marking tests as a bug Jun 23, 2023 5 - Production/Stable pytest (>=7.1.0) :pypi:`pytest-bugtong-tag` pytest-bugtong-tag is a plugin for pytest Jan 16, 2022 N/A N/A :pypi:`pytest-bugzilla` py.test bugzilla integration plugin May 05, 2010 4 - Beta N/A :pypi:`pytest-bugzilla-notifier` A plugin that allows you to execute create, update, and read information from BugZilla bugs Jun 15, 2018 4 - Beta pytest (>=2.9.2) @@ -213,16 +213,17 @@ This list contains 1267 plugins. :pypi:`pytest-colordots` Colorizes the progress indicators Oct 06, 2017 5 - Production/Stable N/A :pypi:`pytest-commander` An interactive GUI test runner for PyTest Aug 17, 2021 N/A pytest (<7.0.0,>=6.2.4) :pypi:`pytest-common-subject` pytest framework for testing different aspects of a common method May 15, 2022 N/A pytest (>=3.6,<8) - :pypi:`pytest-compare` pytest plugin for comparing call arguments. Mar 30, 2023 5 - Production/Stable N/A + :pypi:`pytest-compare` pytest plugin for comparing call arguments. Jun 22, 2023 5 - Production/Stable N/A :pypi:`pytest-concurrent` Concurrently execute test cases with multithread, multiprocess and gevent Jan 12, 2019 4 - Beta pytest (>=3.1.1) :pypi:`pytest-config` Base configurations and utilities for developing your Python project test suite with pytest. Nov 07, 2014 5 - Production/Stable N/A :pypi:`pytest-confluence-report` Package stands for pytest plugin to upload results into Confluence page. Apr 17, 2022 N/A N/A :pypi:`pytest-console-scripts` Pytest plugin for testing console scripts May 31, 2023 4 - Beta pytest (>=4.0.0) :pypi:`pytest-consul` pytest plugin with fixtures for testing consul aware apps Nov 24, 2018 3 - Alpha pytest - :pypi:`pytest-container` Pytest fixtures for writing container based tests Mar 21, 2023 4 - Beta pytest (>=3.10) + :pypi:`pytest-container` Pytest fixtures for writing container based tests Jun 19, 2023 4 - Beta pytest (>=3.10) :pypi:`pytest-contextfixture` Define pytest fixtures as context managers. Mar 12, 2013 4 - Beta N/A :pypi:`pytest-contexts` A plugin to run tests written with the Contexts framework using pytest May 19, 2021 4 - Beta N/A :pypi:`pytest-cookies` The pytest plugin for your Cookiecutter templates. 🍪 Mar 22, 2023 5 - Production/Stable pytest (>=3.9.0) + :pypi:`pytest-copier` A pytest plugin to help testing Copier templates Jun 23, 2023 4 - Beta pytest>=7.1.2 :pypi:`pytest-couchdbkit` py.test extension for per-test couchdb databases using couchdbkit Apr 17, 2012 N/A N/A :pypi:`pytest-count` count erros and send email Jan 12, 2018 4 - Beta N/A :pypi:`pytest-cov` Pytest plugin for measuring coverage. May 24, 2023 5 - Production/Stable pytest (>=4.6) @@ -232,7 +233,7 @@ This list contains 1267 plugins. :pypi:`pytest-coveragemarkers` Using pytest markers to track functional coverage and filtering of tests Nov 29, 2022 N/A pytest (>=7.1.2,<8.0.0) :pypi:`pytest-cov-exclude` Pytest plugin for excluding tests based on coverage data Apr 29, 2016 4 - Beta pytest (>=2.8.0,<2.9.0); extra == 'dev' :pypi:`pytest-cpp` Use pytest's runner to discover and execute C++ tests Jan 30, 2023 5 - Production/Stable pytest (>=7.0) - :pypi:`pytest-cppython` A pytest plugin that imports CPPython testing types Jun 14, 2023 N/A N/A + :pypi:`pytest-cppython` A pytest plugin that imports CPPython testing types Jun 19, 2023 N/A N/A :pypi:`pytest-cqase` Custom qase pytest plugin Aug 22, 2022 N/A pytest (>=7.1.2,<8.0.0) :pypi:`pytest-cram` Run cram tests with pytest. Aug 08, 2020 N/A N/A :pypi:`pytest-crate` Manages CrateDB instances during your integration tests May 28, 2019 3 - Alpha pytest (>=4.0) @@ -296,6 +297,7 @@ This list contains 1267 plugins. :pypi:`pytest-diffeo` A package to prevent Dependency Confusion attacks against Yandex. Feb 10, 2023 N/A N/A :pypi:`pytest-diff-selector` Get tests affected by code changes (using git) Feb 24, 2022 4 - Beta pytest (>=6.2.2) ; extra == 'all' :pypi:`pytest-difido` PyTest plugin for generating Difido reports Oct 23, 2022 4 - Beta pytest (>=4.0.0) + :pypi:`pytest-dir-equal` pytest-dir-equals is a pytest plugin providing helpers to assert directories equality allowing golden testing Jun 23, 2023 4 - Beta pytest>=7.1.2 :pypi:`pytest-disable` pytest plugin to disable a test and skip it from testrun Sep 10, 2015 4 - Beta N/A :pypi:`pytest-disable-plugin` Disable plugins per test Feb 28, 2019 4 - Beta pytest (>=3.5.0) :pypi:`pytest-discord` A pytest plugin to notify test results to a Discord channel. Feb 05, 2023 4 - Beta pytest (!=6.0.0,<8,>=3.3.2) @@ -468,6 +470,7 @@ This list contains 1267 plugins. :pypi:`pytest-flask-sqlalchemy-transactions` Run tests in transactions using pytest, Flask, and SQLalchemy. Aug 02, 2018 4 - Beta pytest (>=3.2.1) :pypi:`pytest-flexreport` Apr 15, 2023 4 - Beta pytest :pypi:`pytest-fluent` A pytest plugin in order to provide logs via fluentd Jul 12, 2022 4 - Beta pytest + :pypi:`pytest-fluentbit` A pytest plugin in order to provide logs via fluentbit Jun 16, 2023 4 - Beta pytest (>=7.0.0) :pypi:`pytest-flyte` Pytest fixtures for simplifying Flyte integration testing May 03, 2021 N/A pytest :pypi:`pytest-focus` A pytest plugin that alerts user of failed test cases with screen notifications May 04, 2019 4 - Beta pytest :pypi:`pytest-forbid` Mar 07, 2023 N/A pytest (>=7.2.2,<8.0.0) @@ -476,7 +479,7 @@ This list contains 1267 plugins. :pypi:`pytest-forward-compatibility` A pytest plugin to shim pytest commandline options for fowards compatibility Sep 29, 2020 N/A N/A :pypi:`pytest-frappe` Pytest Frappe Plugin - A set of pytest fixtures to test Frappe applications May 03, 2023 4 - Beta pytest>=7.0.0 :pypi:`pytest-freezegun` Wrap tests with fixtures in freeze_time Jul 19, 2020 4 - Beta pytest (>=3.0.0) - :pypi:`pytest-freezer` Pytest plugin providing a fixture interface for spulec/freezegun Jun 17, 2023 N/A pytest>=3.6 + :pypi:`pytest-freezer` Pytest plugin providing a fixture interface for spulec/freezegun Jun 21, 2023 N/A pytest >= 3.6 :pypi:`pytest-freeze-reqs` Check if requirement files are frozen Apr 29, 2021 N/A N/A :pypi:`pytest-frozen-uuids` Deterministically frozen UUID's for your tests Apr 17, 2022 N/A pytest (>=3.0) :pypi:`pytest-func-cov` Pytest plugin for measuring function coverage Apr 15, 2021 3 - Alpha pytest (>=5) @@ -493,6 +496,7 @@ This list contains 1267 plugins. :pypi:`pytest-ghostinspector` For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A :pypi:`pytest-girder` A set of pytest fixtures for testing Girder applications. Jun 14, 2023 N/A N/A :pypi:`pytest-git` Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest + :pypi:`pytest-gitconfig` Provide a gitconfig sandbox for testing Jun 22, 2023 4 - Beta pytest>=7.1.2 :pypi:`pytest-gitcov` Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A :pypi:`pytest-git-fixtures` Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest :pypi:`pytest-github` Plugin for py.test that associates tests with github issues using a marker. Mar 07, 2019 5 - Production/Stable N/A @@ -523,10 +527,10 @@ This list contains 1267 plugins. :pypi:`pytest-historic` Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest :pypi:`pytest-historic-hook` Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest :pypi:`pytest-homeassistant` A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A - :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Jun 16, 2023 3 - Alpha pytest (==7.3.1) + :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Jun 24, 2023 3 - Alpha pytest (==7.3.1) :pypi:`pytest-honey` A simple plugin to use with pytest Jan 07, 2022 4 - Beta pytest (>=3.5.0) :pypi:`pytest-honors` Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A - :pypi:`pytest-hot-reloading` Jun 16, 2023 N/A N/A + :pypi:`pytest-hot-reloading` Jun 23, 2023 N/A N/A :pypi:`pytest-hot-test` A plugin that tracks test changes Dec 10, 2022 4 - Beta pytest (>=3.5.0) :pypi:`pytest-hoverfly` Simplify working with Hoverfly from pytest Jan 30, 2023 N/A pytest (>=5.0) :pypi:`pytest-hoverfly-wrapper` Integrates the Hoverfly HTTP proxy into Pytest Feb 27, 2023 5 - Production/Stable pytest (>=3.7.0) @@ -554,7 +558,7 @@ This list contains 1267 plugins. :pypi:`pytest-ibutsu` A plugin to sent pytest results to an Ibutsu server Aug 05, 2022 4 - Beta pytest>=7.1 :pypi:`pytest-icdiff` use icdiff for better error messages in pytest assertions Aug 09, 2022 4 - Beta N/A :pypi:`pytest-idapro` A pytest plugin for idapython. Allows a pytest setup to run tests outside and inside IDA in an automated manner by runnig pytest inside IDA and by mocking idapython api Nov 03, 2018 N/A N/A - :pypi:`pytest-idem` A pytest plugin to help with testing idem projects Sep 07, 2022 5 - Production/Stable N/A + :pypi:`pytest-idem` A pytest plugin to help with testing idem projects Jun 23, 2023 5 - Production/Stable N/A :pypi:`pytest-idempotent` Pytest plugin for testing function idempotence. Jul 25, 2022 N/A N/A :pypi:`pytest-ignore-flaky` ignore failures from flaky tests (pytest plugin) Apr 23, 2021 5 - Production/Stable N/A :pypi:`pytest-image-diff` Mar 09, 2023 3 - Alpha pytest @@ -593,7 +597,7 @@ This list contains 1267 plugins. :pypi:`pytest-jest` A custom jest-pytest oriented Pytest reporter May 22, 2018 4 - Beta pytest (>=3.3.2) :pypi:`pytest-jinja` A plugin to generate customizable jinja-based HTML reports in pytest Oct 04, 2022 3 - Alpha pytest (>=6.2.5,<7.0.0) :pypi:`pytest-jira` py.test JIRA integration plugin, using markers Jun 12, 2023 3 - Alpha N/A - :pypi:`pytest-jira-xfail` Plugin skips (xfail) tests if unresolved Jira issue(s) linked Jun 14, 2023 N/A pytest (>=7.2.0) + :pypi:`pytest-jira-xfail` Plugin skips (xfail) tests if unresolved Jira issue(s) linked Jun 19, 2023 N/A pytest (>=7.2.0) :pypi:`pytest-jira-xray` pytest plugin to integrate tests with JIRA XRAY Jun 06, 2023 4 - Beta pytest :pypi:`pytest-job-selection` A pytest plugin for load balancing test suites Jan 30, 2023 4 - Beta pytest (>=3.5.0) :pypi:`pytest-jobserver` Limit parallel tests with posix jobserver. May 15, 2019 5 - Production/Stable pytest @@ -654,7 +658,7 @@ This list contains 1267 plugins. :pypi:`pytest-logger` Plugin configuring handlers for loggers from Python logging module. Jul 25, 2019 4 - Beta pytest (>=3.2) :pypi:`pytest-logging` Configures logging and allows tweaking the log level with a py.test flag Nov 04, 2015 4 - Beta N/A :pypi:`pytest-logging-end-to-end-test-tool` Sep 23, 2022 N/A pytest (>=7.1.2,<8.0.0) - :pypi:`pytest-logikal` Common testing environment Jun 04, 2023 5 - Production/Stable pytest (==7.3.1) + :pypi:`pytest-logikal` Common testing environment Jun 22, 2023 5 - Production/Stable pytest (==7.3.1) :pypi:`pytest-log-report` Package for creating a pytest test run reprot Dec 26, 2019 N/A N/A :pypi:`pytest-loguru` Pytest Loguru Apr 12, 2022 5 - Production/Stable N/A :pypi:`pytest-loop` pytest plugin for looping tests Jul 22, 2022 5 - Production/Stable pytest (>=6) @@ -734,7 +738,7 @@ This list contains 1267 plugins. :pypi:`pytest-mysql` MySQL process and client fixtures for pytest Mar 27, 2023 5 - Production/Stable pytest (>=6.2) :pypi:`pytest-needle` pytest plugin for visual testing websites using selenium Dec 10, 2018 4 - Beta pytest (<5.0.0,>=3.0.0) :pypi:`pytest-neo` pytest-neo is a plugin for pytest that shows tests like screen of Matrix. Jan 08, 2022 3 - Alpha pytest (>=6.2.0) - :pypi:`pytest-netdut` "Automated software testing for switches using pytest" Jun 05, 2023 N/A pytest (>=3.5.0) + :pypi:`pytest-netdut` "Automated software testing for switches using pytest" Jun 19, 2023 N/A pytest (>=3.5.0) :pypi:`pytest-network` A simple plugin to disable network on socket level. May 07, 2020 N/A N/A :pypi:`pytest-network-endpoints` Network endpoints plugin for pytest Mar 06, 2022 N/A pytest :pypi:`pytest-never-sleep` pytest plugin helps to avoid adding tests without mock \`time.sleep\` May 05, 2021 3 - Alpha pytest (>=3.5.1) @@ -868,7 +872,7 @@ This list contains 1267 plugins. :pypi:`pytest-pydocstyle` pytest plugin to run pydocstyle Jan 05, 2023 3 - Alpha N/A :pypi:`pytest-pylint` pytest plugin to check source code with pylint Sep 10, 2022 5 - Production/Stable pytest (>=5.4) :pypi:`pytest-pymysql-autorecord` Record PyMySQL queries and mock with the stored data. Sep 02, 2022 N/A N/A - :pypi:`pytest-pyodide` "Pytest plugin for testing applications that use Pyodide" Jun 11, 2023 N/A pytest + :pypi:`pytest-pyodide` "Pytest plugin for testing applications that use Pyodide" Jun 19, 2023 N/A pytest :pypi:`pytest-pypi` Easily test your HTTP library against a local copy of pypi Mar 04, 2018 3 - Alpha N/A :pypi:`pytest-pypom-navigation` Core engine for cookiecutter-qa and pytest-play packages Feb 18, 2019 4 - Beta pytest (>=3.0.7) :pypi:`pytest-pyppeteer` A plugin to run pyppeteer in pytest Apr 28, 2022 N/A pytest (>=6.2.5,<7.0.0) @@ -969,7 +973,7 @@ This list contains 1267 plugins. :pypi:`pytest-reverse` Pytest plugin to reverse test order. Jun 16, 2023 5 - Production/Stable pytest :pypi:`pytest-rich` Leverage rich for richer test session output Mar 03, 2022 4 - Beta pytest (>=7.0) :pypi:`pytest-rich-reporter` A pytest plugin using Rich for beautiful test result formatting. Feb 17, 2022 1 - Planning pytest (>=5.0.0) - :pypi:`pytest-richtrace` Nov 05, 2022 N/A pytest (>=7.2.0,<8.0.0) + :pypi:`pytest-richtrace` A pytest plugin that displays the names and information of the pytest hook functions as they are executed. Jun 20, 2023 N/A N/A :pypi:`pytest-ringo` pytest plugin to test webapplications using the Ringo webframework Sep 27, 2017 3 - Alpha N/A :pypi:`pytest-rmsis` Sycronise pytest results to Jira RMsis Aug 10, 2022 N/A pytest (>=5.3.5) :pypi:`pytest-rng` Fixtures for seeding tests and making randomness reproducible Aug 08, 2019 5 - Production/Stable pytest @@ -998,14 +1002,14 @@ This list contains 1267 plugins. :pypi:`pytest-sanic` a pytest plugin for Sanic Oct 25, 2021 N/A pytest (>=5.2) :pypi:`pytest-sanity` Dec 07, 2020 N/A N/A :pypi:`pytest-sa-pg` May 14, 2019 N/A N/A - :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Jun 13, 2023 5 - Production/Stable N/A + :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Jun 24, 2023 5 - Production/Stable N/A :pypi:`pytest-scenario` pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A :pypi:`pytest-schedule` The job of test scheduling for humans. Jan 07, 2023 5 - Production/Stable N/A :pypi:`pytest-schema` 👍 Validate return values against a schema-like object in testing Mar 14, 2022 5 - Production/Stable pytest (>=3.5.0) :pypi:`pytest-securestore` An encrypted password store for use within pytest cases Nov 08, 2021 4 - Beta N/A :pypi:`pytest-select` A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) :pypi:`pytest-selenium` pytest plugin for Selenium May 28, 2023 5 - Production/Stable pytest>=6.0.0 - :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Jun 13, 2023 5 - Production/Stable N/A + :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Jun 24, 2023 5 - Production/Stable N/A :pypi:`pytest-selenium-enhancer` pytest plugin for Selenium Apr 29, 2022 5 - Production/Stable N/A :pypi:`pytest-selenium-pdiff` A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A :pypi:`pytest-send-email` Send pytest execution result email Dec 04, 2019 N/A N/A @@ -1123,7 +1127,7 @@ This list contains 1267 plugins. :pypi:`pytest-telegram-notifier` Telegram notification plugin for Pytest Mar 17, 2023 5 - Production/Stable N/A :pypi:`pytest-tempdir` Predictable and repeatable tempdir support. Oct 11, 2019 4 - Beta pytest (>=2.8.1) :pypi:`pytest-terra-fixt` Terraform and Terragrunt fixtures for pytest Sep 15, 2022 N/A pytest (==6.2.5) - :pypi:`pytest-terraform` A pytest plugin for using terraform fixtures Sep 01, 2022 N/A pytest (>=6.0) + :pypi:`pytest-terraform` A pytest plugin for using terraform fixtures Jun 20, 2023 N/A pytest (>=6.0) :pypi:`pytest-terraform-fixture` generate terraform resources to use with pytest Nov 14, 2018 4 - Beta N/A :pypi:`pytest-testbook` A plugin to run tests written in Jupyter notebook Dec 11, 2016 3 - Alpha N/A :pypi:`pytest-testconfig` Test configuration plugin for pytest. Jan 11, 2020 4 - Beta pytest (>=3.5.0) @@ -1160,7 +1164,7 @@ This list contains 1267 plugins. :pypi:`pytest-thawgun` Pytest plugin for time travel May 26, 2020 3 - Alpha N/A :pypi:`pytest-threadleak` Detects thread leaks Jul 03, 2022 4 - Beta pytest (>=3.1.1) :pypi:`pytest-tick` Ticking on tests Aug 31, 2021 5 - Production/Stable pytest (>=6.2.5,<7.0.0) - :pypi:`pytest-time` Jun 16, 2023 3 - Alpha pytest + :pypi:`pytest-time` Jun 24, 2023 3 - Alpha pytest :pypi:`pytest-timeit` A pytest plugin to time test function runs Oct 13, 2016 4 - Beta N/A :pypi:`pytest-timeout` pytest plugin to abort hanging tests Jan 18, 2022 5 - Production/Stable pytest (>=5.0.0) :pypi:`pytest-timeouts` Linux-only Pytest plugin to control durations of various test case execution phases Sep 21, 2019 5 - Production/Stable N/A @@ -1242,7 +1246,7 @@ This list contains 1267 plugins. :pypi:`pytest-wa-e2e-plugin` Pytest plugin for testing whatsapp bots with end to end tests Feb 18, 2020 4 - Beta pytest (>=3.5.0) :pypi:`pytest-wake` May 11, 2023 N/A pytest :pypi:`pytest-watch` Local continuous test runner with pytest and watchdog. May 20, 2018 N/A N/A - :pypi:`pytest-watcher` Automatically rerun your tests on file modifications Jun 11, 2023 4 - Beta N/A + :pypi:`pytest-watcher` Automatically rerun your tests on file modifications Jun 24, 2023 4 - Beta N/A :pypi:`pytest-wdl` Pytest plugin for testing WDL workflows. Nov 17, 2020 5 - Production/Stable N/A :pypi:`pytest-web3-data` Sep 15, 2022 4 - Beta pytest :pypi:`pytest-webdriver` Selenium webdriver fixture for py.test May 28, 2019 5 - Production/Stable pytest @@ -1259,7 +1263,7 @@ This list contains 1267 plugins. :pypi:`pytest-xdist-debug-for-graingert` pytest xdist plugin for distributed testing and loop-on-failing modes Jul 24, 2019 5 - Production/Stable pytest (>=4.4.0) :pypi:`pytest-xdist-forked` forked from pytest-xdist Feb 10, 2020 5 - Production/Stable pytest (>=4.4.0) :pypi:`pytest-xdist-tracker` pytest plugin helps to reproduce failures for particular xdist node Nov 18, 2021 3 - Alpha pytest (>=3.5.1) - :pypi:`pytest-xdist-worker-stats` A pytest plugin to list worker statistics after a xdist run. Jun 15, 2023 4 - Beta pytest (>=7.3.2,<8.0.0) + :pypi:`pytest-xdist-worker-stats` A pytest plugin to list worker statistics after a xdist run. Jun 19, 2023 4 - Beta pytest (>=7.3.2,<8.0.0) :pypi:`pytest-xfaillist` Maintain a xfaillist in an additional file to avoid merge-conflicts. Sep 17, 2021 N/A pytest (>=6.2.2,<7.0.0) :pypi:`pytest-xfiles` Pytest fixtures providing data read from function, module or package related (x)files. Feb 27, 2018 N/A N/A :pypi:`pytest-xlog` Extended logging for test and decorators May 31, 2020 4 - Beta N/A @@ -1271,15 +1275,16 @@ This list contains 1267 plugins. :pypi:`pytest-xray-server` May 03, 2022 3 - Alpha pytest (>=5.3.1) :pypi:`pytest-xskynet` A package to prevent Dependency Confusion attacks against Yandex. Feb 10, 2023 N/A N/A :pypi:`pytest-xvfb` A pytest plugin to run Xvfb (or Xephyr/Xvnc) for tests. May 29, 2023 4 - Beta pytest (>=2.8.1) + :pypi:`pytest-xvirt` A pytest plugin to virtualize test. For example to transparently running them on a remote box. Jun 18, 2023 4 - Beta pytest (>=7.1.0) :pypi:`pytest-yaml` This plugin is used to load yaml output to your test using pytest framework. Oct 05, 2018 N/A pytest :pypi:`pytest-yaml-sanmu` pytest plugin for generating test cases by yaml May 28, 2023 N/A pytest>=7.2.0 :pypi:`pytest-yamltree` Create or check file/directory trees described by YAML Mar 02, 2020 4 - Beta pytest (>=3.1.1) :pypi:`pytest-yamlwsgi` Run tests against wsgi apps defined in yaml May 11, 2010 N/A N/A - :pypi:`pytest-yaml-yoyo` http/https API run by yaml Jun 08, 2023 N/A pytest (>=7.2.0) + :pypi:`pytest-yaml-yoyo` http/https API run by yaml Jun 19, 2023 N/A pytest (>=7.2.0) :pypi:`pytest-yapf` Run yapf Jul 06, 2017 4 - Beta pytest (>=3.1.1) :pypi:`pytest-yapf3` Validate your Python file format with yapf Mar 29, 2023 5 - Production/Stable pytest (>=7) :pypi:`pytest-yield` PyTest plugin to run tests concurrently, each \`yield\` switch context to other one Jan 23, 2019 N/A N/A - :pypi:`pytest-yls` Pytest plugin to test the YLS as a whole. Mar 29, 2023 N/A pytest (>=7.2.2,<8.0.0) + :pypi:`pytest-yls` Pytest plugin to test the YLS as a whole. Jun 21, 2023 N/A pytest (>=7.2.2,<8.0.0) :pypi:`pytest-yuk` Display tests you are uneasy with, using 🤢/🤮 for pass/fail of tests marked with yuk. Mar 26, 2021 N/A pytest>=5.0.0 :pypi:`pytest-zafira` A Zafira plugin for pytest Sep 18, 2019 5 - Production/Stable pytest (==4.1.1) :pypi:`pytest-zap` OWASP ZAP plugin for py.test. May 12, 2014 4 - Beta N/A @@ -1405,7 +1410,7 @@ This list contains 1267 plugins. Pytest \`client\` fixture for the Aiohttp :pypi:`pytest-aiomoto` - *last release*: Nov 09, 2022, + *last release*: Jun 24, 2023, *status*: N/A, *requires*: pytest (>=7.0,<8.0) @@ -2175,9 +2180,9 @@ This list contains 1267 plugins. Budo Systems is a martial arts school management system. This module is the Budo Systems Pytest Plugin. :pypi:`pytest-bug` - *last release*: Jan 29, 2023, + *last release*: Jun 23, 2023, *status*: 5 - Production/Stable, - *requires*: pytest (>=6.2.0) + *requires*: pytest (>=7.1.0) Pytest plugin for marking tests as a bug @@ -2644,7 +2649,7 @@ This list contains 1267 plugins. pytest framework for testing different aspects of a common method :pypi:`pytest-compare` - *last release*: Mar 30, 2023, + *last release*: Jun 22, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -2686,7 +2691,7 @@ This list contains 1267 plugins. pytest plugin with fixtures for testing consul aware apps :pypi:`pytest-container` - *last release*: Mar 21, 2023, + *last release*: Jun 19, 2023, *status*: 4 - Beta, *requires*: pytest (>=3.10) @@ -2713,6 +2718,13 @@ This list contains 1267 plugins. The pytest plugin for your Cookiecutter templates. 🍪 + :pypi:`pytest-copier` + *last release*: Jun 23, 2023, + *status*: 4 - Beta, + *requires*: pytest>=7.1.2 + + A pytest plugin to help testing Copier templates + :pypi:`pytest-couchdbkit` *last release*: Apr 17, 2012, *status*: N/A, @@ -2777,7 +2789,7 @@ This list contains 1267 plugins. Use pytest's runner to discover and execute C++ tests :pypi:`pytest-cppython` - *last release*: Jun 14, 2023, + *last release*: Jun 19, 2023, *status*: N/A, *requires*: N/A @@ -3224,6 +3236,13 @@ This list contains 1267 plugins. PyTest plugin for generating Difido reports + :pypi:`pytest-dir-equal` + *last release*: Jun 23, 2023, + *status*: 4 - Beta, + *requires*: pytest>=7.1.2 + + pytest-dir-equals is a pytest plugin providing helpers to assert directories equality allowing golden testing + :pypi:`pytest-disable` *last release*: Sep 10, 2015, *status*: 4 - Beta, @@ -4428,6 +4447,13 @@ This list contains 1267 plugins. A pytest plugin in order to provide logs via fluentd + :pypi:`pytest-fluentbit` + *last release*: Jun 16, 2023, + *status*: 4 - Beta, + *requires*: pytest (>=7.0.0) + + A pytest plugin in order to provide logs via fluentbit + :pypi:`pytest-flyte` *last release*: May 03, 2021, *status*: N/A, @@ -4485,9 +4511,9 @@ This list contains 1267 plugins. Wrap tests with fixtures in freeze_time :pypi:`pytest-freezer` - *last release*: Jun 17, 2023, + *last release*: Jun 21, 2023, *status*: N/A, - *requires*: pytest>=3.6 + *requires*: pytest >= 3.6 Pytest plugin providing a fixture interface for spulec/freezegun @@ -4603,6 +4629,13 @@ This list contains 1267 plugins. Git repository fixture for py.test + :pypi:`pytest-gitconfig` + *last release*: Jun 22, 2023, + *status*: 4 - Beta, + *requires*: pytest>=7.1.2 + + Provide a gitconfig sandbox for testing + :pypi:`pytest-gitcov` *last release*: Jan 11, 2020, *status*: 2 - Pre-Alpha, @@ -4814,7 +4847,7 @@ This list contains 1267 plugins. A pytest plugin for use with homeassistant custom components. :pypi:`pytest-homeassistant-custom-component` - *last release*: Jun 16, 2023, + *last release*: Jun 24, 2023, *status*: 3 - Alpha, *requires*: pytest (==7.3.1) @@ -4835,7 +4868,7 @@ This list contains 1267 plugins. Report on tests that honor constraints, and guard against regressions :pypi:`pytest-hot-reloading` - *last release*: Jun 16, 2023, + *last release*: Jun 23, 2023, *status*: N/A, *requires*: N/A @@ -5031,7 +5064,7 @@ This list contains 1267 plugins. A pytest plugin for idapython. Allows a pytest setup to run tests outside and inside IDA in an automated manner by runnig pytest inside IDA and by mocking idapython api :pypi:`pytest-idem` - *last release*: Sep 07, 2022, + *last release*: Jun 23, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -5304,7 +5337,7 @@ This list contains 1267 plugins. py.test JIRA integration plugin, using markers :pypi:`pytest-jira-xfail` - *last release*: Jun 14, 2023, + *last release*: Jun 19, 2023, *status*: N/A, *requires*: pytest (>=7.2.0) @@ -5731,7 +5764,7 @@ This list contains 1267 plugins. :pypi:`pytest-logikal` - *last release*: Jun 04, 2023, + *last release*: Jun 22, 2023, *status*: 5 - Production/Stable, *requires*: pytest (==7.3.1) @@ -6291,7 +6324,7 @@ This list contains 1267 plugins. pytest-neo is a plugin for pytest that shows tests like screen of Matrix. :pypi:`pytest-netdut` - *last release*: Jun 05, 2023, + *last release*: Jun 19, 2023, *status*: N/A, *requires*: pytest (>=3.5.0) @@ -7229,7 +7262,7 @@ This list contains 1267 plugins. Record PyMySQL queries and mock with the stored data. :pypi:`pytest-pyodide` - *last release*: Jun 11, 2023, + *last release*: Jun 19, 2023, *status*: N/A, *requires*: pytest @@ -7936,11 +7969,11 @@ This list contains 1267 plugins. A pytest plugin using Rich for beautiful test result formatting. :pypi:`pytest-richtrace` - *last release*: Nov 05, 2022, + *last release*: Jun 20, 2023, *status*: N/A, - *requires*: pytest (>=7.2.0,<8.0.0) - + *requires*: N/A + A pytest plugin that displays the names and information of the pytest hook functions as they are executed. :pypi:`pytest-ringo` *last release*: Sep 27, 2017, @@ -8139,7 +8172,7 @@ This list contains 1267 plugins. :pypi:`pytest-sbase` - *last release*: Jun 13, 2023, + *last release*: Jun 24, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -8188,7 +8221,7 @@ This list contains 1267 plugins. pytest plugin for Selenium :pypi:`pytest-seleniumbase` - *last release*: Jun 13, 2023, + *last release*: Jun 24, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -9014,7 +9047,7 @@ This list contains 1267 plugins. Terraform and Terragrunt fixtures for pytest :pypi:`pytest-terraform` - *last release*: Sep 01, 2022, + *last release*: Jun 20, 2023, *status*: N/A, *requires*: pytest (>=6.0) @@ -9273,7 +9306,7 @@ This list contains 1267 plugins. Ticking on tests :pypi:`pytest-time` - *last release*: Jun 16, 2023, + *last release*: Jun 24, 2023, *status*: 3 - Alpha, *requires*: pytest @@ -9847,7 +9880,7 @@ This list contains 1267 plugins. Local continuous test runner with pytest and watchdog. :pypi:`pytest-watcher` - *last release*: Jun 11, 2023, + *last release*: Jun 24, 2023, *status*: 4 - Beta, *requires*: N/A @@ -9966,7 +9999,7 @@ This list contains 1267 plugins. pytest plugin helps to reproduce failures for particular xdist node :pypi:`pytest-xdist-worker-stats` - *last release*: Jun 15, 2023, + *last release*: Jun 19, 2023, *status*: 4 - Beta, *requires*: pytest (>=7.3.2,<8.0.0) @@ -10049,6 +10082,13 @@ This list contains 1267 plugins. A pytest plugin to run Xvfb (or Xephyr/Xvnc) for tests. + :pypi:`pytest-xvirt` + *last release*: Jun 18, 2023, + *status*: 4 - Beta, + *requires*: pytest (>=7.1.0) + + A pytest plugin to virtualize test. For example to transparently running them on a remote box. + :pypi:`pytest-yaml` *last release*: Oct 05, 2018, *status*: N/A, @@ -10078,7 +10118,7 @@ This list contains 1267 plugins. Run tests against wsgi apps defined in yaml :pypi:`pytest-yaml-yoyo` - *last release*: Jun 08, 2023, + *last release*: Jun 19, 2023, *status*: N/A, *requires*: pytest (>=7.2.0) @@ -10106,7 +10146,7 @@ This list contains 1267 plugins. PyTest plugin to run tests concurrently, each \`yield\` switch context to other one :pypi:`pytest-yls` - *last release*: Mar 29, 2023, + *last release*: Jun 21, 2023, *status*: N/A, *requires*: pytest (>=7.2.2,<8.0.0) From 45f1a462d505438548a4dda2072093a22fbd1cb3 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 25 Jun 2023 16:08:53 +0100 Subject: [PATCH 59/62] Apply suggestions from code review Co-authored-by: Zac Hatfield-Dodds --- doc/en/how-to/fixtures.rst | 2 +- src/_pytest/deprecated.py | 2 +- testing/deprecated_test.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/en/how-to/fixtures.rst b/doc/en/how-to/fixtures.rst index e9f7f2d4b..c6047af12 100644 --- a/doc/en/how-to/fixtures.rst +++ b/doc/en/how-to/fixtures.rst @@ -1752,7 +1752,7 @@ into an ini-file: def my_fixture_that_sadly_wont_use_my_other_fixture(): ... - Currently this will generate a deprecation warning. + This generates a deprecation warning, and will become an error in Pytest 8. .. _`override fixtures`: diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 5874eeb99..3fcf99ba4 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -122,7 +122,7 @@ HOOK_LEGACY_MARKING = UnformattedWarning( "#configuring-hook-specs-impls-using-markers", ) -MARKED_FIXTURE = PytestDeprecationWarning( +MARKED_FIXTURE = PytestRemovedIn8Warning( "Marks applied to fixtures have no effect\n" "See docs: https://docs.pytest.org/en/stable/deprecations.html#applying-a-mark-to-a-fixture-function" ) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index cf067bac9..f4197a1f6 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -284,7 +284,7 @@ def test_importing_instance_is_deprecated(pytester: Pytester) -> None: def test_fixture_disallow_on_marked_functions(): """Test that applying @pytest.fixture to a marked function warns (#3364).""" with pytest.warns( - pytest.PytestDeprecationWarning, + pytest.PytestRemovedIn8Warning, match=r"Marks applied to fixtures have no effect", ) as record: @@ -303,7 +303,7 @@ def test_fixture_disallow_on_marked_functions(): def test_fixture_disallow_marks_on_fixtures(): """Test that applying a mark to a fixture warns (#3364).""" with pytest.warns( - pytest.PytestDeprecationWarning, + pytest.PytestRemovedIn8Warning, match=r"Marks applied to fixtures have no effect", ) as record: @@ -319,7 +319,7 @@ def test_fixture_disallow_marks_on_fixtures(): def test_fixture_disallowed_between_marks(): """Test that applying a mark to a fixture warns (#3364).""" with pytest.warns( - pytest.PytestDeprecationWarning, + pytest.PytestRemovedIn8Warning, match=r"Marks applied to fixtures have no effect", ) as record: From 518ca37caeda845376f71648714d53f26b218e82 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 25 Jun 2023 16:09:04 +0100 Subject: [PATCH 60/62] Update doc/en/deprecations.rst --- doc/en/deprecations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 3e15f892e..e73c1a18e 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -383,7 +383,7 @@ deprecation warning is now raised. Applying a mark to a fixture function ------------------------------------- -.. deprecated:: 7.2 +.. deprecated:: 7.4 Applying a mark to a fixture function never had any effect, but it is a common user error. From dd5f3773d961273d53691ebb06361639b361e9c6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 05:28:11 +0000 Subject: [PATCH 61/62] [pre-commit.ci] pre-commit autoupdate (#11145) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/autoflake: v2.1.1 → v2.2.0](https://github.com/PyCQA/autoflake/compare/v2.1.1...v2.2.0) - [github.com/pre-commit/mirrors-mypy: v1.3.0 → v1.4.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.3.0...v1.4.1) 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 b5089e129..1f1cd6743 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: exclude: _pytest/(debugging|hookspec).py language_version: python3 - repo: https://github.com/PyCQA/autoflake - rev: v2.1.1 + rev: v2.2.0 hooks: - id: autoflake name: autoflake @@ -56,7 +56,7 @@ repos: hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.3.0 + rev: v1.4.1 hooks: - id: mypy files: ^(src/|testing/) From 5749ea2f46328e9e3d701445085edad2941f05b7 Mon Sep 17 00:00:00 2001 From: Sadra Barikbin Date: Thu, 29 Jun 2023 10:51:27 +0330 Subject: [PATCH 62/62] Slight change in early teardown solution Fix some bugs and do some improvements and refactors --- src/_pytest/fixtures.py | 301 ++++++++++------ src/_pytest/python.py | 51 ++- testing/python/fixtures.py | 701 ++++++++++++++++++++++++++++++++++--- testing/python/metafunc.py | 2 + 4 files changed, 900 insertions(+), 155 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index e42119711..2d561f10d 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -225,13 +225,19 @@ class FixtureArgKey: param_value: Optional[Hashable] scoped_item_path: Optional[Path] item_cls: Optional[type] + baseid: Optional[str] -def get_fixture_arg_key(item: nodes.Item, argname: str, scope: Scope) -> FixtureArgKey: +def get_fixture_arg_key( + item: nodes.Item, + argname: str, + scope: Scope, + baseid: Optional[str] = None, + is_parametrized: Optional[bool] = False, +) -> FixtureArgKey: param_index = None param_value = None - if hasattr(item, "callspec") and argname in item.callspec.params: - # Fixture is parametrized. + if is_parametrized: if isinstance(item.callspec.params[argname], Hashable): param_value = item.callspec.params[argname] else: @@ -251,7 +257,9 @@ def get_fixture_arg_key(item: nodes.Item, argname: str, scope: Scope) -> Fixture else: item_cls = None - return FixtureArgKey(argname, param_index, param_value, scoped_item_path, item_cls) + return FixtureArgKey( + argname, param_index, param_value, scoped_item_path, item_cls, baseid + ) def get_fixture_keys(item: nodes.Item, scope: Scope) -> Iterator[FixtureArgKey]: @@ -259,17 +267,27 @@ def get_fixture_keys(item: nodes.Item, scope: Scope) -> Iterator[FixtureArgKey]: the specified scope.""" assert scope is not Scope.Function if hasattr(item, "_fixtureinfo"): - # sort this so that different calls to - # get_fixture_keys will be deterministic. - for argname, fixture_def in sorted(item._fixtureinfo.name2fixturedefs.items()): - # In the case item is parametrized on the `argname` with - # a scope, it overrides that of the fixture. - if hasattr(item, "callspec") and argname in item.callspec._arg2scope: - if item.callspec._arg2scope[argname] != scope: - continue - elif fixture_def[-1]._scope != scope: - continue - yield get_fixture_arg_key(item, argname, scope) + for argname in item._fixtureinfo.names_closure: + is_parametrized = ( + hasattr(item, "callspec") and argname in item.callspec._arg2scope + ) + for i in reversed( + range(item._fixtureinfo.name2num_fixturedefs_used[argname]) + ): + fixturedef = item._fixtureinfo.name2fixturedefs[argname][-(i + 1)] + # In the case item is parametrized on the `argname` with + # a scope, it overrides that of the fixture. + if (is_parametrized and item.callspec._arg2scope[argname] != scope) or ( + not is_parametrized and fixturedef._scope != scope + ): + break + yield get_fixture_arg_key( + item, + argname, + scope, + None if fixturedef.is_pseudo else fixturedef.baseid, + is_parametrized, + ) # Algorithm for sorting on a per-parametrized resource setup basis. @@ -293,17 +311,31 @@ def reorder_items(items: Sequence[nodes.Item]) -> List[nodes.Item]: for key in keys: item_d[key].append(item) items_dict = dict.fromkeys(items, None) + last_item_by_argkey: Dict[FixtureArgKey, nodes.Item] = {} reordered_items = list( - reorder_items_atscope(items_dict, argkeys_cache, items_by_argkey, Scope.Session) + reorder_items_atscope( + items_dict, + argkeys_cache, + items_by_argkey, + last_item_by_argkey, + Scope.Session, + ) ) for scope in reversed(HIGH_SCOPES): for key in items_by_argkey[scope]: - last_item_dependent_on_key = items_by_argkey[scope][key].pop() - fixturedef = last_item_dependent_on_key._fixtureinfo.name2fixturedefs[ - key.argname - ][-1] - if fixturedef.is_pseudo: + last_item_dependent_on_key = last_item_by_argkey[key] + if key.baseid is None: continue + for i in range( + last_item_dependent_on_key._fixtureinfo.name2num_fixturedefs_used[ + key.argname + ] + ): + fixturedef = last_item_dependent_on_key._fixtureinfo.name2fixturedefs[ + key.argname + ][-(i + 1)] + if fixturedef.baseid == key.baseid: + break last_item_dependent_on_key.teardown = functools.partial( lambda other_finalizers, new_finalizer: [ finalizer() for finalizer in (new_finalizer, other_finalizers) @@ -330,20 +362,28 @@ def fix_cache_order( if key in ignore: continue items_by_argkey[scope][key].appendleft(item) - # Make sure last dependent item on a key - # remains updated while reordering. - if items_by_argkey[scope][key][-1] == item: - items_by_argkey[scope][key].pop() def reorder_items_atscope( items: Dict[nodes.Item, None], argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[FixtureArgKey, None]]], items_by_argkey: Dict[Scope, Dict[FixtureArgKey, "Deque[nodes.Item]"]], + last_item_by_argkey: Dict[FixtureArgKey, nodes.Item], scope: Scope, ) -> Dict[nodes.Item, None]: - if scope is Scope.Function or len(items) < 3: + if scope is Scope.Function: return items + elif len(items) < 3: + for item in items: + for key in argkeys_cache[scope].get(item, []): + last_item_by_argkey[key] = item + return reorder_items_atscope( + items, + argkeys_cache, + items_by_argkey, + last_item_by_argkey, + scope.next_lower(), + ) ignore: Set[Optional[FixtureArgKey]] = set() items_deque = deque(items) items_done: Dict[nodes.Item, None] = {} @@ -363,20 +403,26 @@ def reorder_items_atscope( no_argkey_group[item] = None else: slicing_argkey, _ = argkeys.popitem() - # We don't have to remove relevant items from later in the # deque because they'll just be ignored. - matching_items = [ - i for i in scoped_items_by_argkey[slicing_argkey] if i in items - ] - for i in reversed(matching_items): + for i in reversed( + dict.fromkeys(scoped_items_by_argkey[slicing_argkey]) + ): + if i not in items: + continue fix_cache_order(i, argkeys_cache, items_by_argkey, ignore, scope) items_deque.appendleft(i) break if no_argkey_group: no_argkey_group = reorder_items_atscope( - no_argkey_group, argkeys_cache, items_by_argkey, scope.next_lower() + no_argkey_group, + argkeys_cache, + items_by_argkey, + last_item_by_argkey, + scope.next_lower(), ) for item in no_argkey_group: + for key in scoped_argkeys_cache.get(item, []): + last_item_by_argkey[key] = item items_done[item] = None ignore.add(slicing_argkey) return items_done @@ -384,7 +430,13 @@ def reorder_items_atscope( @dataclasses.dataclass class FuncFixtureInfo: - __slots__ = ("argnames", "initialnames", "names_closure", "name2fixturedefs") + __slots__ = ( + "argnames", + "initialnames", + "names_closure", + "name2fixturedefs", + "name2num_fixturedefs_used", + ) # Original function argument names. argnames: Tuple[str, ...] @@ -394,6 +446,7 @@ class FuncFixtureInfo: initialnames: Tuple[str, ...] names_closure: List[str] name2fixturedefs: Dict[str, Sequence["FixtureDef[Any]"]] + name2num_fixturedefs_used: Dict[str, int] def prune_dependency_tree(self) -> None: """Recompute names_closure from initialnames and name2fixturedefs. @@ -401,26 +454,11 @@ class FuncFixtureInfo: Can only reduce names_closure, which means that the new closure will always be a subset of the old one. The order is preserved. - This method is needed because direct parametrization may shadow some - of the fixtures that were included in the originally built dependency + This method is needed because dynamic direct parametrization may shadow + some of the fixtures that were included in the originally built dependency tree. In this way the dependency tree can get pruned, and the closure of argnames may get reduced. """ - closure: Set[str] = set() - working_set = set(self.initialnames) - while working_set: - argname = working_set.pop() - # Argname may be smth not included in the original names_closure, - # in which case we ignore it. This currently happens with pseudo - # FixtureDefs which wrap 'get_direct_param_fixture_func(request)'. - # So they introduce the new dependency 'request' which might have - # been missing in the original tree (closure). - if argname not in closure and argname in self.names_closure: - closure.add(argname) - if argname in self.name2fixturedefs: - working_set.update(self.name2fixturedefs[argname][-1].argnames) - - self.names_closure[:] = sorted(closure, key=self.names_closure.index) class FixtureRequest: @@ -1407,6 +1445,26 @@ def pytest_addoption(parser: Parser) -> None: ) +def _get_direct_parametrize_args(node: nodes.Node) -> List[str]: + """Return all direct parametrization arguments of a node, so we don't + mistake them for fixtures. + + Check https://github.com/pytest-dev/pytest/issues/5036. + + These things are done later as well when dealing with parametrization + so this could be improved. + """ + parametrize_argnames: List[str] = [] + for marker in node.iter_markers(name="parametrize"): + if not marker.kwargs.get("indirect", False): + p_argnames, _ = ParameterSet._parse_parametrize_args( + *marker.args, **marker.kwargs + ) + parametrize_argnames.extend(p_argnames) + + return parametrize_argnames + + class FixtureManager: """pytest fixture definitions and information is stored and managed from this class. @@ -1452,25 +1510,6 @@ class FixtureManager: } session.config.pluginmanager.register(self, "funcmanage") - def _get_direct_parametrize_args(self, node: nodes.Node) -> List[str]: - """Return all direct parametrization arguments of a node, so we don't - mistake them for fixtures. - - Check https://github.com/pytest-dev/pytest/issues/5036. - - These things are done later as well when dealing with parametrization - so this could be improved. - """ - parametrize_argnames: List[str] = [] - for marker in node.iter_markers(name="parametrize"): - if not marker.kwargs.get("indirect", False): - p_argnames, _ = ParameterSet._parse_parametrize_args( - *marker.args, **marker.kwargs - ) - parametrize_argnames.extend(p_argnames) - - return parametrize_argnames - def getfixtureinfo( self, node: nodes.Node, func, cls, funcargs: bool = True ) -> FuncFixtureInfo: @@ -1482,12 +1521,27 @@ class FixtureManager: usefixtures = tuple( arg for mark in node.iter_markers(name="usefixtures") for arg in mark.args ) - initialnames = usefixtures + argnames - fm = node.session._fixturemanager - initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure( - initialnames, node, ignore_args=self._get_direct_parametrize_args(node) + initialnames = tuple( + dict.fromkeys( + tuple(self._getautousenames(node.nodeid)) + usefixtures + argnames + ) + ) + + arg2fixturedefs: Dict[str, Sequence[FixtureDef[Any]]] = {} + names_closure, arg2num_fixturedefs_used = self.getfixtureclosure( + node, + initialnames, + arg2fixturedefs, + self, + ignore_args=_get_direct_parametrize_args(node), + ) + return FuncFixtureInfo( + argnames, + initialnames, + names_closure, + arg2fixturedefs, + arg2num_fixturedefs_used, ) - return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs) def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None: nodeid = None @@ -1518,12 +1572,14 @@ class FixtureManager: if basenames: yield from basenames + @staticmethod def getfixtureclosure( - self, - fixturenames: Tuple[str, ...], parentnode: nodes.Node, + initialnames: Tuple[str], + arg2fixturedefs: Dict[str, Sequence[FixtureDef[Any]]], + fixturemanager: Optional["FixtureManager"] = None, ignore_args: Sequence[str] = (), - ) -> Tuple[Tuple[str, ...], List[str], Dict[str, Sequence[FixtureDef[Any]]]]: + ) -> Tuple[List[str], Dict[str, List[FixtureDef[Any]]]]: # Collect the closure of all fixtures, starting with the given # fixturenames as the initial set. As we have to visit all # factory definitions anyway, we also return an arg2fixturedefs @@ -1532,44 +1588,74 @@ class FixtureManager: # (discovering matching fixtures for a given name/node is expensive). parentid = parentnode.nodeid - fixturenames_closure = list(self._getautousenames(parentid)) - - def merge(otherlist: Iterable[str]) -> None: - for arg in otherlist: - if arg not in fixturenames_closure: - fixturenames_closure.append(arg) - - merge(fixturenames) + fixturenames_closure: Dict[str, int] = {} # At this point, fixturenames_closure contains what we call "initialnames", # which is a set of fixturenames the function immediately requests. We # need to return it as well, so save this. - initialnames = tuple(fixturenames_closure) - arg2fixturedefs: Dict[str, Sequence[FixtureDef[Any]]] = {} - lastlen = -1 - while lastlen != len(fixturenames_closure): - lastlen = len(fixturenames_closure) - for argname in fixturenames_closure: - if argname in ignore_args: - continue - if argname in arg2fixturedefs: - continue - fixturedefs = self.getfixturedefs(argname, parentid) - if fixturedefs: - arg2fixturedefs[argname] = fixturedefs - merge(fixturedefs[-1].argnames) + arg2num_fixturedefs_used: Dict[str, int] = defaultdict(lambda: 0) + arg2num_def_used_in_path: Dict[str, int] = defaultdict(lambda: 0) + nodes_in_fixture_tree: Deque[Tuple[str, bool]] = deque( + [(name, name != initialnames[-1]) for name in initialnames] + ) + nodes_in_path: Deque[Tuple[str, bool]] = deque() - def sort_by_scope(arg_name: str) -> Scope: + while nodes_in_fixture_tree: + node, has_sibling = nodes_in_fixture_tree.popleft() + if node not in fixturenames_closure or fixturenames_closure[node][0] > len( + nodes_in_path + ): + fixturenames_closure[node] = ( + len(nodes_in_path), + len(fixturenames_closure), + ) + if node not in arg2fixturedefs: + if node not in ignore_args: + fixturedefs = fixturemanager.getfixturedefs(node, parentid) + if fixturedefs: + arg2fixturedefs[node] = fixturedefs + if node in arg2fixturedefs: + def_index = arg2num_def_used_in_path[node] + 1 + if ( + def_index <= len(arg2fixturedefs[node]) + and def_index > arg2num_fixturedefs_used[node] + ): + arg2num_fixturedefs_used[node] = def_index + fixturedef = arg2fixturedefs[node][-def_index] + if fixturedef.argnames: + nodes_in_path.append((node, has_sibling)) + arg2num_def_used_in_path[node] += 1 + nodes_in_fixture_tree.extendleft( + [ + (argname, argname != fixturedef.argnames[-1]) + for argname in reversed(fixturedef.argnames) + ] + ) + continue + while not has_sibling: + try: + node, has_sibling = nodes_in_path.pop() + except IndexError: + assert len(nodes_in_fixture_tree) == 0 + break + arg2num_def_used_in_path[node] -= 1 + + fixturenames_closure_list = list(fixturenames_closure) + + def sort_by_scope_depth_and_arrival(arg_name: str) -> Scope: + depth, arrival = fixturenames_closure[arg_name] try: fixturedefs = arg2fixturedefs[arg_name] except KeyError: - return Scope.Function + return (Scope.Function, -depth, -arrival) else: - return fixturedefs[-1]._scope + return (fixturedefs[-1]._scope, -depth, -arrival) - fixturenames_closure.sort(key=sort_by_scope, reverse=True) - return initialnames, fixturenames_closure, arg2fixturedefs + fixturenames_closure_list.sort( + key=sort_by_scope_depth_and_arrival, reverse=True + ) + return fixturenames_closure_list, arg2num_fixturedefs_used def pytest_generate_tests(self, metafunc: "Metafunc") -> None: """Generate new tests based on parametrized fixtures used by the given metafunc""" @@ -1744,6 +1830,13 @@ class FixtureManager: def _matchfactories( self, fixturedefs: Iterable[FixtureDef[Any]], nodeid: str ) -> Iterator[FixtureDef[Any]]: + """Yields the visible fixturedefs to a node with the given id + from among the specified fixturedefs. + + :param Iterable[FixtureDef] fixturedefs: The list of specified fixturedefs. + :param str nodeid: Full node id of the node. + :rtype: Iterator[FixtureDef] + """ parentnodeids = set(nodes.iterparentnodeids(nodeid)) for fixturedef in fixturedefs: if fixturedef.baseid in parentnodeids: diff --git a/src/_pytest/python.py b/src/_pytest/python.py index e7929a2c0..40befca7b 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -58,7 +58,9 @@ from _pytest.config.argparsing import Parser from _pytest.deprecated import check_ispytest from _pytest.deprecated import INSTANCE_COLLECTOR from _pytest.deprecated import NOSE_SUPPORT_METHOD +from _pytest.fixtures import _get_direct_parametrize_args from _pytest.fixtures import FixtureDef +from _pytest.fixtures import FixtureManager from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FuncFixtureInfo from _pytest.fixtures import get_scope_node @@ -388,6 +390,26 @@ del _EmptyClass # fmt: on +def unwrap_metafunc_parametrize_and_possibly_prune_dependency_tree(metafunc): + metafunc.parametrize = metafunc._parametrize + del metafunc._parametrize + if metafunc.has_dynamic_parametrize: + # Direct parametrization may have shadowed some fixtures + # so make sure we update what the function really needs. + fixture_closure, arg2num_fixturedefs_used = FixtureManager.getfixtureclosure( + metafunc.definition, + metafunc.definition._fixtureinfo.initialnames, + metafunc._arg2fixturedefs, + ignore_args=_get_direct_parametrize_args(metafunc.definition) + ["request"], + ) + metafunc.fixturenames[:] = fixture_closure + metafunc.definition._fixtureinfo.name2num_fixturedefs_used.clear() + metafunc.definition._fixtureinfo.name2num_fixturedefs_used.update( + arg2num_fixturedefs_used + ) + del metafunc.has_dynamic_parametrize + + class PyCollector(PyobjMixin, nodes.Collector): def funcnamefilter(self, name: str) -> bool: return self._matches_prefix_or_glob_option("python_functions", name) @@ -484,8 +506,6 @@ class PyCollector(PyobjMixin, nodes.Collector): definition = FunctionDefinition.from_parent(self, name=name, callobj=funcobj) fixtureinfo = definition._fixtureinfo - # pytest_generate_tests impls call metafunc.parametrize() which fills - # metafunc._calls, the outcome of the hook. metafunc = Metafunc( definition=definition, fixtureinfo=fixtureinfo, @@ -494,19 +514,29 @@ class PyCollector(PyobjMixin, nodes.Collector): module=module, _ispytest=True, ) - methods = [] + methods = [unwrap_metafunc_parametrize_and_possibly_prune_dependency_tree] if hasattr(module, "pytest_generate_tests"): methods.append(module.pytest_generate_tests) if cls is not None and hasattr(cls, "pytest_generate_tests"): methods.append(cls().pytest_generate_tests) + metafunc.has_dynamic_parametrize = False + from functools import wraps + + @wraps(metafunc.parametrize) + def set_has_dynamic_parametrize(*args, **kwargs): + metafunc.has_dynamic_parametrize = True + metafunc._parametrize(*args, **kwargs) + + metafunc._parametrize = metafunc.parametrize + metafunc.parametrize = set_has_dynamic_parametrize + + # pytest_generate_tests impls call metafunc.parametrize() which fills + # metafunc._calls, the outcome of the hook. self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc)) + if not metafunc._calls: yield Function.from_parent(self, name=name, fixtureinfo=fixtureinfo) else: - # Direct parametrization may have shadowed some fixtures - # so make sure we update what the function really needs. - fixtureinfo.prune_dependency_tree() - for callspec in metafunc._calls: subname = f"{name}[{callspec.id}]" yield Function.from_parent( @@ -1360,7 +1390,7 @@ class Metafunc: if arg_values_types[argname] == "params": continue if name2pseudofixturedef is not None and argname in name2pseudofixturedef: - arg2fixturedefs[argname] = [name2pseudofixturedef[argname]] + fixturedef = name2pseudofixturedef[argname] else: fixturedef = FixtureDef( fixturemanager=self.definition.session._fixturemanager, @@ -1373,9 +1403,10 @@ class Metafunc: ids=None, is_pseudo=True, ) - arg2fixturedefs[argname] = [fixturedef] if name2pseudofixturedef is not None: name2pseudofixturedef[argname] = fixturedef + arg2fixturedefs[argname] = [fixturedef] + self.definition._fixtureinfo.name2num_fixturedefs_used[argname] = 1 # Create the new calls: if we are parametrize() multiple times (by applying the decorator # more than once) then we accumulate those calls generating the cartesian product @@ -1557,7 +1588,7 @@ def _find_parametrized_scope( if all_arguments_are_fixtures: fixturedefs = arg2fixturedefs or {} used_scopes = [ - fixturedef[0]._scope + fixturedef[0]._scope # Shouldn't be -1 ? for name, fixturedef in fixturedefs.items() if name in argnames ] diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 9828df6fc..fc896bf75 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -2,6 +2,7 @@ import os import sys import textwrap from pathlib import Path +from unittest.mock import Mock import pytest from _pytest import fixtures @@ -2681,16 +2682,19 @@ class TestFixtureMarker: """ ) result = pytester.runpytest("-v") + # Order changed because fixture keys were sorted by their names in fixtures::get_fixture_keys + # beforehand so encap key came before flavor. This isn't problematic here as both fixtures + # are session-scoped but when this isn't the case, it might be problematic. result.stdout.fnmatch_lines( """ test_dynamic_parametrized_ordering.py::test[flavor1-vxlan] PASSED test_dynamic_parametrized_ordering.py::test2[flavor1-vxlan] PASSED - test_dynamic_parametrized_ordering.py::test[flavor2-vxlan] PASSED - test_dynamic_parametrized_ordering.py::test2[flavor2-vxlan] PASSED - test_dynamic_parametrized_ordering.py::test[flavor2-vlan] PASSED - test_dynamic_parametrized_ordering.py::test2[flavor2-vlan] PASSED test_dynamic_parametrized_ordering.py::test[flavor1-vlan] PASSED test_dynamic_parametrized_ordering.py::test2[flavor1-vlan] PASSED + test_dynamic_parametrized_ordering.py::test[flavor2-vlan] PASSED + test_dynamic_parametrized_ordering.py::test2[flavor2-vlan] PASSED + test_dynamic_parametrized_ordering.py::test[flavor2-vxlan] PASSED + test_dynamic_parametrized_ordering.py::test2[flavor2-vxlan] PASSED """ ) @@ -4475,39 +4479,39 @@ def test_yield_fixture_with_no_value(pytester: Pytester) -> None: assert result.ret == ExitCode.TESTS_FAILED -def test_teardown_high_scope_fixture_at_last_dependent_item_simple( +def test_early_teardown_simple( pytester: Pytester, ) -> None: pytester.makepyfile( """ import pytest - @pytest.fixture(scope='module', params=[None]) + @pytest.fixture(scope='module') def fixture(): - yield - print("Tearing down fixture!") + pass def test_0(fixture): pass def test_1(fixture): - print("Running test_1!") + pass def test_2(): - print("Running test_2!") + pass """ ) - result = pytester.runpytest("-s") - assert result.ret == 0 + result = pytester.runpytest("--setup-show") result.stdout.fnmatch_lines( [ - "*Running test_1!*", - "*Tearing down fixture!*", - "*Running test_2!*", + "*SETUP M fixture*", + "*test_0*", + "*test_1*", + "*TEARDOWN M fixture*", + "*test_2*", ] ) -def test_teardown_high_scope_fixture_at_last_dependent_item_simple_2( +def test_early_teardown_simple_2( pytester: Pytester, ) -> None: pytester.makepyfile( @@ -4515,37 +4519,75 @@ def test_teardown_high_scope_fixture_at_last_dependent_item_simple_2( import pytest @pytest.fixture(scope='module', params=[None]) def fixture1(): - yield - print("Tearing down fixture!") + pass @pytest.fixture(scope='module', params=[None]) def fixture2(): - yield - print("Tearing down fixture!") + pass def test_0(fixture1): pass def test_1(fixture1, fixture2): - print("Running test_1!") + pass def test_2(): - print("Running test_2!") + pass """ ) - result = pytester.runpytest("-s") + result = pytester.runpytest("--setup-show") assert result.ret == 0 result.stdout.fnmatch_lines( [ - "*Running test_1!*", - "*Tearing down fixture!*", - "*Tearing down fixture!*", - "*Running test_2!*", + "*SETUP M fixture1*", + "*test_0*", + "*SETUP M fixture2*", + "*test_1*", + "*TEARDOWN M fixture2*", + "*TEARDOWN M fixture1*", + "*test_2*", ] ) -def test_teardown_high_scope_fixture_at_last_dependent_item_complex( +def test_early_teardown_simple_3(pytester: Pytester): + pytester.makepyfile( + """ + import pytest + @pytest.fixture(scope='module') + def fixture1(): + pass + + @pytest.fixture(scope='module') + def fixture2(): + pass + + def test_0(fixture1, fixture2): + pass + + def test_1(fixture1): + pass + + def test_2(fixture1, fixture2): + pass + """ + ) + result = pytester.runpytest("--setup-show") + result.stdout.fnmatch_lines( + [ + "*SETUP M fixture1*", + "*SETUP M fixture2*", + "*test_0*", + "*test_2*", + "*TEARDOWN M fixture2*", + "*test_1*", + "*TEARDOWN M fixture1*", + ], + consecutive=True, + ) + + +def test_early_teardown_complex( pytester: Pytester, ) -> None: pytester.makepyfile( @@ -4634,6 +4676,141 @@ def test_teardown_high_scope_fixture_at_last_dependent_item_complex( ) +def test_fixtureclosure_contains_shadowed_fixtures(pytester: Pytester): + pytester.makeconftest( + """ + import pytest + + @pytest.fixture + def fixt0(): + pass + + @pytest.fixture + def fixt1(): + pass + + @pytest.fixture + def fixt2(fixt0, fixt1): + pass + """ + ) + pytester.makepyfile( + """ + import pytest + + @pytest.fixture + def fixt2(fixt2): + pass + + @pytest.fixture + def fixt3(): + pass + + def test(fixt2, fixt3): + pass + """ + ) + result = pytester.runpytest("--setup-show") + result.stdout.fnmatch_lines( + [ + "*SETUP F fixt0*", + "*SETUP F fixt1*", + "*SETUP F fixt2 (fixtures used: fixt0, fixt1)*", + "*SETUP F fixt2 (fixtures used: fixt2)*", + "*SETUP F fixt3*", + "*test (fixtures used: fixt0, fixt1, fixt2, fixt3)*", + ] + ) + + +def test_early_teardown_fixture_both_overriden_and_being_used(pytester: Pytester): + pytester.makeconftest( + """ + import pytest + + @pytest.fixture(scope='session') + def shadowed(): + pass + + @pytest.fixture + def intermediary(shadowed): + pass + """ + ) + pytester.makepyfile( + test_a=""" + import pytest + + def test_0(shadowed): + pass + """, + test_b=""" + import pytest + + @pytest.fixture(scope='session') + def shadowed(): + pass + + def test_1(): + pass + + def test_2(shadowed, intermediary): + pass + """, + ) + result = pytester.runpytest("--setup-show") + result.stdout.fnmatch_lines( + [ + "*SETUP S shadowed*", + "*test_0*", + "*TEARDOWN S shadowed*", + "*test_1*", + "*SETUP S shadowed*", + "*SETUP F intermediary*", + "*test_2*", + "*TEARDOWN F intermediary*", + "*TEARDOWN S shadowed*", + ] + ) + + +def test_early_teardown_homonym_session_scoped_fixtures(pytester: Pytester): + """Session-scoped fixtures, as only argname and baseid are active in their + corresponding arg keys.""" + pytester.makepyfile( + test_a=""" + import pytest + @pytest.fixture(scope='session') + def fixture(): + yield 0 + + def test_0(fixture): + assert fixture == 0 + """, + test_b=""" + import pytest + @pytest.fixture(scope='session') + def fixture(): + yield 1 + + def test_1(fixture): + assert fixture == 1 + """, + ) + result = pytester.runpytest("--setup-show") + assert result.ret == 0 + result.stdout.fnmatch_lines( + [ + "*SETUP S fixture*", + "*test_0*", + "*TEARDOWN S fixture*", + "*SETUP S fixture", + "*test_1*", + "*TEARDOWN S fixture*", + ] + ) + + def test_reorder_with_nonparametrized_fixtures(pytester: Pytester): path = pytester.makepyfile( """ @@ -4696,6 +4873,326 @@ def test_reorder_with_both_parametrized_and_nonparametrized_fixtures( result.stdout.fnmatch_lines([f"*test_{i}*" for i in [0, 2, 1]]) +def test_early_teardown_parametrized_homonym_parent_fixture(pytester: Pytester): + pytester.makeconftest( + """ + import pytest + + @pytest.fixture(params=[0, 1], scope='session') + def fixture(request): + pass + """ + ) + pytester.makepyfile( + test_a=""" + import pytest + + @pytest.fixture(scope='session') + def fixture(fixture): + pass + + def test_0(fixture): + pass + + def test_1(): + pass + + def test_2(fixture): + pass + """, + test_b=""" + def test_3(fixture): + pass + + def test_4(): + pass + """, + test_c=""" + import pytest + + @pytest.fixture(scope='session') + def fixture(): + pass + + def test_5(fixture): + pass + """, + ) + result = pytester.runpytest("--setup-show") + result.stdout.re_match_lines( + [ + r"\s*SETUP S fixture\[0\].*", + r"\s*SETUP S fixture.*", + r".*test_0\[0\].*", + r".*test_2\[0\].*", + r"\s*TEARDOWN S fixture.*", + r".*test_3\[0\].*", + r"\s*TEARDOWN S fixture\[0\].*", + r"\s*SETUP S fixture\[1\].*", + r"\s*SETUP S fixture.*", + r".*test_0\[1\].*", + r".*test_2\[1\].*", + r"\s*TEARDOWN S fixture.*", + r".*test_3\[1\].*", + r"\s*TEARDOWN S fixture\[1\].*", + r".*test_1.*", + r".*test_4.*", + r"\s*SETUP S fixture.*", + r".*test_5.*", + r"\s*TEARDOWN S fixture.*", + ] + ) + + +def test_early_teardown_parametrized_homonym_parent_and_child_fixture( + pytester: Pytester, +): + pytester.makeconftest( + """ + import pytest + + @pytest.fixture(params=[0, 1], scope='session') + def fixture(request): + pass + """ + ) + pytester.makepyfile( + test_a=""" + import pytest + + @pytest.fixture(params=[2, 3], scope='session') + def fixture(request, fixture): + pass + + def test_0(fixture): + pass + + def test_1(): + pass + + def test_2(fixture): + pass + """, + test_b=""" + def test_3(fixture): + pass + + def test_4(): + pass + + def test_5(fixture): + pass + """, + ) + result = pytester.runpytest("--setup-show") + result.stdout.re_match_lines( + [ + r"\s*SETUP S fixture.*", + r"\s*SETUP S fixture \(fixtures used: fixture\)\[2\].*", + r".*test_0.*", + r".*test_2.*", + r"\s*TEARDOWN S fixture\[2\].*", + r"\s*TEARDOWN S fixture.*", + r"\s*SETUP S fixture.*", + r"\s*SETUP S fixture \(fixtures used: fixture\)\[3\].*", + r".*test_0.*", + r".*test_2.*", + r"\s*TEARDOWN S fixture\[3\].*", + r"\s*TEARDOWN S fixture.*", + r".*test_1.*", + r"\s*SETUP S fixture\[0\].*", + r".*test_3.*", + r".*test_5.*", + r"\s*TEARDOWN S fixture\[0\].*", + r"\s*SETUP S fixture\[1\].*", + r".*test_3.*", + r".*test_5.*", + r"\s*TEARDOWN S fixture\[1\].*", + r".*test_4.*", + ] + ) + + +def test_early_teardown_parametrized_overriden_and_overrider_fixture( + pytester: Pytester, +): + pytester.makeconftest( + """ + import pytest + + @pytest.fixture(params=[0, 1], scope='session') + def fixture(request): + pass + """ + ) + pytester.makepyfile( + test_a=""" + def test_0(fixture): + pass + + def test_1(): + pass + + def test_2(fixture): + pass + + def test_3(): + pass + """, + test_b=""" + import pytest + + @pytest.fixture(params=[2, 3], scope='session') + def fixture(request): + pass + + def test_4(fixture): + pass + + def test_5(): + pass + + def test_6(fixture): + pass + """, + ) + result = pytester.runpytest("--setup-show") + result.stdout.re_match_lines( + [ + r"\s*SETUP S fixture\[0\].*", + r".*test_0.*", + r".*test_2.*", + r"\s*TEARDOWN S fixture\[0\].*", + r"\s*SETUP S fixture\[1\].*", + r".*test_0.*", + r".*test_2.*", + r"\s*TEARDOWN S fixture\[1\].*", + r".*test_3.*", + r"\s*SETUP S fixture\[2\].*", + r".*test_4.*", + r".*test_6.*", + r"\s*TEARDOWN S fixture\[2\].*", + r"\s*SETUP S fixture\[3\].*", + r".*test_4.*", + r".*test_6.*", + r"\s*TEARDOWN S fixture\[3\].*", + r".*test_5.*", + ] + ) + + +def test_early_teardown_indirectly_parametrized_fixture(pytester: Pytester): + pytester.makeconftest( + """ + import pytest + + @pytest.fixture(params=[0, 1], scope='session') + def fixture(request): + pass + """ + ) + pytester.makepyfile( + test_a=""" + import pytest + + @pytest.fixture(scope='session') + def fixture(request, fixture): + pass + + @pytest.mark.parametrize('fixture', [2, 3], indirect=True) + def test_0(fixture): + pass + + def test_1(): + pass + + def test_2(fixture): + pass + + def test_3(): + pass + + def test_4(fixture): + pass + """, + test_b=""" + def test_5(): + pass + + def test_6(fixture): + pass + """, + ) + result = pytester.runpytest("--setup-show") + result.stdout.re_match_lines( + [ + r"\s*SETUP S fixture.*", + r"\s*SETUP S fixture \(fixtures used: fixture\)\[2\].*", + r".*test_0\[2\].*", + r"\s*TEARDOWN S fixture\[2\].*", + # One might expect conftest::fixture not to tear down here, as test_0[3] will use it afterwards, + # but since conftest::fixture gets parameter although it's not parametrized on test_0, it tears down + # itself as soon as it sees the parameter, which has nothing to do with, has changed from 2 to 3. + # In the future we could change callspec param dict to have the fixture baseid in its key as well to + # satisfy this expectation. + r"\s*TEARDOWN S fixture.*", + r"\s*SETUP S fixture.*", + r"\s*SETUP S fixture \(fixtures used: fixture\)\[3\].*", + r".*test_0\[3\].*", + r"\s*TEARDOWN S fixture\[3\].*", + r"\s*TEARDOWN S fixture.*", + r".*test_1.*", + r"\s*SETUP S fixture\[0\].*", + r"\s*SETUP S fixture.*", + r".*test_2\[0\].*", + r".*test_4\[0\].*", + r"\s*TEARDOWN S fixture.*", + r".*test_6\[0\].*", + r"\s*TEARDOWN S fixture\[0\].*", + r"\s*SETUP S fixture\[1\].*", + r"\s*SETUP S fixture.*", + r".*test_2\[1\].*", + r".*test_4\[1\].*", + r"\s*TEARDOWN S fixture.*", + r".*test_6\[1\].*", + r"\s*TEARDOWN S fixture\[1\].*", + r".*test_3.*", + r".*test_5.*", + ] + ) + + +def test_item_determines_which_def_to_use_not_the_requester(pytester: Pytester): + pytester.makeconftest( + """ + import pytest + + @pytest.fixture + def fixture(): + yield 1 + + @pytest.fixture + def intermediary(fixture): + yield fixture + """ + ) + pytester.makepyfile( + """ + import pytest + + @pytest.fixture + def fixture(): + yield 2 + + def test_0(intermediary): + "If requester(intermediary here) was to determine which fixture to use, we should have had 1" + assert intermediary == 2 + """ + ) + result = pytester.runpytest() + result.ret == 0 + + def test_add_new_test_dependent_on_a_fixuture_and_use_nfplugin(pytester: Pytester): test_module_string = """ import pytest @@ -4765,9 +5262,6 @@ def test_last_dependent_test_on_a_fixture_is_in_last_failed_using_lfplugin( ) -@pytest.mark.xfail( - reason="We do not attempt to tear down early the fixture that is overridden and also is used" -) def test_early_teardown_of_overridden_and_being_used_fixture( pytester: Pytester, ) -> None: @@ -4777,8 +5271,8 @@ def test_early_teardown_of_overridden_and_being_used_fixture( @pytest.fixture(scope='module') def fixture0(): - yield None - print("Tearing down higher-level fixture0") + # This fixture is used by its overrider + pass """ ) pytester.makepyfile( @@ -4787,23 +5281,26 @@ def test_early_teardown_of_overridden_and_being_used_fixture( @pytest.fixture(scope='module') def fixture0(fixture0): - yield None - print("Tearing down lower-level fixture0") + pass def test_0(fixture0): pass def test_1(): - print("Both `fixture0`s should have been torn down") + pass """ ) - result = pytester.runpytest("-s") + result = pytester.runpytest("--setup-show") result.stdout.fnmatch_lines( [ - "*Tearing down lower-level fixture0*", - "*Tearing down higher-level fixture0*", - "*Both `fixture0`s should have been torn down*", - ] + "*SETUP M fixture0*", + "*SETUP M fixture0*", + "*test_0*", + "*TEARDOWN M fixture0*", + "*TEARDOWN M fixture0*", + "*test_1*", + ], + consecutive=True, ) @@ -5010,3 +5507,125 @@ def test_early_teardown_does_not_occur_for_pseudo_fixtures(pytester: Pytester) - import functools assert not any([isinstance(item.teardown, functools.partial) for item in items]) + + +def test_fixture_info_after_dynamic_parametrize(pytester: Pytester) -> None: + pytester.makeconftest( + """ + import pytest + + @pytest.fixture(scope='session', params=[0, 1]) + def fixture1(request): + pass + + @pytest.fixture(scope='session') + def fixture2(fixture1): + pass + + @pytest.fixture(scope='session', params=[2, 3]) + def fixture3(request, fixture2): + pass + """ + ) + pytester.makepyfile( + """ + import pytest + def pytest_generate_tests(metafunc): + metafunc.parametrize("fixture2", [4, 5], scope='session') + + @pytest.fixture(scope='session') + def fixture4(): + pass + + @pytest.fixture(scope='session') + def fixture2(fixture3, fixture4): + pass + + def test(fixture2): + pass + """ + ) + res = pytester.inline_run() + res.assertoutcome(passed=2) + + +def test_reordering_after_dynamic_parametrize(pytester: Pytester): + pytester.makepyfile( + """ + import pytest + + def pytest_generate_tests(metafunc): + if metafunc.definition.name == "test_0": + metafunc.parametrize("fixture2", [0]) + + @pytest.fixture(scope='module') + def fixture1(): + pass + + @pytest.fixture(scope='module') + def fixture2(fixture1): + pass + + def test_0(fixture2): + pass + + def test_1(): + pass + + def test_2(fixture1): + pass + """ + ) + result = pytester.runpytest("--collect-only") + result.stdout.fnmatch_lines( + [ + "*test_0*", + "*test_1*", + "*test_2*", + ], + consecutive=True, + ) + + +def test_dont_recompute_dependency_tree_if_no_dynamic_parametrize( + pytester: Pytester, monkeypatch: MonkeyPatch +): + pytester.makepyfile( + """ + import pytest + + def pytest_generate_tests(metafunc): + if metafunc.definition.name == "test_0": + metafunc.parametrize("fixture", [0]) + + @pytest.fixture(scope='module') + def fixture(): + pass + + def test_0(fixture): + pass + + def test_1(): + pass + + @pytest.mark.parametrize("fixture", [0]) + def test_2(fixture): + pass + + @pytest.mark.parametrize("fixture", [0], indirect=True) + def test_3(fixture): + pass + """ + ) + from _pytest.fixtures import FixtureManager + + with monkeypatch.context() as m: + mocked_function = Mock(wraps=FixtureManager.getfixtureclosure) + m.setattr(FixtureManager, "getfixtureclosure", mocked_function) + pytester.inline_run() + assert len(mocked_function.call_args_list) == 5 + assert mocked_function.call_args_list[0].args[0].nodeid.endswith("test_0") + assert mocked_function.call_args_list[1].args[0].nodeid.endswith("test_0") + assert mocked_function.call_args_list[2].args[0].nodeid.endswith("test_1") + assert mocked_function.call_args_list[3].args[0].nodeid.endswith("test_2") + assert mocked_function.call_args_list[4].args[0].nodeid.endswith("test_3") diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index a1a39b87e..61b5e7c1c 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -35,6 +35,7 @@ class TestMetafunc: # initialization. class FuncFixtureInfoMock: name2fixturedefs = {} + name2num_fixturedefs_used = {} def __init__(self, names): self.names_closure = names @@ -55,6 +56,7 @@ class TestMetafunc: names = getfuncargnames(func) fixtureinfo: Any = FuncFixtureInfoMock(names) definition: Any = DefinitionMock._create(obj=func, _nodeid="mock::nodeid") + definition._fixtureinfo = fixtureinfo definition.session = SessionMock(FixtureManagerMock({})) return python.Metafunc(definition, fixtureinfo, config, _ispytest=True)