diff --git a/trio/_core/tests/test_run.py b/trio/_core/tests/test_run.py index 5f4f232b1f..fc949290bd 100644 --- a/trio/_core/tests/test_run.py +++ b/trio/_core/tests/test_run.py @@ -11,7 +11,7 @@ import pytest import attr -from .tutil import check_sequence_matches +from .tutil import check_sequence_matches, gc_collect_harder from ...testing import ( wait_all_tasks_blocked, Sequencer, assert_yields, ) @@ -41,7 +41,8 @@ def ignore_coroutine_never_awaited_warnings(): finally: # Make sure to trigger any coroutine __del__ methods now, before # we leave the context manager. - gc.collect() + gc_collect_harder() + def test_basic(): async def trivial(x): @@ -883,7 +884,7 @@ async def main(): # Because this crashes, various __del__ methods print complaints on # stderr. Make sure that they get run now, so the output is attached to # this test. - gc.collect() + gc_collect_harder() def test_error_in_run_loop(): @@ -1490,6 +1491,8 @@ async def f(): # pragma: no cover bad_call(len, [1, 2, 3]) assert "appears to be synchronous" in str(excinfo.value) + # Make sure no references are kept around to keep anything alive + del excinfo def test_calling_asyncio_function_gives_nice_error(): async def misguided(): diff --git a/trio/_core/tests/tutil.py b/trio/_core/tests/tutil.py index 0a38f21f24..c2e59157fa 100644 --- a/trio/_core/tests/tutil.py +++ b/trio/_core/tests/tutil.py @@ -2,13 +2,26 @@ import pytest +import gc + # See trio/tests/conftest.py for the other half of this slow = pytest.mark.skipif( not pytest.config.getoption("--run-slow", True), reason="use --run-slow to run slow tests", ) -from ... import _core +def gc_collect_harder(): + # In the test suite we sometimes want to call gc.collect() to make sure + # that any objects with noisy __del__ methods (e.g. unawaited coroutines) + # get collected before we continue, so their noise doesn't leak into + # unrelated tests. + # + # On PyPy, coroutine objects (for example) can survive at least 1 round of + # garbage collection, because executing their __del__ method to print the + # warning can cause them to be resurrected. So we call collect a few times + # to make sure. + for _ in range(4): + gc.collect() # template is like: # [1, {2.1, 2.2}, 3] -> matches [1, 2.1, 3] or [1, 2.2, 3]