20
20
21
21
import pytest
22
22
from _pytest .fixtures import FixtureDef , FixtureManager , FixtureRequest , call_fixture_func
23
- from _pytest .nodes import iterparentnodeids
24
23
from typing_extensions import ParamSpec
25
24
26
25
from . import exceptions
26
+ from .compat import getfixturedefs
27
27
from .feature import get_feature , get_features
28
28
from .steps import StepFunctionContext , get_step_fixture_name , inject_fixture
29
29
from .utils import CONFIG_STACK , get_args , get_caller_module_locals , get_caller_module_path
30
30
31
31
if TYPE_CHECKING :
32
32
from _pytest .mark .structures import ParameterSet
33
+ from _pytest .nodes import Node
33
34
34
35
from .parser import Feature , Scenario , ScenarioTemplate , Step
35
36
43
44
ALPHA_REGEX = re .compile (r"^\d+_*" )
44
45
45
46
46
- def find_fixturedefs_for_step (step : Step , fixturemanager : FixtureManager , nodeid : str ) -> Iterable [FixtureDef [Any ]]:
47
+ def find_fixturedefs_for_step (step : Step , fixturemanager : FixtureManager , node : Node ) -> Iterable [FixtureDef [Any ]]:
47
48
"""Find the fixture defs that can parse a step."""
48
49
# happens to be that _arg2fixturedefs is changed during the iteration so we use a copy
49
50
fixture_def_by_name = list (fixturemanager ._arg2fixturedefs .items ())
@@ -60,14 +61,62 @@ def find_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, nodeid
60
61
if not match :
61
62
continue
62
63
63
- if fixturedef not in (fixturemanager .getfixturedefs (fixturename , nodeid ) or []):
64
+ fixturedefs = getfixturedefs (fixturemanager , fixturename , node )
65
+ if fixturedef not in (fixturedefs or []):
64
66
continue
65
67
66
68
yield fixturedef
67
69
68
70
71
+ # Function copied from pytest 8.0 (removed in later versions).
72
+ def iterparentnodeids (nodeid : str ) -> Iterator [str ]:
73
+ """Return the parent node IDs of a given node ID, inclusive.
74
+
75
+ For the node ID
76
+
77
+ "testing/code/test_excinfo.py::TestFormattedExcinfo::test_repr_source"
78
+
79
+ the result would be
80
+
81
+ ""
82
+ "testing"
83
+ "testing/code"
84
+ "testing/code/test_excinfo.py"
85
+ "testing/code/test_excinfo.py::TestFormattedExcinfo"
86
+ "testing/code/test_excinfo.py::TestFormattedExcinfo::test_repr_source"
87
+
88
+ Note that / components are only considered until the first ::.
89
+ """
90
+ SEP = "/"
91
+ pos = 0
92
+ first_colons : Optional [int ] = nodeid .find ("::" )
93
+ if first_colons == - 1 :
94
+ first_colons = None
95
+ # The root Session node - always present.
96
+ yield ""
97
+ # Eagerly consume SEP parts until first colons.
98
+ while True :
99
+ at = nodeid .find (SEP , pos , first_colons )
100
+ if at == - 1 :
101
+ break
102
+ if at > 0 :
103
+ yield nodeid [:at ]
104
+ pos = at + len (SEP )
105
+ # Eagerly consume :: parts.
106
+ while True :
107
+ at = nodeid .find ("::" , pos )
108
+ if at == - 1 :
109
+ break
110
+ if at > 0 :
111
+ yield nodeid [:at ]
112
+ pos = at + len ("::" )
113
+ # The node ID itself.
114
+ if nodeid :
115
+ yield nodeid
116
+
117
+
69
118
@contextlib .contextmanager
70
- def inject_fixturedefs_for_step (step : Step , fixturemanager : FixtureManager , nodeid : str ) -> Iterator [None ]:
119
+ def inject_fixturedefs_for_step (step : Step , fixturemanager : FixtureManager , node : Node ) -> Iterator [None ]:
71
120
"""Inject fixture definitions that can parse a step.
72
121
73
122
We fist iterate over all the fixturedefs that can parse the step.
@@ -78,7 +127,7 @@ def inject_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, node
78
127
"""
79
128
bdd_name = get_step_fixture_name (step = step )
80
129
81
- fixturedefs = list (find_fixturedefs_for_step (step = step , fixturemanager = fixturemanager , nodeid = nodeid ))
130
+ fixturedefs = list (find_fixturedefs_for_step (step = step , fixturemanager = fixturemanager , node = node ))
82
131
83
132
# Sort the fixture definitions by their "path", so that the `bdd_name` fixture will
84
133
# respect the fixture scope
@@ -114,7 +163,7 @@ def get_step_function(request, step: Step) -> StepFunctionContext | None:
114
163
__tracebackhide__ = True
115
164
bdd_name = get_step_fixture_name (step = step )
116
165
117
- with inject_fixturedefs_for_step (step = step , fixturemanager = request ._fixturemanager , nodeid = request .node . nodeid ):
166
+ with inject_fixturedefs_for_step (step = step , fixturemanager = request ._fixturemanager , node = request .node ):
118
167
try :
119
168
return cast (StepFunctionContext , request .getfixturevalue (bdd_name ))
120
169
except pytest .FixtureLookupError :
0 commit comments