Skip to content

Add tractor.to_actor one-shot task API subpkg#481

Open
goodboy wants to merge 3 commits into
mainfrom
wkt/to_actor_subpkg
Open

Add tractor.to_actor one-shot task API subpkg#481
goodboy wants to merge 3 commits into
mainfrom
wkt/to_actor_subpkg

Conversation

@goodboy

@goodboy goodboy commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Add tractor.to_actor one-shot task API subpkg

Motivation

First cut at resolving #477: the .run_in_actor() "one shot"
single-remote-task style API is coupled to ActorNursery internals
(the secondary ._ria_nursery, portal result-marking sets and
backend-side reaper tasks) which complicates the spawn and
cancellation machinery and defers remote-error propagation to nursery
teardown. Adopting the parlance of sibling APIs (trio.to_thread,
anyio.to_process) we instead deliver a tractor.to_actor subpkg
composed purely from the lower level daemon-spawn + portal
primitives, moving error collection/propagation up into whatever
local trio scope encloses the call — and paving the way to fully
drop the ._ria_nursery machinery.

Summary of changes

  • new tractor.to_actor subpkg exporting run(fn, **fn_kwargs) -> Any: spawn a subactor via ActorNursery.start_actor(), schedule
    fn as its lone remote task with Portal.run(), block waiting on
    and return its result, ALWAYS reaping the subactor via a
    finally-scoped Portal.cancel_actor(). Remote errors raise
    directly in the caller's task as boxed RemoteActorErrors.
    "Placement" opts: portal= re-uses a running actor (no
    spawn/reap), an= spawns from a caller-managed actor-nursery,
    neither opens a private call-scoped open_nursery() (implicitly
    booting the runtime, tunable via pass-through runtime_kwargs).
    Fail-fast pre-spawn validation: non-streaming async fn only,
    portal=/an= mutually exclusive, no runtime_kwargs alongside a
    placement opt. Legacy .run_in_actor() behavior untouched; its
    TODO/docstring now x-ref the successor API.
  • tests/test_to_actor.py: 11-test suite covering all placement
    variants, remote-error relay (bare + inside a caller-managed an),
    portal re-use w/o implicit reap, the concurrent "worker-pool-ish"
    pattern (local trio task nursery scheduling one-shots against a
    shared an) and the 4 validation rejections.
  • examples/parallelism/to_actor_one_shots.py: runnable demo
    (auto-collected by test_docs_examples.py) of the fully-implicit
    one-shot + the concurrent prime-check pattern, a mini version of
    the neighboring concurrent_actors_primes.py.

Future follow up

Tracked in #477 (see the landed-shape comment there),

  • migrate in-repo .run_in_actor() usage (tests, examples, docs)
    to to_actor.run()
  • emit a DeprecationWarning from .run_in_actor() (blocked on
    ^)
  • drop the ._ria_nursery + ._cancel_after_result_on_exit
    machinery — in progress on the stacked drop_ria_nursery branch
    (result-waiting being re-scoped into the to_actor layer), see
    ai/conc-anal/ria_nursery_removal_plan.md
  • docs nod from the .run_in_actor() autodoc entry
  • maybe: the worker-pool "sugar" API layer (Do we need a streaming equivalent (sugar) for .run_in_actor()? #172)

(this pr content was generated in some part by claude-code)

goodboy added 3 commits July 2, 2026 12:10
First cut at the `to_thread`/`to_process`-style "run it over there"
wrapper layer from issue #477: a single-remote-task invocation API
decoupled from the `ActorNursery` spawn machinery, composed purely
from the lower level daemon-actor + portal primitives,

- `to_actor.run(fn, **fn_kwargs)` spawns a subactor via
  `ActorNursery.start_actor()`, schedules `fn` as its lone task
  with `Portal.run()` and ALWAYS reaps it via a `finally`-scoped
  `Portal.cancel_actor()` (whose bounded cancel-req wait is
  internally shielded so the reap also runs under caller-scope
  cancellation).
- remote errors raise directly in the caller's task as boxed
  `RemoteActorError`s, moving error collection/propagation up into
  whatever local `trio` scope encloses the call.
- "placement" opts: `portal=` reuses a running actor (no
  spawn/reap), `an=` spawns from a caller-managed actor-nursery,
  neither opens a private call-scoped `open_nursery()` (implicitly
  booting the runtime, tunable via pass-through `runtime_kwargs`).
- fail-fast validation BEFORE any spawn: non-streaming async fn
  only (same constraint as `Portal.run()`), `portal=`/`an=` mutual
  exclusion and no `runtime_kwargs` alongside a placement opt.

Also,
- x-ref the successor API from `.run_in_actor()`'s deprecation TODO
  + docstring; emitting a formal `DeprecationWarning` waits on
  migrating in-repo usage.
- log prompt-io provenance per NLNet policy incl. the driver prompt
  file.

Prompt-IO: ai/prompt-io/claude/20260702T154255Z_65bf9df5_prompt_io.md

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Cover every placement variant + failure mode of the new
`to_actor.run()`,

- private-nursery one-shot + implicit runtime boot via pass-through
  `runtime_kwargs`,
- remote-error relay to the caller's task (bare and inside a
  caller-managed `an`) as boxed `RemoteActorError`s,
