Skip to content

Commit 48fb989

Browse files
committed
runner: avoid using node's store in SetupState
SetupState maintains its own state, so it can store the exception itself, instead of using the node's store, which is better avoided when possible. This also reduces the lifetime of the reference-cycle-inducing exception objects which is never a bad thing.
1 parent d5df8f9 commit 48fb989

File tree

1 file changed

+14
-11
lines changed

1 file changed

+14
-11
lines changed

src/_pytest/runner.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
from _pytest.outcomes import OutcomeException
3737
from _pytest.outcomes import Skipped
3838
from _pytest.outcomes import TEST_OUTCOME
39-
from _pytest.store import StoreKey
4039

4140
if TYPE_CHECKING:
4241
from typing_extensions import Literal
@@ -467,29 +466,33 @@ class SetupState:
467466
"""
468467

469468
def __init__(self) -> None:
470-
# Maps node -> the node's finalizers.
471469
# The stack is in the dict insertion order.
472-
self.stack: Dict[Node, List[Callable[[], object]]] = {}
473-
474-
_prepare_exc_key = StoreKey[Union[OutcomeException, Exception]]()
470+
self.stack: Dict[
471+
Node,
472+
Tuple[
473+
# Node's finalizers.
474+
List[Callable[[], object]],
475+
# Node's exception, if its setup raised.
476+
Optional[Union[OutcomeException, Exception]],
477+
],
478+
] = {}
475479

476480
def prepare(self, item: Item) -> None:
477481
"""Setup objects along the collector chain to the item."""
478482
# If a collector fails its setup, fail its entire subtree of items.
479483
# The setup is not retried for each item - the same exception is used.
480-
for col in self.stack:
481-
prepare_exc = col._store.get(self._prepare_exc_key, None)
484+
for col, (finalizers, prepare_exc) in self.stack.items():
482485
if prepare_exc:
483486
raise prepare_exc
484487

485488
needed_collectors = item.listchain()
486489
for col in needed_collectors[len(self.stack) :]:
487490
assert col not in self.stack
488-
self.stack[col] = [col.teardown]
491+
self.stack[col] = ([col.teardown], None)
489492
try:
490493
col.setup()
491494
except TEST_OUTCOME as e:
492-
col._store[self._prepare_exc_key] = e
495+
self.stack[col] = (self.stack[col][0], e)
493496
raise e
494497

495498
def addfinalizer(self, finalizer: Callable[[], object], node: Node) -> None:
@@ -500,7 +503,7 @@ def addfinalizer(self, finalizer: Callable[[], object], node: Node) -> None:
500503
assert node and not isinstance(node, tuple)
501504
assert callable(finalizer)
502505
assert node in self.stack, (node, self.stack)
503-
self.stack[node].append(finalizer)
506+
self.stack[node][0].append(finalizer)
504507

505508
def teardown_exact(self, nextitem: Optional[Item]) -> None:
506509
"""Teardown the current stack up until reaching nodes that nextitem
@@ -514,7 +517,7 @@ def teardown_exact(self, nextitem: Optional[Item]) -> None:
514517
while self.stack:
515518
if list(self.stack.keys()) == needed_collectors[: len(self.stack)]:
516519
break
517-
node, finalizers = self.stack.popitem()
520+
node, (finalizers, prepare_exc) = self.stack.popitem()
518521
while finalizers:
519522
fin = finalizers.pop()
520523
try:

0 commit comments

Comments
 (0)