Skip to content

Commit cc86d99

Browse files
committed
Adapt to getfixturedefs change in pytest 8.1
The signature of this (private) function will change in the upcoming pytest 8.1 release: pytest-dev/pytest#11785 Additionally, the `iterparentnodeids` function is removed, so copy/pasting it for now. I verified that all tests pass when run against pytest main.
1 parent 8a694ff commit cc86d99

File tree

2 files changed

+62
-8
lines changed

2 files changed

+62
-8
lines changed

src/pytest_bdd/generation.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os.path
66
from typing import TYPE_CHECKING, cast
77

8+
import pytest
89
from _pytest._io import TerminalWriter
910
from mako.lookup import TemplateLookup
1011

@@ -127,9 +128,12 @@ def _find_step_fixturedef(
127128
fixturemanager: FixtureManager, item: Function, step: Step
128129
) -> Sequence[FixtureDef[Any]] | None:
129130
"""Find step fixturedef."""
130-
with inject_fixturedefs_for_step(step=step, fixturemanager=fixturemanager, nodeid=item.nodeid):
131+
with inject_fixturedefs_for_step(step=step, fixturemanager=fixturemanager, node=item):
131132
bdd_name = get_step_fixture_name(step=step)
132-
return fixturemanager.getfixturedefs(bdd_name, item.nodeid)
133+
if hasattr(pytest, "version_tuple") and pytest.version_tuple >= (8, 1):
134+
return fixturemanager.getfixturedefs(bdd_name, item)
135+
else:
136+
return fixturemanager.getfixturedefs(bdd_name, item.nodeid)
133137

134138

135139
def parse_feature_files(paths: list[str], **kwargs: Any) -> tuple[list[Feature], list[ScenarioTemplate], list[Step]]:

src/pytest_bdd/scenario.py

+56-6
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import pytest
2222
from _pytest.fixtures import FixtureDef, FixtureManager, FixtureRequest, call_fixture_func
23-
from _pytest.nodes import iterparentnodeids
2423
from typing_extensions import ParamSpec
2524

2625
from . import exceptions
@@ -43,7 +42,7 @@
4342
ALPHA_REGEX = re.compile(r"^\d+_*")
4443

4544

46-
def find_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, nodeid: str) -> Iterable[FixtureDef[Any]]:
45+
def find_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, node) -> Iterable[FixtureDef[Any]]:
4746
"""Find the fixture defs that can parse a step."""
4847
# happens to be that _arg2fixturedefs is changed during the iteration so we use a copy
4948
fixture_def_by_name = list(fixturemanager._arg2fixturedefs.items())
@@ -60,14 +59,65 @@ def find_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, nodeid
6059
if not match:
6160
continue
6261

63-
if fixturedef not in (fixturemanager.getfixturedefs(fixturename, nodeid) or []):
62+
if hasattr(pytest, "version_tuple") and pytest.version_tuple >= (8, 1):
63+
fixturedefs = fixturemanager.getfixturedefs(fixturename, node)
64+
else:
65+
fixturedefs = fixturemanager.getfixturedefs(fixturename, node.nodeid)
66+
if fixturedef not in (fixturedefs or []):
6467
continue
6568

6669
yield fixturedef
6770

6871

72+
# Function copied from pytest 8.0 (removed in later versions).
73+
def iterparentnodeids(nodeid: str) -> Iterator[str]:
74+
"""Return the parent node IDs of a given node ID, inclusive.
75+
76+
For the node ID
77+
78+
"testing/code/test_excinfo.py::TestFormattedExcinfo::test_repr_source"
79+
80+
the result would be
81+
82+
""
83+
"testing"
84+
"testing/code"
85+
"testing/code/test_excinfo.py"
86+
"testing/code/test_excinfo.py::TestFormattedExcinfo"
87+
"testing/code/test_excinfo.py::TestFormattedExcinfo::test_repr_source"
88+
89+
Note that / components are only considered until the first ::.
90+
"""
91+
SEP = "/"
92+
pos = 0
93+
first_colons: Optional[int] = nodeid.find("::")
94+
if first_colons == -1:
95+
first_colons = None
96+
# The root Session node - always present.
97+
yield ""
98+
# Eagerly consume SEP parts until first colons.
99+
while True:
100+
at = nodeid.find(SEP, pos, first_colons)
101+
if at == -1:
102+
break
103+
if at > 0:
104+
yield nodeid[:at]
105+
pos = at + len(SEP)
106+
# Eagerly consume :: parts.
107+
while True:
108+
at = nodeid.find("::", pos)
109+
if at == -1:
110+
break
111+
if at > 0:
112+
yield nodeid[:at]
113+
pos = at + len("::")
114+
# The node ID itself.
115+
if nodeid:
116+
yield nodeid
117+
118+
69119
@contextlib.contextmanager
70-
def inject_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, nodeid: str) -> Iterator[None]:
120+
def inject_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, node) -> Iterator[None]:
71121
"""Inject fixture definitions that can parse a step.
72122
73123
We fist iterate over all the fixturedefs that can parse the step.
@@ -78,7 +128,7 @@ def inject_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, node
78128
"""
79129
bdd_name = get_step_fixture_name(step=step)
80130

81-
fixturedefs = list(find_fixturedefs_for_step(step=step, fixturemanager=fixturemanager, nodeid=nodeid))
131+
fixturedefs = list(find_fixturedefs_for_step(step=step, fixturemanager=fixturemanager, node=node))
82132

83133
# Sort the fixture definitions by their "path", so that the `bdd_name` fixture will
84134
# respect the fixture scope
@@ -114,7 +164,7 @@ def get_step_function(request, step: Step) -> StepFunctionContext | None:
114164
__tracebackhide__ = True
115165
bdd_name = get_step_fixture_name(step=step)
116166

117-
with inject_fixturedefs_for_step(step=step, fixturemanager=request._fixturemanager, nodeid=request.node.nodeid):
167+
with inject_fixturedefs_for_step(step=step, fixturemanager=request._fixturemanager, node=request.node):
118168
try:
119169
return cast(StepFunctionContext, request.getfixturevalue(bdd_name))
120170
except pytest.FixtureLookupError:

0 commit comments

Comments
 (0)