|
67 | 67 | from _pytest.scope import _ScopeName |
68 | 68 | from _pytest.scope import HIGH_SCOPES |
69 | 69 | from _pytest.scope import Scope |
| 70 | +from _pytest.stash import StashKey |
70 | 71 | from _pytest.warning_types import PytestWarning |
71 | 72 |
|
72 | 73 |
|
|
75 | 76 |
|
76 | 77 |
|
77 | 78 | if TYPE_CHECKING: |
78 | | - from _pytest.nodes import Item |
79 | | - from _pytest.nodes import Node |
80 | 79 | from _pytest.python import CallSpec2 |
81 | 80 | from _pytest.python import Function |
82 | 81 | from _pytest.python import Metafunc |
@@ -424,7 +423,7 @@ def fixturenames(self) -> list[str]: |
424 | 423 |
|
425 | 424 | @property |
426 | 425 | @abc.abstractmethod |
427 | | - def node(self) -> Node: |
| 426 | + def node(self): |
428 | 427 | """Underlying collection node (depends on current request scope).""" |
429 | 428 | raise NotImplementedError() |
430 | 429 |
|
@@ -700,7 +699,7 @@ def _check_scope( |
700 | 699 | pass |
701 | 700 |
|
702 | 701 | @property |
703 | | - def node(self) -> Node: |
| 702 | + def node(self): |
704 | 703 | return self._pyfuncitem |
705 | 704 |
|
706 | 705 | def __repr__(self) -> str: |
@@ -753,7 +752,7 @@ def _scope(self) -> Scope: |
753 | 752 | return self._scope_field |
754 | 753 |
|
755 | 754 | @property |
756 | | - def node(self) -> Node: |
| 755 | + def node(self): |
757 | 756 | scope = self._scope |
758 | 757 | if scope is Scope.Function: |
759 | 758 | # This might also be a non-function Item despite its attribute name. |
@@ -973,6 +972,21 @@ def _get_cached_value( |
973 | 972 | return cached_result[0] |
974 | 973 |
|
975 | 974 |
|
| 975 | +_item_index = StashKey[int]() |
| 976 | + |
| 977 | + |
| 978 | +def _get_item_index(item: nodes.Item) -> int: |
| 979 | + if (index := item.stash.get(_item_index, None)) is not None: |
| 980 | + return index |
| 981 | + |
| 982 | + for index, session_item in enumerate(item.session.items): |
| 983 | + session_item.stash[_item_index] = index |
| 984 | + |
| 985 | + index = item.stash.get(_item_index, None) |
| 986 | + assert index is not None, f"Item {item.nodeid} is not inside its session" |
| 987 | + return index |
| 988 | + |
| 989 | + |
976 | 990 | class FixtureDef(Generic[FixtureValue]): |
977 | 991 | """A container for a fixture definition. |
978 | 992 |
|
@@ -1147,7 +1161,7 @@ def _should_finalize_in_current_item(self, request: SubRequest) -> bool: |
1147 | 1161 |
|
1148 | 1162 | node = request.node |
1149 | 1163 | session = request.session |
1150 | | - current_item_index = session._current_item_index |
| 1164 | + current_item_index = _get_item_index(request._pyfuncitem) |
1151 | 1165 | assert current_item_index is not None |
1152 | 1166 |
|
1153 | 1167 | for nextitem in itertools.islice(session.items, current_item_index + 1, None): |
@@ -1188,7 +1202,7 @@ def _check_cache_hit(self, request: SubRequest, old_cache_key: object) -> None: |
1188 | 1202 | def cache_key(self, request: SubRequest) -> object: |
1189 | 1203 | return getattr(request, "param", None) |
1190 | 1204 |
|
1191 | | - def _cache_key_internal(self, item: Item) -> object: |
| 1205 | + def _cache_key_internal(self, item: nodes.Item) -> object: |
1192 | 1206 | callspec: CallSpec2 | None = getattr(item, "callspec", None) |
1193 | 1207 | if callspec is not None and self.argname in callspec.params: |
1194 | 1208 | return callspec.params[self.argname] |
|
0 commit comments