From e700e667e65a090b54ea5076e25b923fdb30b084 Mon Sep 17 00:00:00 2001 From: Sadra Barikbin Date: Fri, 4 Aug 2023 23:53:12 +0330 Subject: [PATCH 1/8] Do the change --- src/_pytest/fixtures.py | 9 ++++++--- testing/python/fixtures.py | 7 ++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index dd37f8ec3e3..92325405353 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -682,9 +682,12 @@ def __repr__(self) -> str: def _fillfixtures(self) -> None: item = self._pyfuncitem - for argname in item.fixturenames: - if argname not in item.funcargs: - item.funcargs[argname] = self.getfixturevalue(argname) + fixturenames = getattr(item, "fixturenames", self.fixturenames) + initialnames = item._fixtureinfo.initialnames + for argname in fixturenames: + value = self.getfixturevalue(argname) + if argname not in item.funcargs and argname in initialnames: + item.funcargs[argname] = value def addfinalizer(self, finalizer: Callable[[], object]) -> None: self.node.addfinalizer(finalizer) diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 81aa2bcc782..c976b656d42 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -9,7 +9,6 @@ from _pytest.fixtures import deduplicate_names from _pytest.fixtures import TopRequest from _pytest.monkeypatch import MonkeyPatch -from _pytest.pytester import get_public_names from _pytest.pytester import Pytester from _pytest.python import Function @@ -128,8 +127,7 @@ def test_funcarg_basic(self, pytester: Pytester) -> None: assert isinstance(item, Function) # Execute's item's setup, which fills fixtures. item.session._setupstate.setup(item) - del item.funcargs["request"] - assert len(get_public_names(item.funcargs)) == 2 + assert len(item.funcargs) == 2 assert item.funcargs["some"] == "test_func" assert item.funcargs["other"] == 42 @@ -841,8 +839,7 @@ def test_func(something): pass val2 = req.getfixturevalue("other") # see about caching assert val2 == 2 assert item.funcargs["something"] == 1 - assert len(get_public_names(item.funcargs)) == 2 - assert "request" in item.funcargs + assert len(item.funcargs) == 1 def test_request_addfinalizer(self, pytester: Pytester) -> None: item = pytester.getitem( From 3f10d53b8c8694986feb4692b45fe3aa1ee58ab4 Mon Sep 17 00:00:00 2001 From: Sadra Barikbin Date: Wed, 6 Sep 2023 17:53:49 +0330 Subject: [PATCH 2/8] Just raise deprecation warning --- doc/en/example/simple.rst | 2 +- src/_pytest/fixtures.py | 6 ++---- src/_pytest/python.py | 21 ++++++++++++++++++++- testing/python/fixtures.py | 13 ++++++++++--- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 21e5f4a09a5..64420874ebf 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -819,7 +819,7 @@ case we just write some information out to a ``failures`` file: with open("failures", mode, encoding="utf-8") as f: # let's also access a fixture for the fun of it if "tmp_path" in item.fixturenames: - extra = " ({})".format(item.funcargs["tmp_path"]) + extra = " ({})".format(item._request.getfixturevalue("tmp_path")) else: extra = "" diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 92325405353..440116dfa9c 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -683,11 +683,9 @@ def __repr__(self) -> str: def _fillfixtures(self) -> None: item = self._pyfuncitem fixturenames = getattr(item, "fixturenames", self.fixturenames) - initialnames = item._fixtureinfo.initialnames for argname in fixturenames: - value = self.getfixturevalue(argname) - if argname not in item.funcargs and argname in initialnames: - item.funcargs[argname] = value + if argname not in item.funcargs: + item.funcargs[argname] = self.getfixturevalue(argname) def addfinalizer(self, finalizer: Callable[[], object]) -> None: self.node.addfinalizer(finalizer) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 4ca77fbfab9..2a7cd4979fb 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1659,6 +1659,23 @@ def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None: tw.line(indent + line) +class DeprecatingFuncArgs(Dict[str, object]): + def __init__(self, initialnames): + self.initialnames = initialnames + super().__init__() + + def __getitem__(self, key: str) -> object: + if key not in self.initialnames: + warnings.warn( + "Accessing to names other than initialnames i.e., direct args," + " the ones with `usefixture` or the ones with `autouse` through " + "`item.funcargs` is deprecated and will raise `KeyError` from " + "pytest 9. Please use `request.getfixturevalue` instead.", + DeprecationWarning, + ) + return super().__getitem__(key) + + class Function(PyobjMixin, nodes.Item): """Item responsible for setting up and executing a Python test function. @@ -1747,7 +1764,9 @@ def from_parent(cls, parent, **kw): # todo: determine sound type limitations return super().from_parent(parent=parent, **kw) def _initrequest(self) -> None: - self.funcargs: Dict[str, object] = {} + self.funcargs: Dict[str, object] = DeprecatingFuncArgs( + self._fixtureinfo.initialnames + ) self._request = fixtures.TopRequest(self, _ispytest=True) @property diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index c976b656d42..199636ef762 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -9,6 +9,7 @@ from _pytest.fixtures import deduplicate_names from _pytest.fixtures import TopRequest from _pytest.monkeypatch import MonkeyPatch +from _pytest.pytester import get_public_names from _pytest.pytester import Pytester from _pytest.python import Function @@ -121,15 +122,20 @@ def test_detect_recursive_dependency_error(self, pytester: Pytester) -> None: ["*recursive dependency involving fixture 'fix1' detected*"] ) - def test_funcarg_basic(self, pytester: Pytester) -> None: + def test_funcarg_basic(self, recwarn, pytester: Pytester) -> None: pytester.copy_example() item = pytester.getitem(Path("test_funcarg_basic.py")) assert isinstance(item, Function) # Execute's item's setup, which fills fixtures. item.session._setupstate.setup(item) - assert len(item.funcargs) == 2 + assert len(recwarn) == 0 + item.funcargs["request"] + assert len(recwarn) == 1 and recwarn[0].category is DeprecationWarning + del item.funcargs["request"] + assert len(get_public_names(item.funcargs)) == 2 assert item.funcargs["some"] == "test_func" assert item.funcargs["other"] == 42 + assert len(recwarn) == 1 def test_funcarg_lookup_modulelevel(self, pytester: Pytester) -> None: pytester.copy_example() @@ -839,7 +845,8 @@ def test_func(something): pass val2 = req.getfixturevalue("other") # see about caching assert val2 == 2 assert item.funcargs["something"] == 1 - assert len(item.funcargs) == 1 + assert len(get_public_names(item.funcargs)) == 2 + assert "request" in item.funcargs def test_request_addfinalizer(self, pytester: Pytester) -> None: item = pytester.getitem( From fba008cf29ebf03049f1116f7296b100c50c5feb Mon Sep 17 00:00:00 2001 From: Sadra Barikbin Date: Sun, 17 Sep 2023 09:54:15 +0330 Subject: [PATCH 3/8] Apply review comments --- doc/en/example/simple.rst | 4 +- src/_pytest/deprecated.py | 8 ++++ src/_pytest/python.py | 14 +++--- testing/deprecated_test.py | 90 ++++++++++++++++++++++++++++++++++++++ testing/python/fixtures.py | 6 +-- 5 files changed, 106 insertions(+), 16 deletions(-) diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 64420874ebf..c908cbe3ac1 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -818,8 +818,8 @@ case we just write some information out to a ``failures`` file: mode = "a" if os.path.exists("failures") else "w" with open("failures", mode, encoding="utf-8") as f: # let's also access a fixture for the fun of it - if "tmp_path" in item.fixturenames: - extra = " ({})".format(item._request.getfixturevalue("tmp_path")) + if "tmp_path" in item.funcargs: + extra = " ({})".format(item.funcargs["tmp_path"]) else: extra = "" diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 1bc2cf57e80..c513675a792 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -11,6 +11,7 @@ from warnings import warn from _pytest.warning_types import PytestDeprecationWarning +from _pytest.warning_types import PytestRemovedIn8Warning from _pytest.warning_types import PytestRemovedIn9Warning from _pytest.warning_types import UnformattedWarning @@ -48,6 +49,13 @@ "See docs: https://docs.pytest.org/en/stable/deprecations.html#applying-a-mark-to-a-fixture-function" ) +ITEM_FUNCARGS_MEMBERS = PytestRemovedIn9Warning( + "Access to names other than initialnames i.e., direct args," + " the ones with `usefixture` or the ones with `autouse` through " + "`item.funcargs` is deprecated and will raise `KeyError` from " + "pytest 9. Please use `request.getfixturevalue` instead." +) + # You want to make some `__init__` or function "private". # # def my_private_function(some, args): diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 2a7cd4979fb..52f4a888139 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -15,6 +15,7 @@ from typing import Any from typing import Callable from typing import Dict +from typing import Final from typing import final from typing import Generator from typing import Iterable @@ -55,6 +56,7 @@ from _pytest.config import hookimpl from _pytest.config.argparsing import Parser from _pytest.deprecated import check_ispytest +from _pytest.deprecated import ITEM_FUNCARGS_MEMBERS from _pytest.fixtures import FixtureDef from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FuncFixtureInfo @@ -1660,19 +1662,13 @@ def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None: class DeprecatingFuncArgs(Dict[str, object]): - def __init__(self, initialnames): - self.initialnames = initialnames + def __init__(self, initialnames: Sequence[str]) -> None: super().__init__() + self.initialnames: Final = initialnames def __getitem__(self, key: str) -> object: if key not in self.initialnames: - warnings.warn( - "Accessing to names other than initialnames i.e., direct args," - " the ones with `usefixture` or the ones with `autouse` through " - "`item.funcargs` is deprecated and will raise `KeyError` from " - "pytest 9. Please use `request.getfixturevalue` instead.", - DeprecationWarning, - ) + warnings.warn(ITEM_FUNCARGS_MEMBERS) return super().__getitem__(key) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index ebff49ce6d9..36b878df99c 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -133,3 +133,93 @@ def foo(): raise NotImplementedError() assert len(record) == 2 # one for each mark decorator + + +@pytest.mark.filterwarnings("default") +def test_nose_deprecated_with_setup(pytester: Pytester) -> None: + pytest.importorskip("nose") + pytester.makepyfile( + """ + from nose.tools import with_setup + + def setup_fn_no_op(): + ... + + def teardown_fn_no_op(): + ... + + @with_setup(setup_fn_no_op, teardown_fn_no_op) + def test_omits_warnings(): + ... + """ + ) + 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)", + "*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: `teardown_fn_no_op` (teardown)", + ] + output.stdout.fnmatch_lines(message) + output.assert_outcomes(passed=1) + + +@pytest.mark.filterwarnings("default") +def test_nose_deprecated_setup_teardown(pytester: Pytester) -> None: + pytest.importorskip("nose") + pytester.makepyfile( + """ + class Test: + + def setup(self): + ... + + def teardown(self): + ... + + def test(self): + ... + """ + ) + 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)`", + "*To remove this warning, rename it to `setup_method(self)`", + "*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: `teardown(self)`", + "*To remove this warning, rename it to `teardown_method(self)`", + ] + output.stdout.fnmatch_lines(message) + output.assert_outcomes(passed=1) + + +def test_deprecated_access_to_item_funcargs(pytester: Pytester) -> None: + module = pytester.makepyfile( + """ + import pytest + + @pytest.fixture + def fixture1(): + return None + + @pytest.fixture + def fixture2(fixture1): + return None + + def test(fixture2): + pass + """ + ) + test = pytester.genitems((pytester.getmodulecol(module),))[0] + assert isinstance(test, pytest.Function) + test.session._setupstate.setup(test) + test.setup() + with pytest.warns( + pytest.PytestRemovedIn9Warning, + match=r"Access to names other than initialnames", + ) as record: + test.funcargs["fixture1"] + assert len(record) == 1 + test.funcargs["fixture2"] + assert len(record) == 1 diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 199636ef762..81aa2bcc782 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -122,20 +122,16 @@ def test_detect_recursive_dependency_error(self, pytester: Pytester) -> None: ["*recursive dependency involving fixture 'fix1' detected*"] ) - def test_funcarg_basic(self, recwarn, pytester: Pytester) -> None: + def test_funcarg_basic(self, pytester: Pytester) -> None: pytester.copy_example() item = pytester.getitem(Path("test_funcarg_basic.py")) assert isinstance(item, Function) # Execute's item's setup, which fills fixtures. item.session._setupstate.setup(item) - assert len(recwarn) == 0 - item.funcargs["request"] - assert len(recwarn) == 1 and recwarn[0].category is DeprecationWarning del item.funcargs["request"] assert len(get_public_names(item.funcargs)) == 2 assert item.funcargs["some"] == "test_func" assert item.funcargs["other"] == 42 - assert len(recwarn) == 1 def test_funcarg_lookup_modulelevel(self, pytester: Pytester) -> None: pytester.copy_example() From 3a88adb3202c3fb230b485280e75eb716916d150 Mon Sep 17 00:00:00 2001 From: Sadra Barikbin Date: Sun, 17 Sep 2023 10:31:17 +0330 Subject: [PATCH 4/8] Add changelog and note --- changelog/11284.deprecation.rst | 3 +++ doc/en/deprecations.rst | 14 ++++++++++++++ src/_pytest/deprecated.py | 8 ++++---- testing/deprecated_test.py | 2 +- 4 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 changelog/11284.deprecation.rst diff --git a/changelog/11284.deprecation.rst b/changelog/11284.deprecation.rst new file mode 100644 index 00000000000..12bcbdc569c --- /dev/null +++ b/changelog/11284.deprecation.rst @@ -0,0 +1,3 @@ +Accessing ``item.funcargs`` with fixture names other than the direct ones, i.e. the direct args, the ones with ``autouse`` and the ones with ``usefixtures`` issues a warning. + +This will become an error in pytest 9. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 76cc3482ad3..c7c04f85914 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -278,6 +278,20 @@ The accompanying ``py.path.local`` based paths have been deprecated: plugins whi resolved in future versions as we slowly get rid of the :pypi:`py` dependency (see :issue:`9283` for a longer discussion). +.. _item-funcargs-deprecation: + +Accessing ``item.funcargs`` with not directly requested fixture names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 8.0 +.. versionremoved:: 9.0 + +Accessing ``item.funcargs`` with not directly requested fixture names issues warning and +will be erroneous starting from pytest 9. Directly requested fixtures are the direct args +to the test, the ``usefixtures`` fixtures and the ``autouse`` ones. + +To request fixtures other than the directly requested ones, user could use +``request.getfixturevalue`` instead. .. _nose-deprecation: diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index c513675a792..aae2777058d 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -50,10 +50,10 @@ ) ITEM_FUNCARGS_MEMBERS = PytestRemovedIn9Warning( - "Access to names other than initialnames i.e., direct args," - " the ones with `usefixture` or the ones with `autouse` through " - "`item.funcargs` is deprecated and will raise `KeyError` from " - "pytest 9. Please use `request.getfixturevalue` instead." + "Accessing `item.funcargs` with a fixture name not directly requested" + " by the item, through a direct argument, `usefixtures` marker or" + " an `autouse` fixture, is deprecated and will raise KeyError starting" + " from pytest 9. Please use request.getfixturevalue instead." ) # You want to make some `__init__` or function "private". diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 36b878df99c..56fee570a22 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -217,7 +217,7 @@ def test(fixture2): test.setup() with pytest.warns( pytest.PytestRemovedIn9Warning, - match=r"Access to names other than initialnames", + match=r"Accessing `item.funcargs` with a fixture", ) as record: test.funcargs["fixture1"] assert len(record) == 1 From 2b41123004ae907aea4bdba50e0447c7b9218b50 Mon Sep 17 00:00:00 2001 From: Sadra Barikbin Date: Sun, 17 Sep 2023 16:59:23 +0330 Subject: [PATCH 5/8] A tiny improvement --- src/_pytest/python.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 52f4a888139..ca679ab7367 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1668,7 +1668,7 @@ def __init__(self, initialnames: Sequence[str]) -> None: def __getitem__(self, key: str) -> object: if key not in self.initialnames: - warnings.warn(ITEM_FUNCARGS_MEMBERS) + warnings.warn(ITEM_FUNCARGS_MEMBERS, stacklevel=2) return super().__getitem__(key) From 1de56fc344bdcf657f545a246e732a15ec19f176 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 14 Jan 2024 18:14:16 +0000 Subject: [PATCH 6/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/deprecated.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index aae2777058d..55cddc4a854 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -11,7 +11,6 @@ from warnings import warn from _pytest.warning_types import PytestDeprecationWarning -from _pytest.warning_types import PytestRemovedIn8Warning from _pytest.warning_types import PytestRemovedIn9Warning from _pytest.warning_types import UnformattedWarning From 56e95333546f349c3fa85818ed99a9d64e6ca529 Mon Sep 17 00:00:00 2001 From: Sadra Barikbin Date: Sun, 14 Jan 2024 23:53:10 +0330 Subject: [PATCH 7/8] Apply all but one review comments --- doc/en/deprecations.rst | 13 +++++------- src/_pytest/assertion/rewrite.py | 7 ++++--- src/_pytest/doctest.py | 5 ++++- src/_pytest/python.py | 4 +++- testing/deprecated_test.py | 28 ++++++++++++------------- testing/test_doctest.py | 35 ++++++++++++++++++++++++++++++++ 6 files changed, 64 insertions(+), 28 deletions(-) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index c7c04f85914..c1c644d8c7f 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -280,18 +280,15 @@ The accompanying ``py.path.local`` based paths have been deprecated: plugins whi .. _item-funcargs-deprecation: -Accessing ``item.funcargs`` with not directly requested fixture names +Accessing ``item.funcargs`` with non-directly requested fixture names ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. deprecated:: 8.0 -.. versionremoved:: 9.0 +.. versionremoved:: 8.1 -Accessing ``item.funcargs`` with not directly requested fixture names issues warning and -will be erroneous starting from pytest 9. Directly requested fixtures are the direct args -to the test, the ``usefixtures`` fixtures and the ``autouse`` ones. +Accessing ``item.funcargs`` with non-directly requested fixture names issues a warning and will be erroneous starting from pytest 9. +Directly requested fixtures are the direct arguments to the test, ``usefixtures`` fixtures and ``autouse`` fixtures. -To request fixtures other than the directly requested ones, user could use -``request.getfixturevalue`` instead. +To request a fixture other than the directly requested ones, use :func:`request.getfixturevalue ` instead. .. _nose-deprecation: diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 149101e716f..b1a644cbfec 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -17,6 +17,7 @@ from pathlib import Path from pathlib import PurePath from typing import Callable +from typing import DefaultDict from typing import Dict from typing import IO from typing import Iterable @@ -668,9 +669,9 @@ def __init__( else: self.enable_assertion_pass_hook = False self.source = source - self.scope: tuple[ast.AST, ...] = () - self.variables_overwrite: defaultdict[ - tuple[ast.AST, ...], Dict[str, str] + self.scope: Tuple[ast.AST, ...] = () + self.variables_overwrite: DefaultDict[ + Tuple[ast.AST, ...], Dict[str, str] ] = defaultdict(dict) def run(self, mod: ast.Module) -> None: diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 4ce32a298e8..a555334365d 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -40,6 +40,7 @@ from _pytest.outcomes import skip from _pytest.pathlib import fnmatch_ex from _pytest.pathlib import import_path +from _pytest.python import DeprecatingFuncArgs from _pytest.python import Module from _pytest.python_api import approx from _pytest.warning_types import PytestWarning @@ -284,7 +285,9 @@ def from_parent( # type: ignore return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest) def _initrequest(self) -> None: - self.funcargs: Dict[str, object] = {} + self.funcargs: Dict[str, object] = DeprecatingFuncArgs( + self._fixtureinfo.initialnames + ) self._request = TopRequest(self, _ispytest=True) # type: ignore[arg-type] def setup(self) -> None: diff --git a/src/_pytest/python.py b/src/_pytest/python.py index ca679ab7367..5d9aa9655a7 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1664,10 +1664,12 @@ def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None: class DeprecatingFuncArgs(Dict[str, object]): def __init__(self, initialnames: Sequence[str]) -> None: super().__init__() + self.warned: bool = False self.initialnames: Final = initialnames def __getitem__(self, key: str) -> object: - if key not in self.initialnames: + if not self.warned and key not in self.initialnames: + self.warned = True warnings.warn(ITEM_FUNCARGS_MEMBERS, stacklevel=2) return super().__getitem__(key) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 56fee570a22..8e201fce55b 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -195,7 +195,7 @@ def test(self): def test_deprecated_access_to_item_funcargs(pytester: Pytester) -> None: - module = pytester.makepyfile( + pytester.makepyfile( """ import pytest @@ -207,19 +207,17 @@ def fixture1(): def fixture2(fixture1): return None - def test(fixture2): - pass + def test(request, fixture2): + with pytest.warns( + pytest.PytestRemovedIn9Warning, + match=r"Accessing `item.funcargs` with a fixture", + ) as record: + request.node.funcargs["fixture1"] + assert request.node.funcargs.warned + request.node.funcargs.warned = False + request.node.funcargs["fixture2"] + assert len(record) == 1 """ ) - test = pytester.genitems((pytester.getmodulecol(module),))[0] - assert isinstance(test, pytest.Function) - test.session._setupstate.setup(test) - test.setup() - with pytest.warns( - pytest.PytestRemovedIn9Warning, - match=r"Accessing `item.funcargs` with a fixture", - ) as record: - test.funcargs["fixture1"] - assert len(record) == 1 - test.funcargs["fixture2"] - assert len(record) == 1 + output = pytester.runpytest() + output.assert_outcomes(passed=1) diff --git a/testing/test_doctest.py b/testing/test_doctest.py index f4d3155c435..28b3d65c7ef 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -881,6 +881,41 @@ def test_foo(): result = pytester.runpytest(p, "--doctest-modules") result.stdout.fnmatch_lines(["*collected 1 item*"]) + def test_deprecated_access_to_item_funcargs(self, pytester: Pytester): + pytester.makeconftest( + """ + import pytest + + @pytest.fixture + def fixture1(): + return None + + @pytest.fixture(autouse=True) + def fixture2(fixture1): + return None + """ + ) + pytester.makepyfile( + """ + ''' + >>> import pytest + >>> request = getfixture('request') + >>> with pytest.warns( + ... pytest.PytestRemovedIn9Warning, + ... match=r"Accessing `item.funcargs` with a fixture", + ... ) as record: + ... request.node.funcargs["fixture1"] + ... assert request.node.funcargs.warned + ... request.node.funcargs.warned = False + ... request.node.funcargs["fixture2"] + >>> len(record) + 1 + ''' + """ + ) + result = pytester.runpytest("--doctest-modules") + result.assert_outcomes(passed=1) + class TestLiterals: @pytest.mark.parametrize("config_mode", ["ini", "comment"]) From 93e9dafc757c375c883bbbe1a7ddf9dd3a250a9d Mon Sep 17 00:00:00 2001 From: Sadra Barikbin Date: Tue, 16 Jan 2024 21:22:53 +0330 Subject: [PATCH 8/8] Remove redundant tests --- testing/deprecated_test.py | 59 -------------------------------------- 1 file changed, 59 deletions(-) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 8e201fce55b..6d1b79e1cf4 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -135,65 +135,6 @@ def foo(): assert len(record) == 2 # one for each mark decorator -@pytest.mark.filterwarnings("default") -def test_nose_deprecated_with_setup(pytester: Pytester) -> None: - pytest.importorskip("nose") - pytester.makepyfile( - """ - from nose.tools import with_setup - - def setup_fn_no_op(): - ... - - def teardown_fn_no_op(): - ... - - @with_setup(setup_fn_no_op, teardown_fn_no_op) - def test_omits_warnings(): - ... - """ - ) - 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)", - "*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: `teardown_fn_no_op` (teardown)", - ] - output.stdout.fnmatch_lines(message) - output.assert_outcomes(passed=1) - - -@pytest.mark.filterwarnings("default") -def test_nose_deprecated_setup_teardown(pytester: Pytester) -> None: - pytest.importorskip("nose") - pytester.makepyfile( - """ - class Test: - - def setup(self): - ... - - def teardown(self): - ... - - def test(self): - ... - """ - ) - 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)`", - "*To remove this warning, rename it to `setup_method(self)`", - "*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: `teardown(self)`", - "*To remove this warning, rename it to `teardown_method(self)`", - ] - output.stdout.fnmatch_lines(message) - output.assert_outcomes(passed=1) - - def test_deprecated_access_to_item_funcargs(pytester: Pytester) -> None: pytester.makepyfile( """