@@ -403,23 +403,86 @@ def pytest_make_collect_report(collector: Collector) -> CollectReport:
403
403
404
404
405
405
class SetupState :
406
- """Shared state for setting up/tearing down test items or collectors."""
406
+ """Shared state for setting up/tearing down test items or collectors
407
+ in a session.
408
+
409
+ Suppose we have a collection tree as follows:
410
+
411
+ <Session session>
412
+ <Module mod1>
413
+ <Function item1>
414
+ <Module mod2>
415
+ <Function item2>
416
+
417
+ The SetupState maintains a stack. The stack starts out empty:
418
+
419
+ []
420
+
421
+ During the setup phase of item1, prepare(item1) is called. What it does
422
+ is:
423
+
424
+ push session to stack, run session.setup()
425
+ push mod1 to stack, run mod1.setup()
426
+ push item1 to stack, run item1.setup()
427
+
428
+ The stack is:
429
+
430
+ [session, mod1, item1]
431
+
432
+ While the stack is in this shape, it is allowed to add finalizers to
433
+ each of session, mod1, item1 using addfinalizer().
434
+
435
+ During the teardown phase of item1, teardown_exact(item2) is called,
436
+ where item2 is the next item to item1. What it does is:
437
+
438
+ pop item1 from stack, run its teardowns
439
+ pop mod1 from stack, run its teardowns
440
+
441
+ mod1 was popped because it ended its purpose with item1. The stack is:
442
+
443
+ [session]
444
+
445
+ During the setup phase of item2, prepare(item2) is called. What it does
446
+ is:
447
+
448
+ push mod2 to stack, run mod2.setup()
449
+ push item2 to stack, run item2.setup()
450
+
451
+ Stack:
452
+
453
+ [session, mod2, item2]
454
+
455
+ During the teardown phase of item2, teardown_exact(None) is called,
456
+ because item2 is the last item. What it does is:
457
+
458
+ pop item2 from stack, run its teardowns
459
+ pop mod2 from stack, run its teardowns
460
+ pop session from stack, run its teardowns
461
+
462
+ Stack:
463
+
464
+ []
465
+
466
+ The end!
467
+ """
407
468
408
469
def __init__ (self ) -> None :
470
+ # Maps node -> the node's finalizers.
471
+ # The stack is in the dict insertion order.
409
472
self .stack : Dict [Node , List [Callable [[], object ]]] = {}
410
473
411
474
_prepare_exc_key = StoreKey [Union [OutcomeException , Exception ]]()
412
475
413
- def prepare (self , colitem : Item ) -> None :
414
- """Setup objects along the collector chain to the test-method ."""
415
-
416
- # Check if the last collection node has raised an error .
476
+ def prepare (self , item : Item ) -> None :
477
+ """Setup objects along the collector chain to the item ."""
478
+ # If a collector fails its setup, fail its entire subtree of items.
479
+ # The setup is not retried for each item - the same exception is used .
417
480
for col in self .stack :
418
481
prepare_exc = col ._store .get (self ._prepare_exc_key , None )
419
482
if prepare_exc :
420
483
raise prepare_exc
421
484
422
- needed_collectors = colitem .listchain ()
485
+ needed_collectors = item .listchain ()
423
486
for col in needed_collectors [len (self .stack ) :]:
424
487
assert col not in self .stack
425
488
self .stack [col ] = [col .teardown ]
@@ -429,20 +492,29 @@ def prepare(self, colitem: Item) -> None:
429
492
col ._store [self ._prepare_exc_key ] = e
430
493
raise e
431
494
432
- def addfinalizer (self , finalizer : Callable [[], object ], colitem : Node ) -> None :
433
- """Attach a finalizer to the given colitem."""
434
- assert colitem and not isinstance (colitem , tuple )
495
+ def addfinalizer (self , finalizer : Callable [[], object ], node : Node ) -> None :
496
+ """Attach a finalizer to the given node.
497
+
498
+ The node must be currently active in the stack.
499
+ """
500
+ assert node and not isinstance (node , tuple )
435
501
assert callable (finalizer )
436
- assert colitem in self .stack , (colitem , self .stack )
437
- self .stack [colitem ].append (finalizer )
502
+ assert node in self .stack , (node , self .stack )
503
+ self .stack [node ].append (finalizer )
438
504
439
505
def teardown_exact (self , nextitem : Optional [Item ]) -> None :
506
+ """Teardown the current stack up until reaching nodes that nextitem
507
+ also descends from.
508
+
509
+ When nextitem is None (meaning we're at the last item), the entire
510
+ stack is torn down.
511
+ """
440
512
needed_collectors = nextitem and nextitem .listchain () or []
441
513
exc = None
442
514
while self .stack :
443
515
if list (self .stack .keys ()) == needed_collectors [: len (self .stack )]:
444
516
break
445
- colitem , finalizers = self .stack .popitem ()
517
+ node , finalizers = self .stack .popitem ()
446
518
while finalizers :
447
519
fin = finalizers .pop ()
448
520
try :
0 commit comments