diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 38f4643bd8b..c00cdff8f31 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -706,6 +706,13 @@ def __init__( # Do not use colors for inner runs by default. mp.setenv("PY_COLORS", "0") + # Pytester executes a full pytest section, including calling `pytest_unconfigure()`, which causes + # the `unraisableexception` plugin to call `gc.collect()` multiple times. + # Disable this forced garbage collection as it seriously slows down the entire test suite (#13482). + from _pytest import unraisableexception + + mp.setattr(unraisableexception, "GC_COLLECT_ITERATIONS", 0) + @property def path(self) -> Path: """Temporary directory path used to create files/run tests from, etc.""" diff --git a/src/_pytest/unraisableexception.py b/src/_pytest/unraisableexception.py index 7826aeccd12..6128d4ae5cd 100644 --- a/src/_pytest/unraisableexception.py +++ b/src/_pytest/unraisableexception.py @@ -24,10 +24,13 @@ from exceptiongroup import ExceptionGroup +# This constant was determined experimentally by the Trio project. +GC_COLLECT_ITERATIONS = 5 + + def gc_collect_harder() -> None: - # A single collection doesn't necessarily collect everything. - # Constant determined experimentally by the Trio project. - for _ in range(5): + """Call gc.collect() multiple times, a single collection doesn't necessarily collect everything.""" + for _ in range(GC_COLLECT_ITERATIONS): gc.collect() diff --git a/testing/test_unraisableexception.py b/testing/test_unraisableexception.py index 6c0dc542e93..5e7350f3c52 100644 --- a/testing/test_unraisableexception.py +++ b/testing/test_unraisableexception.py @@ -6,6 +6,7 @@ import sys from unittest import mock +from _pytest import unraisableexception from _pytest.pytester import Pytester import pytest @@ -238,7 +239,10 @@ def _disable_gc() -> Generator[None]: _set_gc_state(enabled=was_enabled) -def test_refcycle_unraisable(pytester: Pytester) -> None: +def test_refcycle_unraisable( + pytester: Pytester, monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setattr(unraisableexception, "GC_COLLECT_ITERATIONS", 5) # see: https://github.com/pytest-dev/pytest/issues/10404 pytester.makepyfile( test_it=""" @@ -267,7 +271,10 @@ def test_it(): @pytest.mark.filterwarnings("default::pytest.PytestUnraisableExceptionWarning") -def test_refcycle_unraisable_warning_filter(pytester: Pytester) -> None: +def test_refcycle_unraisable_warning_filter( + pytester: Pytester, monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setattr(unraisableexception, "GC_COLLECT_ITERATIONS", 5) # note that the host pytest warning filter is disabled and the pytester # warning filter applies during config teardown of unraisablehook. # see: https://github.com/pytest-dev/pytest/issues/10404 @@ -298,7 +305,10 @@ def test_it(): @pytest.mark.filterwarnings("default::pytest.PytestUnraisableExceptionWarning") -def test_create_task_raises_unraisable_warning_filter(pytester: Pytester) -> None: +def test_create_task_raises_unraisable_warning_filter( + pytester: Pytester, monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setattr(unraisableexception, "GC_COLLECT_ITERATIONS", 5) # note that the host pytest warning filter is disabled and the pytester # warning filter applies during config teardown of unraisablehook. # see: https://github.com/pytest-dev/pytest/issues/10404