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`))drop_ria_nursery
parent
37eeb7bab6
commit
dfdaf2b1c1
|
|
@ -216,9 +216,39 @@ def test_multierror(
|
||||||
with pytest.raises((
|
with pytest.raises((
|
||||||
BaseExceptionGroup,
|
BaseExceptionGroup,
|
||||||
tractor.RemoteActorError,
|
tractor.RemoteActorError,
|
||||||
)):
|
)) as excinfo:
|
||||||
trio.run(main)
|
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():
|
async def do_nothing():
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue