55
66import argparse
77import collections .abc
8+ import contextlib
89import copy
910import dataclasses
1011import enum
3334from typing import TextIO
3435from typing import Type
3536from typing import TYPE_CHECKING
37+ from typing import TypeVar
3638import warnings
3739
3840import pluggy
7375 from _pytest .cacheprovider import Cache
7476 from _pytest .terminal import TerminalReporter
7577
78+ _T_callback = TypeVar ("_T_callback" , bound = Callable [[], None ])
79+
7680
7781_PluggyPlugin = object
7882"""A type to represent plugin objects.
@@ -1077,7 +1081,7 @@ def __init__(
10771081 self ._inicache : dict [str , Any ] = {}
10781082 self ._override_ini : Sequence [str ] = ()
10791083 self ._opt2dest : dict [str , str ] = {}
1080- self ._cleanup : list [ Callable [[], None ]] = []
1084+ self ._exit_stack = contextlib . ExitStack ()
10811085 self .pluginmanager .register (self , "pytestconfig" )
10821086 self ._configured = False
10831087 self .hook .pytest_addoption .call_historic (
@@ -1104,10 +1108,11 @@ def inipath(self) -> pathlib.Path | None:
11041108 """
11051109 return self ._inipath
11061110
1107- def add_cleanup (self , func : Callable [[], None ] ) -> None :
1111+ def add_cleanup (self , func : _T_callback ) -> _T_callback :
11081112 """Add a function to be called when the config object gets out of
11091113 use (usually coinciding with pytest_unconfigure)."""
1110- self ._cleanup .append (func )
1114+ self ._exit_stack .callback (func )
1115+ return func
11111116
11121117 def _do_configure (self ) -> None :
11131118 assert not self ._configured
@@ -1117,13 +1122,18 @@ def _do_configure(self) -> None:
11171122 self .hook .pytest_configure .call_historic (kwargs = dict (config = self ))
11181123
11191124 def _ensure_unconfigure (self ) -> None :
1120- if self ._configured :
1121- self ._configured = False
1122- self .hook .pytest_unconfigure (config = self )
1123- self .hook .pytest_configure ._call_history = []
1124- while self ._cleanup :
1125- fin = self ._cleanup .pop ()
1126- fin ()
1125+ try :
1126+ if self ._configured :
1127+ self ._configured = False
1128+ try :
1129+ self .hook .pytest_unconfigure (config = self )
1130+ finally :
1131+ self .hook .pytest_configure ._call_history = []
1132+ finally :
1133+ try :
1134+ self ._exit_stack .close ()
1135+ finally :
1136+ self ._exit_stack = contextlib .ExitStack ()
11271137
11281138 def get_terminal_writer (self ) -> TerminalWriter :
11291139 terminalreporter : TerminalReporter | None = self .pluginmanager .get_plugin (
0 commit comments