From f4631659e17e3d2e180aeacff17937843c8e7d75 Mon Sep 17 00:00:00 2001 From: DABH Date: Thu, 12 Nov 2026 20:39:00 -0600 Subject: [PATCH 1/2] Run the garbage collector between tests Objects a test leaves in reference cycles were collected whenever an allocation crossed the collector's threshold, which could be on a workflow thread in the middle of a sandboxed import. redis-py's ConnectionPool.__del__ runs an import, and on CPython before 3.12 an import started by a finalizer while the same thread is inside importlib's module-lock acquire leaks that lock (python/cpython#91351), so the next sandbox import of that module waits forever. That is the Python 3.10 CI stall. An autouse fixture now collects on the main thread after every test. --- tests/conftest.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 83dcaef5..fb4378a1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,8 @@ import asyncio +import gc import multiprocessing import sys -from typing import AsyncGenerator +from typing import AsyncGenerator, Iterator import pytest import pytest_asyncio @@ -65,3 +66,19 @@ async def env(request) -> AsyncGenerator[WorkflowEnvironment, None]: @pytest_asyncio.fixture async def client(env: WorkflowEnvironment) -> Client: return env.client + + +@pytest.fixture(autouse=True) +def collect_garbage_between_tests() -> Iterator[None]: + """Run the cyclic garbage collector on the main thread after every test. + + Otherwise objects a test leaves in reference cycles are collected whenever + an allocation crosses the collector's threshold, which can be on a workflow + thread in the middle of a sandboxed import. redis-py's ConnectionPool.__del__ + runs an import, and on CPython before 3.12 an import started by a finalizer + while the same thread is inside importlib's module-lock acquire leaks that + lock (python/cpython#91351), so the next sandbox import of the same module + waits forever. That is what stalled the Python 3.10 CI job. + """ + yield + gc.collect() From 11c632cbcefe67c439add4d513799172fa10b9fd Mon Sep 17 00:00:00 2001 From: David Hyde Date: Thu, 12 Nov 2026 23:53:22 -0600 Subject: [PATCH 2/2] clean up --- tests/conftest.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index fb4378a1..cc318eeb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -72,13 +72,8 @@ async def client(env: WorkflowEnvironment) -> Client: def collect_garbage_between_tests() -> Iterator[None]: """Run the cyclic garbage collector on the main thread after every test. - Otherwise objects a test leaves in reference cycles are collected whenever - an allocation crosses the collector's threshold, which can be on a workflow - thread in the middle of a sandboxed import. redis-py's ConnectionPool.__del__ - runs an import, and on CPython before 3.12 an import started by a finalizer - while the same thread is inside importlib's module-lock acquire leaks that - lock (python/cpython#91351), so the next sandbox import of the same module - waits forever. That is what stalled the Python 3.10 CI job. + Otherwise there can be a bug that leads to test stalls, when using CPython + before 3.12 (python/cpython#91351). """ yield gc.collect()