- caller-nursery spawn + portal-reuse w/o implicit reap,
- the concurrent "worker-pool-ish" pattern: a local `trio` task
  nursery scheduling one-shots against a shared `an`,
- the 4 pre-spawn validation rejections (sync fn, async-gen fn,
  `portal`+`an` combo, `runtime_kwargs`+placement combo).

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Demo both flavors of the new API in a runnable script
(auto-collected by `test_docs_examples.py`),

- the fully-implicit one-shot which boots (and tears down) the
  actor-runtime around a single `to_actor.run()` call,
- the concurrent "worker-pool-ish" prime-check pattern: a local
  `trio` task nursery scheduling one-shots against a shared
  caller-managed `an`, mirroring (in miniature) the neighboring
  `concurrent_actors_primes.py` example per issue #477.

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Copilot AI review requested due to automatic review settings July 2, 2026 16:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces a new tractor.to_actor subpackage providing a high-level, “one-shot” remote task invocation API built on existing ActorNursery.start_actor() + Portal.run() + Portal.cancel_actor() primitives, intended as the successor to ActorNursery.run_in_actor() (issue #477).

Changes:

  • Adds tractor.to_actor.run() implementing one-shot subactor spawn/reuse, remote execution, and teardown behavior.
  • Exposes the new subpackage at the top level (import tractor.to_actor) and documents the intended migration path from run_in_actor().
  • Adds a dedicated test suite and a runnable example demonstrating concurrent one-shot usage patterns.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tractor/to_actor/_api.py Implements the new to_actor.run() API and its helper routines.
tractor/to_actor/init.py Defines the new tractor.to_actor public surface (re-export of run).
tractor/runtime/_supervise.py Updates run_in_actor() notes/TODOs to point to the successor API.
tractor/init.py Re-exports to_actor at the package top level.
tests/test_to_actor.py Adds coverage for placement modes, error propagation, concurrency pattern, and validation.
examples/parallelism/to_actor_one_shots.py Adds an example demonstrating implicit runtime boot and concurrent one-shots.
ai/prompt-io/prompts/issue_477.md Adds the driver prompt used for the work.
ai/prompt-io/claude/20260702T154255Z_65bf9df5_prompt_io.raw.md Adds raw AI output log for the work session.
ai/prompt-io/claude/20260702T154255Z_65bf9df5_prompt_io.md Adds summarized AI output log for the work session.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tractor/to_actor/_api.py
Comment on lines +105 to +112
finally:
# one-shot semantics: the subactor's lifetime is
# bound to its lone task's completion; the
# cancel-req's bounded wait is shielded
# internally (see `Portal.cancel_actor()`) so
# this reap also runs when the caller's scope
# was itself cancelled.
await portal.cancel_actor()
Comment on lines +1 to +3
NOTE: you MUST pause this work at 12:50PM EST (BEFORE your weekly
limit reset) for review by a human!

@goodboy goodboy changed the title Wkt/to actor subpkg Add tractor.to_actor one-shot task API subpkg Jul 2, 2026
@goodboy goodboy force-pushed the wkt/to_actor_subpkg branch from 5cd190c to a34aaf9 Compare July 3, 2026 01:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants