Propagate unexpected `@pub` subscriber errors

Give each background subscriber runner its own teardown event and
suppress only `ContextCancelled` relayed by the root actor after that
portal's explicit cancellation begins.

Let generic remote errors, foreign cancellation and cancellation
before teardown escape the local task nursery so the test cannot pass
after a subscriber fails unexpectedly.

Review: PR #484 (GitHub Copilot and OpenCode)
https://github.com/goodboy/tractor/pull/484#discussion_r3858426546

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
drop_ria_nursery
Gud Boi 2026-08-25 20:38:57 -04:00
parent dd3e7482bf
commit 1d59f1963c
1 changed files with 29 additions and 8 deletions

View File

@ -194,14 +194,18 @@ def test_multi_actor_subs_arbiter_pub(
)
name = 'streamer'
root_uid = tractor.current_actor().aid.uid
# spawn the two subscriber actors as daemons and run
# `subs()` on each as a background task (was the legacy
# `run_in_actor()`); keep the portals for the explicit
# `cancel_actor()` teardown below. Each runner swallows
# the teardown error that `cancel_actor()` relays.
# only the cancellation relayed after its own teardown
# starts; every earlier or unrelated failure propagates.
async def _run_subs(
portal: tractor.Portal,
which: list[str],
teardown_started: trio.Event,
) -> None:
try:
await portal.run(
@ -209,11 +213,16 @@ def test_multi_actor_subs_arbiter_pub(
which=which,
pub_actor_name=name,
)
except (
tractor.RemoteActorError,
tractor.ContextCancelled,
):
pass # expected once we `cancel_actor()` below
except tractor.ContextCancelled as ctxc:
if not (
teardown_started.is_set()
and
ctxc.canceller == root_uid
):
raise
even_teardown_started = trio.Event()
odd_teardown_started = trio.Event()
even_portal = await an.start_actor(
'evens',
@ -223,8 +232,18 @@ def test_multi_actor_subs_arbiter_pub(
'odds',
enable_modules=[__name__],
)
tn.start_soon(_run_subs, even_portal, ['even'])
tn.start_soon(_run_subs, odd_portal, ['odd'])
tn.start_soon(
_run_subs,
even_portal,
['even'],
even_teardown_started,
)
tn.start_soon(
_run_subs,
odd_portal,
['odd'],
odd_teardown_started,
)
async with tractor.wait_for_actor('evens'):
# block until 2nd actor is initialized
@ -263,12 +282,14 @@ def test_multi_actor_subs_arbiter_pub(
# await even_portal.result()
await trio.sleep(0.5)
even_teardown_started.set()
await even_portal.cancel_actor()
await trio.sleep(1)
if pub_actor == 'arbiter':
assert 'even' not in get_topics()
odd_teardown_started.set()
await odd_portal.cancel_actor()
if pub_actor == 'arbiter':