From dfdaf2b1c18f40491cfe4d61365d17bab0d98ab2 Mon Sep 17 00:00:00 2001 From: goodboy Date: Tue, 25 Aug 2026 23:00:13 -0400 Subject: [PATCH] Validate `test_multierror()` relay shapes Inspect the exception emitted by the concurrent error fan-out instead of accepting any `RemoteActorError` or `BaseExceptionGroup`. Require each non-cancellation leaf to box `AssertionError`, allow one or two relays for cancel-on-first timing and require both child relays when no cancellation leaf accompanies the group. Review: PR #484 (OpenCode) https://github.com/goodboy/tractor/pull/484#pullrequestreview-5025383596 (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`)) --- tests/test_cancellation.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/tests/test_cancellation.py b/tests/test_cancellation.py index 91060e63..bf52816e 100644 --- a/tests/test_cancellation.py +++ b/tests/test_cancellation.py @@ -216,9 +216,39 @@ def test_multierror( with pytest.raises(( BaseExceptionGroup, tractor.RemoteActorError, - )): + )) as excinfo: trio.run(main) + exc = excinfo.value + if isinstance(exc, tractor.RemoteActorError): + assert exc.boxed_type is AssertionError + return + + def iter_group_leaves( + group: BaseExceptionGroup, + ): + for subexc in group.exceptions: + if isinstance(subexc, BaseExceptionGroup): + yield from iter_group_leaves(subexc) + else: + yield subexc + + assertion_errors: list[tractor.RemoteActorError] = [] + cancellations: list[BaseException] = [] + for leaf in iter_group_leaves(exc): + if isinstance(leaf, tractor.ContextCancelled): + cancellations.append(leaf) + elif isinstance(leaf, trio.Cancelled): + cancellations.append(leaf) + else: + assert isinstance(leaf, tractor.RemoteActorError) + assert leaf.boxed_type is AssertionError + assertion_errors.append(leaf) + + assert len(assertion_errors) in (1, 2) + if not cancellations: + assert len(assertion_errors) == 2 + async def do_nothing(): pass