Merge f8f34b48d8
into 20b18f0f9a
This commit is contained in:
commit
fe06dc0c4b
|
@ -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.
|
|
@ -274,6 +274,17 @@ 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 non-directly requested fixture names
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 8.1
|
||||
|
||||
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 a fixture other than the directly requested ones, use :func:`request.getfixturevalue <pytest.FixtureRequest.getfixturevalue>` instead.
|
||||
|
||||
.. _nose-deprecation:
|
||||
|
||||
|
|
|
@ -843,7 +843,7 @@ 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:
|
||||
if "tmp_path" in item.funcargs:
|
||||
extra = " ({})".format(item.funcargs["tmp_path"])
|
||||
else:
|
||||
extra = ""
|
||||
|
|
|
@ -18,6 +18,7 @@ import sys
|
|||
import tokenize
|
||||
import types
|
||||
from typing import Callable
|
||||
from typing import DefaultDict
|
||||
from typing import Dict
|
||||
from typing import IO
|
||||
from typing import Iterable
|
||||
|
@ -671,9 +672,9 @@ class AssertionRewriter(ast.NodeVisitor):
|
|||
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:
|
||||
|
|
|
@ -50,6 +50,13 @@ MARKED_FIXTURE = PytestRemovedIn9Warning(
|
|||
"See docs: https://docs.pytest.org/en/stable/deprecations.html#applying-a-mark-to-a-fixture-function"
|
||||
)
|
||||
|
||||
ITEM_FUNCARGS_MEMBERS = PytestRemovedIn9Warning(
|
||||
"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".
|
||||
#
|
||||
# def my_private_function(some, args):
|
||||
|
|
|
@ -41,6 +41,7 @@ from _pytest.outcomes import OutcomeException
|
|||
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
|
||||
|
@ -286,7 +287,9 @@ class DoctestItem(Item):
|
|||
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:
|
||||
|
|
|
@ -679,7 +679,8 @@ class TopRequest(FixtureRequest):
|
|||
|
||||
def _fillfixtures(self) -> None:
|
||||
item = self._pyfuncitem
|
||||
for argname in item.fixturenames:
|
||||
fixturenames = getattr(item, "fixturenames", self.fixturenames)
|
||||
for argname in fixturenames:
|
||||
if argname not in item.funcargs:
|
||||
item.funcargs[argname] = self.getfixturevalue(argname)
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import types
|
|||
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
|
||||
|
@ -56,6 +57,7 @@ from _pytest.config import ExitCode
|
|||
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
|
||||
|
@ -1650,6 +1652,19 @@ def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None:
|
|||
tw.line(indent + line)
|
||||
|
||||
|
||||
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 not self.warned and key not in self.initialnames:
|
||||
self.warned = True
|
||||
warnings.warn(ITEM_FUNCARGS_MEMBERS, stacklevel=2)
|
||||
return super().__getitem__(key)
|
||||
|
||||
|
||||
class Function(PyobjMixin, nodes.Item):
|
||||
"""Item responsible for setting up and executing a Python test function.
|
||||
|
||||
|
@ -1738,7 +1753,9 @@ class Function(PyobjMixin, nodes.Item):
|
|||
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
|
||||
|
|
|
@ -134,3 +134,32 @@ def test_fixture_disallowed_between_marks():
|
|||
raise NotImplementedError()
|
||||
|
||||
assert len(record) == 2 # one for each mark decorator
|
||||
|
||||
|
||||
def test_deprecated_access_to_item_funcargs(pytester: Pytester) -> None:
|
||||
pytester.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def fixture1():
|
||||
return None
|
||||
|
||||
@pytest.fixture
|
||||
def fixture2(fixture1):
|
||||
return None
|
||||
|
||||
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
|
||||
"""
|
||||
)
|
||||
output = pytester.runpytest()
|
||||
output.assert_outcomes(passed=1)
|
||||
|
|
|
@ -878,6 +878,41 @@ class TestDoctests:
|
|||
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"])
|
||||
|
|
Loading…
Reference in New Issue