@@ -245,6 +245,51 @@ def pytest_runtest_makereport(item: pytest.Item, call: pytest.CallInfo):
245245 )
246246
247247
248+ def pytest_configure (config : pytest .Config ):
249+ # we need to adjust the hook order if pytest-retry is present because it:
250+ # - also declares a `pytest_runtest_makereport` with `hookwrapper=True, tryfirst=True`
251+ # - this supersedes our one because pytest always loads plugins first and they take precedence over user code
252+ #
253+ # but, we need our one to run first because it's capturing and ignoring certain errors that cause pytest-retry to fail
254+ # and not retry. so we need to adjust the order the hooks are called which pytest does NOT make easy.
255+ #
256+ # we can't just unload the pytest-retry plugin, load our hook and reload the pytest-retry plugin either.
257+ # this causes an error:
258+ # > Hook 'pytest_set_excluded_exceptions' is already registered within namespace
259+ # because unregister() apparently doesnt unregister plugins cleanly in such a way they can be re-registered
260+ #
261+ # so what we end up doing below is a small monkey-patch to adjust the call order of the hooks
262+ pm = config .pluginmanager
263+
264+ from pluggy ._hooks import HookCaller
265+
266+ hook_caller : HookCaller = pm .hook .pytest_runtest_makereport
267+ hook_impls = hook_caller .get_hookimpls ()
268+
269+ # find the index of our one
270+ our_makereport_idx = next (
271+ (i for i , v in enumerate (hook_impls ) if v .plugin_name .endswith ("tests/conftest.py" )), None
272+ )
273+
274+ # find the index of the pytest-retry one
275+ pytest_retry_makereport_idx = next (
276+ (i for i , v in enumerate (hook_impls ) if v .plugin_name == "pytest-retry" ), None
277+ )
278+
279+ if (
280+ pytest_retry_makereport_idx is not None
281+ and our_makereport_idx is not None
282+ and our_makereport_idx > pytest_retry_makereport_idx
283+ ):
284+ our_makereport_hook = hook_impls .pop (our_makereport_idx )
285+
286+ # inject our one to run before the pytest-retry one
287+ hook_impls .insert (pytest_retry_makereport_idx , our_makereport_hook )
288+
289+ # HookCaller doesnt have a setter method for this.
290+ hook_caller ._hookimpls = hook_impls # type: ignore
291+
292+
248293# Ignore all local config files
249294@pytest .fixture (scope = "session" , autouse = True )
250295def ignore_local_config_files ():
0 commit comments