Compare commits

..

4 Commits

Author SHA1 Message Date
Gud Boi cf56f33be6 Correct cancellation and asyncio test prose
Fix five spelling errors in comments and failure text touched by the
one-shot migration: one `propagate`, one `Daemon` and three `directly`
corrections.

Review: PR #484 (GitHub Copilot)
https://github.com/goodboy/tractor/pull/484#pullrequestreview-5025348921

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-25 23:35:43 -04:00
Gud Boi 8739c5fadb Document legacy one-shot API removals
Add the PR #484 towncrier fragment for removing
`ActorNursery.run_in_actor()`, `Portal.wait_for_result()` and
`Portal.result()`.

Point callers to `to_actor.run()`, `Portal.run()` or
`Portal.open_context()` according to task ownership and dialog shape.

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`))
2026-08-25 23:03:20 -04:00
Gud Boi dfdaf2b1c1 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`))
2026-08-25 23:00:13 -04:00
Gud Boi 37eeb7bab6 Exercise active RPC tasks in SIGINT test
Open one linked sleeping context in each daemon and wait for every
`StartAck` before `test_cancel_via_SIGINT_other_task()` reports
startup.

This restores the legacy test's active remote-task cancellation
target instead of proving SIGINT teardown only against idle actor
runtimes.

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`))
2026-08-25 22:53:18 -04:00
3 changed files with 69 additions and 12 deletions

View File

@ -0,0 +1,5 @@
Remove legacy ``ActorNursery.run_in_actor()``,
``Portal.wait_for_result()`` and ``Portal.result()``. Use
``tractor.to_actor.run()`` for caller-owned one-shot tasks,
``Portal.run()`` for daemon RPC results or ``Portal.open_context()``
for linked task dialogs.

View File

@ -94,6 +94,18 @@ async def sleep_forever():
await trio.sleep_forever()
@tractor.context
async def sleep_forever_ctx(
ctx: tractor.Context,
) -> None:
'''
Signal task startup before sleeping until context cancellation.
'''
await ctx.started()
await sleep_forever()
async def do_nuthin():
# just nick the scheduler
await trio.sleep(0)
@ -204,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
@ -394,7 +436,7 @@ async def test_some_cancels_all(
except tractor.RemoteActorError as err:
assert err.boxed_type == err_type
# we only expect this first error to propogate
# we only expect this first error to propagate
# (all other daemons are cancelled before they
# can be scheduled)
num_actors = 1
@ -403,7 +445,7 @@ async def test_some_cancels_all(
else:
if expect_error:
pytest.fail(
"Deamon call should fail at checkpoint?")
"Daemon call should fail at checkpoint?")
# should error here with a `RemoteActorError` or a beg of them
@ -827,13 +869,23 @@ def test_cancel_via_SIGINT_other_task(
async with tractor.open_nursery(
registry_addrs=[reg_addr],
) as an:
# just keep a set of (daemon) subactors alive for the
# SIGINT to cancel (was 3 `run_in_actor(sleep_forever)`
# one-shots — a daemon needs no "main" task to idle).
for i in range(3):
portals = [
await an.start_actor(
f'namesucka_{i}',
enable_modules=[__name__],
)
for i in range(3)
]
# Keep one linked RPC task active in every daemon before
# reporting startup, preserving the original
# `run_in_actor(sleep_forever)` cancellation target.
async with gather_contexts(
mngrs=[
portal.open_context(sleep_forever_ctx)
for portal in portals
],
):
task_status.started()
await trio.sleep_forever()

View File

@ -667,7 +667,7 @@ def test_basic_interloop_channel_stream(
async with tractor.open_nursery(
registry_addrs=[reg_addr],
) as an:
# should raise RAE diectly
# should raise RAE directly
await to_actor.run(
partial(
stream_from_aio,
@ -726,7 +726,7 @@ def test_trio_closes_early_causes_aio_checkpoint_raise(
# enable_stack_on_sig=True,
registry_addrs=[reg_addr],
) as an:
# should raise RAE diectly
# should raise RAE directly
print('waiting on final infected subactor result..')
res: None = await to_actor.run(
partial(
@ -779,7 +779,7 @@ def test_aio_exits_early_relays_AsyncioTaskExited(
debug_mode=debug_mode,
# enable_stack_on_sig=True,
) as an:
# should raise RAE diectly
# should raise RAE directly
print('waiting on final infected subactor result..')
res: None = await to_actor.run(
partial(