From 060f7d24c42698e00d9ed5d34f936f487b619b67 Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 29 Apr 2026 10:21:56 -0400 Subject: [PATCH] Backend-aware timeout in `maybe_expect_raises` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Default `timeout` from `int = 3` → `int|None = None`; when unset, pick a backend-aware value. Fork-based backends (`main_thread_forkserver`) need real headroom bc actor spawn + IPC ctx-exit + msg-validation error path is much heavier than under `trio` backend — especially under cross-pytest-stream contention (#451). Defaults: - `main_thread_forkserver` → 30s - everything else → 3s (unchanged) Empirical flake history that motivated 30s as the floor on fork backends (all from `test_basic_payload_spec`): - 3s → all-valid variant flaked w/ `TooSlowError` - 8s → `invalid-return` variant flaked w/ `Cancelled` (surfaced instead of `MsgTypeError` bc the outer `fail_after` fired mid-error-path) - 15s → flaked under cross-pytest-stream contention 30s gives plenty of headroom while still failing-loud on a genuine hang. Callers can opt out by passing an explicit `timeout=` kw. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- tests/msg/test_pldrx_limiting.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/msg/test_pldrx_limiting.py b/tests/msg/test_pldrx_limiting.py index b180dc03..1a8b6117 100644 --- a/tests/msg/test_pldrx_limiting.py +++ b/tests/msg/test_pldrx_limiting.py @@ -55,12 +55,37 @@ async def maybe_expect_raises( raises: BaseException|None = None, ensure_in_message: list[str]|None = None, post_mortem: bool = False, - timeout: int = 3, + # NOTE, `None` selects a backend-aware default below — + # see `_BACKEND_TIMEOUT_DEFAULTS` for rationale. Caller + # can override with an explicit value to opt out. + timeout: int|None = None, ) -> None: ''' Async wrapper for ensuring errors propagate from the inner scope. ''' + if timeout is None: + # Pick a backend-aware default. Fork-based backends + # (`main_thread_forkserver`) need much more headroom + # because actor spawn + IPC ctx-exit + msg-validation + # error path takes longer than under `trio` backend + # — especially under cross-pytest-stream contention + # (#451). `test_basic_payload_spec` empirically: + # - 3s flaked all-valid variant (`TooSlowError`) + # - 8s flaked `invalid-return` variant + # (`Cancelled` surfaced instead of `MsgTypeError` + # because `fail_after` fired mid-error-path) + # - 15s flaked under cross-stream contention + # 30s for fork-based gives plenty of headroom while + # still failing-loud on a genuine hang. Other + # backends keep the original 3s. + from tractor.spawn import _spawn as _spawn_mod + timeout = ( + 30 + if _spawn_mod._spawn_method == 'main_thread_forkserver' + else 3 + ) + if tractor.debug_mode(): timeout += 999