From 9f0709eee2c6513ee364e6ef357c38d3ac0792a1 Mon Sep 17 00:00:00 2001 From: goodboy Date: Mon, 27 Apr 2026 19:47:44 -0400 Subject: [PATCH] Migrate test/smoketest imports + rename test file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename `tests/spawn/test_subint_forkserver.py` → `test_main_thread_forkserver.py` and migrate its imports + internal refs to the new canonical names: - `fork_from_worker_thread`, `wait_child` → from `tractor.spawn._main_thread_forkserver`. - `run_subint_in_worker_thread` → still from `_subint_forkserver` (variant-2 primitive). - Module docstring + tier-3 fixture + the `*_spawn_basic` test fn renamed for variant-1-honesty. - Orphan-harness subprocess argv flipped from `'subint_forkserver'` → `'main_thread_forkserver'`. `ai/conc-anal/subint_fork_from_main_thread_smoketest.py` imports split the same way. `tractor/spawn/_subint_forkserver.py` drops the backward- compat re-exports of the fork primitives — the only consumers (test file + smoketest) now import from `_main_thread_forkserver` directly. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- .../subint_fork_from_main_thread_smoketest.py | 6 ++- ...rver.py => test_main_thread_forkserver.py} | 53 ++++++++++--------- tractor/spawn/_subint_forkserver.py | 15 ------ 3 files changed, 32 insertions(+), 42 deletions(-) rename tests/spawn/{test_subint_forkserver.py => test_main_thread_forkserver.py} (93%) diff --git a/ai/conc-anal/subint_fork_from_main_thread_smoketest.py b/ai/conc-anal/subint_fork_from_main_thread_smoketest.py index 84d11d49..08166eac 100644 --- a/ai/conc-anal/subint_fork_from_main_thread_smoketest.py +++ b/ai/conc-anal/subint_fork_from_main_thread_smoketest.py @@ -89,11 +89,13 @@ except ImportError: # the "zero tractor imports" isolation guarantee; now that # CPython-level feasibility is confirmed, the validated # primitives have moved into tractor proper.) -from tractor.spawn._subint_forkserver import ( +from tractor.spawn._main_thread_forkserver import ( fork_from_worker_thread, - run_subint_in_worker_thread, wait_child, ) +from tractor.spawn._subint_forkserver import ( + run_subint_in_worker_thread, +) # ---------------------------------------------------------------- diff --git a/tests/spawn/test_subint_forkserver.py b/tests/spawn/test_main_thread_forkserver.py similarity index 93% rename from tests/spawn/test_subint_forkserver.py rename to tests/spawn/test_main_thread_forkserver.py index 784c78db..2ccc3c6c 100644 --- a/tests/spawn/test_subint_forkserver.py +++ b/tests/spawn/test_main_thread_forkserver.py @@ -1,13 +1,14 @@ ''' -Integration exercises for the `tractor.spawn._subint_forkserver` +Integration exercises for the `tractor.spawn._main_thread_forkserver` submodule at three tiers: 1. the low-level primitives - (`fork_from_worker_thread()` + - `run_subint_in_worker_thread()`) driven from inside a real + (`fork_from_worker_thread()` from `_main_thread_forkserver` + + `run_subint_in_worker_thread()` from + `_subint_forkserver`) driven from inside a real `trio.run()` in the parent process, -2. the full `subint_forkserver_proc` spawn backend wired +2. the full `main_thread_forkserver_proc` spawn backend wired through tractor's normal actor-nursery + portal-RPC machinery — i.e. `open_root_actor` + `open_nursery` + `run_in_actor` against a subactor spawned via fork from a @@ -27,15 +28,15 @@ Those smoke-test scenarios are standalone — no trio runtime in the *parent*. Tiers (1)+(2) here cover the primitives driven from inside `trio.run()` in the parent, and tier (3) (the `*_spawn_basic` test) drives the registered -`subint_forkserver` spawn backend end-to-end against the -tractor runtime. +`main_thread_forkserver` spawn backend end-to-end against +the tractor runtime. Gating ------ - py3.14+ (via `concurrent.interpreters` presence) - no `--spawn-backend` restriction — the backend-level test flips `tractor.spawn._spawn._spawn_method` programmatically - (via `try_set_start_method('subint_forkserver')`) and + (via `try_set_start_method('main_thread_forkserver')`) and restores it on teardown, so these tests are independent of the session-level CLI backend choice. @@ -64,11 +65,13 @@ from tractor.devx import dump_on_hang # `tractor.spawn._subint` for why. pytest.importorskip('concurrent.interpreters') -from tractor.spawn._subint_forkserver import ( # noqa: E402 +from tractor.spawn._main_thread_forkserver import ( # noqa: E402 fork_from_worker_thread, - run_subint_in_worker_thread, wait_child, ) +from tractor.spawn._subint_forkserver import ( # noqa: E402 + run_subint_in_worker_thread, +) from tractor.spawn import _spawn as _spawn_mod # noqa: E402 from tractor.spawn._spawn import try_set_start_method # noqa: E402 @@ -195,7 +198,7 @@ def test_fork_from_worker_thread_via_trio( deadline: float = 10.0 with dump_on_hang( seconds=deadline, - path='/tmp/subint_forkserver_baseline.dump', + path='/tmp/main_thread_forkserver_baseline.dump', ): pid: int = trio.run( partial(run_fork_in_non_trio_thread, deadline), @@ -217,14 +220,14 @@ def test_fork_and_run_trio_in_child() -> None: `trio.run()` inside it on yet another worker thread. This is the full "forkserver + trio-in-subint-in-child" - pattern the proposed `subint_forkserver` spawn backend + pattern the proposed `main_thread_forkserver` spawn backend would rest on. ''' deadline: float = 15.0 with dump_on_hang( seconds=deadline, - path='/tmp/subint_forkserver_trio_in_child.dump', + path='/tmp/main_thread_forkserver_trio_in_child.dump', ): pid: int = trio.run( partial( @@ -237,7 +240,7 @@ def test_fork_and_run_trio_in_child() -> None: # ---------------------------------------------------------------- -# tier-3 backend test: drive the registered `subint_forkserver` +# tier-3 backend test: drive the registered `main_thread_forkserver` # spawn backend end-to-end through tractor's actor-nursery + # portal-RPC machinery. # ---------------------------------------------------------------- @@ -260,7 +263,7 @@ async def _happy_path_forkserver( Parent-side harness: stand up a root actor, open an actor nursery, spawn one subactor via the currently-selected spawn backend (which this test will have flipped to - `subint_forkserver`), run a trivial RPC through its + `main_thread_forkserver`), run a trivial RPC through its portal, assert the round-trip result. ''' @@ -304,19 +307,19 @@ def forkserver_spawn_method(): @pytest.mark.timeout(60, method='thread') -def test_subint_forkserver_spawn_basic( +def test_main_thread_forkserver_spawn_basic( reg_addr: tuple[str, int | str], forkserver_spawn_method, ) -> None: ''' Happy-path: spawn ONE subactor via the - `subint_forkserver` backend (parent-side fork from a + `main_thread_forkserver` backend (parent-side fork from a main-interp worker thread), do a trivial portal-RPC round-trip, tear the nursery down cleanly. If this passes, the "forkserver + tractor runtime" arch is proven end-to-end: the registered - `subint_forkserver_proc` spawn target successfully + `main_thread_forkserver_proc` spawn target successfully forks a child, the child runs `_actor_child_main()` + completes IPC handshake + serves an RPC, and the parent reaps via `_ForkedProc.wait()` without regressing any of @@ -326,7 +329,7 @@ def test_subint_forkserver_spawn_basic( deadline: float = 20.0 with dump_on_hang( seconds=deadline, - path='/tmp/subint_forkserver_spawn_basic.dump', + path='/tmp/main_thread_forkserver_spawn_basic.dump', ): trio.run( partial( @@ -340,7 +343,7 @@ def test_subint_forkserver_spawn_basic( # ---------------------------------------------------------------- # tier-4 DRAFT: orphaned-subactor SIGINT survivability # -# Motivating question: with `subint_forkserver`, the child's +# Motivating question: with `main_thread_forkserver`, the child's # `trio.run()` lives on the fork-inherited worker thread which # is NOT `threading.main_thread()` — so trio cannot install its # `signal.set_wakeup_fd`-based SIGINT handler. If the parent @@ -360,7 +363,7 @@ def test_subint_forkserver_spawn_basic( # Cross-backend generalization (decide after this passes): # - applicable to any backend whose subactors are separate OS # processes: `trio`, `mp_spawn`, `mp_forkserver`, -# `subint_forkserver`. +# `main_thread_forkserver`. # - NOT applicable to plain `subint` (subactors are in-process # subinterpreters, no orphan child process to SIGINT). # - move path: lift the harness script into @@ -446,7 +449,7 @@ def _process_alive(pid: int) -> bool: return False -# Known-gap test — `subint_forkserver` orphan-SIGINT +# Known-gap test — `main_thread_forkserver` orphan-SIGINT # handling. See # `ai/conc-anal/subint_forkserver_orphan_sigint_hang_issue.md`. # `strict=True` so if a future fix closes the gap the @@ -471,12 +474,12 @@ def test_orphaned_subactor_sigint_cleanup_DRAFT( ) -> None: ''' DRAFT — orphaned-subactor SIGINT survivability under the - `subint_forkserver` backend. + `main_thread_forkserver` backend. Sequence: 1. Spawn a harness subprocess that brings up a root actor + one `sleep_forever` subactor via - `subint_forkserver`. + `main_thread_forkserver`. 2. Read the harness's stdout for `PARENT_READY=` and `CHILD_PID=` markers (confirms the parent→child IPC handshake completed). @@ -524,7 +527,7 @@ def test_orphaned_subactor_sigint_cleanup_DRAFT( [ sys.executable, str(script_path), - 'subint_forkserver', + 'main_thread_forkserver', host, str(port), ], @@ -577,7 +580,7 @@ def test_orphaned_subactor_sigint_cleanup_DRAFT( pytest.fail( f'Orphan subactor (pid={child_pid}) did NOT exit ' - f'within 10s of SIGINT under `subint_forkserver` ' + f'within 10s of SIGINT under `main_thread_forkserver` ' f'→ trio on non-main thread did not observe the ' f'default CPython KeyboardInterrupt; backend needs ' f'explicit SIGINT plumbing.' diff --git a/tractor/spawn/_subint_forkserver.py b/tractor/spawn/_subint_forkserver.py index 94f87f8c..07fbcf14 100644 --- a/tractor/spawn/_subint_forkserver.py +++ b/tractor/spawn/_subint_forkserver.py @@ -154,21 +154,6 @@ from trio import TaskStatus from tractor.log import get_logger from ._subint import _has_subints -# Backward-compat re-exports of the fork primitives whose -# canonical home is now `_main_thread_forkserver`. Kept here -# transiently so existing -# `from tractor.spawn._subint_forkserver import ...` callsites -# in the tests + the conc-anal smoketest keep resolving; -# dropped once a follow-up commit migrates those imports to -# the new module. -from ._main_thread_forkserver import ( - _close_inherited_fds as _close_inherited_fds, - _format_child_exit as _format_child_exit, - fork_from_worker_thread as fork_from_worker_thread, - wait_child as wait_child, - _ForkedProc as _ForkedProc, -) - if TYPE_CHECKING: from tractor.discovery._addr import UnwrappedAddress from tractor.runtime._portal import Portal