Skip to content

Commit bb542d4

Browse files
committed
pytester: patch GC_COLLECT_ITERATIONS to speed up the test suite
Because `pytester.runpytest()` executes the full session cycle (including `pytest_unconfigure`), it was calling `gc.collect()` in a loop multiple times—even for small, fast tests. This significantly increased the total test suite runtime. To optimize performance, pytester now patches `GC_COLLECT_ITERATIONS` to skip `gc.collect()` entirely, matching the behavior before pytest-dev#12958. Locally the test suite runtime improved dramatically, dropping from 425s to 160s. Fixes pytest-dev#13482.
1 parent 9e9633d commit bb542d4

File tree

3 files changed

+26
-6
lines changed

3 files changed

+26
-6
lines changed

src/_pytest/pytester.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,13 @@ def __init__(
706706
# Do not use colors for inner runs by default.
707707
mp.setenv("PY_COLORS", "0")
708708

709+
# Pytester executes a full pytest section, including calling `pytest_unconfigure()`, which causes
710+
# the `unraisableexception` plugin to call `gc.collect()` multiple times.
711+
# Disable this forced garbage collection as it seriously slows down the entire test suite (#13482).
712+
from _pytest import unraisableexception
713+
714+
mp.setattr(unraisableexception, "GC_COLLECT_ITERATIONS", 0)
715+
709716
@property
710717
def path(self) -> Path:
711718
"""Temporary directory path used to create files/run tests from, etc."""

src/_pytest/unraisableexception.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@
2424
from exceptiongroup import ExceptionGroup
2525

2626

27+
# This constant was determined experimentally by the Trio project.
28+
GC_COLLECT_ITERATIONS = 5
29+
30+
2731
def gc_collect_harder() -> None:
28-
# A single collection doesn't necessarily collect everything.
29-
# Constant determined experimentally by the Trio project.
30-
for _ in range(5):
32+
"""Call gc.collect() multiple times, a single collection doesn't necessarily collect everything."""
33+
for _ in range(GC_COLLECT_ITERATIONS):
3134
gc.collect()
3235

3336

testing/test_unraisableexception.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import sys
77
from unittest import mock
88

9+
from _pytest import unraisableexception
910
from _pytest.pytester import Pytester
1011
import pytest
1112

@@ -238,7 +239,10 @@ def _disable_gc() -> Generator[None]:
238239
_set_gc_state(enabled=was_enabled)
239240

240241

241-
def test_refcycle_unraisable(pytester: Pytester) -> None:
242+
def test_refcycle_unraisable(
243+
pytester: Pytester, monkeypatch: pytest.MonkeyPatch
244+
) -> None:
245+
monkeypatch.setattr(unraisableexception, "GC_COLLECT_ITERATIONS", 5)
242246
# see: https://github.com/pytest-dev/pytest/issues/10404
243247
pytester.makepyfile(
244248
test_it="""
@@ -267,7 +271,10 @@ def test_it():
267271

268272

269273
@pytest.mark.filterwarnings("default::pytest.PytestUnraisableExceptionWarning")
270-
def test_refcycle_unraisable_warning_filter(pytester: Pytester) -> None:
274+
def test_refcycle_unraisable_warning_filter(
275+
pytester: Pytester, monkeypatch: pytest.MonkeyPatch
276+
) -> None:
277+
monkeypatch.setattr(unraisableexception, "GC_COLLECT_ITERATIONS", 5)
271278
# note that the host pytest warning filter is disabled and the pytester
272279
# warning filter applies during config teardown of unraisablehook.
273280
# see: https://github.com/pytest-dev/pytest/issues/10404
@@ -298,7 +305,10 @@ def test_it():
298305

299306

300307
@pytest.mark.filterwarnings("default::pytest.PytestUnraisableExceptionWarning")
301-
def test_create_task_raises_unraisable_warning_filter(pytester: Pytester) -> None:
308+
def test_create_task_raises_unraisable_warning_filter(
309+
pytester: Pytester, monkeypatch: pytest.MonkeyPatch
310+
) -> None:
311+
monkeypatch.setattr(unraisableexception, "GC_COLLECT_ITERATIONS", 5)
302312
# note that the host pytest warning filter is disabled and the pytester
303313
# warning filter applies during config teardown of unraisablehook.
304314
# see: https://github.com/pytest-dev/pytest/issues/10404

0 commit comments

Comments
 (0)