From de6016763f9bff955408eacfabb65b30433ceca2 Mon Sep 17 00:00:00 2001 From: goodboy Date: Thu, 23 Apr 2026 14:37:48 -0400 Subject: [PATCH] Wire `reg_addr` through leaky cancel tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stopgap companion to d0121960 (`subint_forkserver` test-cancellation leak doc): five tests in `tests/test_cancellation.py` were running against the default `:1616` registry, so any leaked `subint-forkserv` descendant from a prior test holds the port and blows up every subsequent run with `TooSlowError` / "address in use". Thread the session-unique `reg_addr` fixture through so each run picks its own port — zombies can no longer poison other tests (they'll only cross-contaminate whatever happens to share their port, which is now nothing). Deats, - add `reg_addr: tuple` fixture param to: - `test_cancel_infinite_streamer` - `test_some_cancels_all` - `test_nested_multierrors` - `test_cancel_via_SIGINT` - `test_cancel_via_SIGINT_other_task` - explicitly pass `registry_addrs=[reg_addr]` to the two `open_nursery()` calls that previously had no kwargs at all (in `test_cancel_via_SIGINT` and `test_cancel_via_SIGINT_other_task`) - add bounded `@pytest.mark.timeout(7, method='thread')` to `test_nested_multierrors` so a hung run doesn't wedge the whole session Still doesn't close the real leak — the `subint_forkserver` backend's `_ForkedProc.kill()` is PID-scoped not tree-scoped, so grandchildren survive teardown regardless of registry port. This commit is just blast-radius containment until that fix lands. See `ai/conc-anal/ subint_forkserver_test_cancellation_leak_issue.md`. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- tests/test_cancellation.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/test_cancellation.py b/tests/test_cancellation.py index 645ee068..17f19723 100644 --- a/tests/test_cancellation.py +++ b/tests/test_cancellation.py @@ -275,7 +275,8 @@ async def stream_forever(): timeout=6, ) async def test_cancel_infinite_streamer( - start_method: str + reg_addr: tuple, + start_method: str, ): # stream for at most 1 seconds with ( @@ -341,6 +342,7 @@ async def test_cancel_infinite_streamer( ) async def test_some_cancels_all( num_actors_and_errs: tuple, + reg_addr: tuple, start_method: str, loglevel: str, ): @@ -450,8 +452,13 @@ async def spawn_and_error( await nursery.run_in_actor(*args, **kwargs) +@pytest.mark.timeout( + 10, + method='thread', +) @tractor_test async def test_nested_multierrors( + reg_addr: tuple, loglevel: str, start_method: str, ): @@ -541,6 +548,7 @@ async def test_nested_multierrors( @no_windows def test_cancel_via_SIGINT( + reg_addr: tuple, loglevel: str, start_method: str, ): @@ -553,7 +561,9 @@ def test_cancel_via_SIGINT( async def main(): with trio.fail_after(2): - async with tractor.open_nursery() as tn: + async with tractor.open_nursery( + registry_addrs=[reg_addr], + ) as tn: await tn.start_actor('sucka') if 'mp' in start_method: time.sleep(0.1) @@ -566,6 +576,7 @@ def test_cancel_via_SIGINT( @no_windows def test_cancel_via_SIGINT_other_task( + reg_addr: tuple, loglevel: str, start_method: str, spawn_backend: str, @@ -594,7 +605,9 @@ def test_cancel_via_SIGINT_other_task( async def spawn_and_sleep_forever( task_status=trio.TASK_STATUS_IGNORED ): - async with tractor.open_nursery() as tn: + async with tractor.open_nursery( + registry_addrs=[reg_addr], + ) as tn: for i in range(3): await tn.run_in_actor( sleep_forever,