tractor/examples/debugging/multi_subactors.py

76 lines
2.1 KiB
Python
Raw Normal View History

2020-10-04 21:31:02 +00:00
import tractor
import trio
async def breakpoint_forever():
"Indefinitely re-enter debugger in child actor."
while True:
await trio.sleep(0.1)
await tractor.pause()
2020-10-04 21:31:02 +00:00
async def name_error():
"Raise a ``NameError``"
2021-02-24 18:39:14 +00:00
getattr(doggypants) # noqa
2020-10-04 21:31:02 +00:00
async def spawn_error():
""""A nested nursery that triggers another ``NameError``.
"""
async with tractor.open_nursery() as an:
Port debugging examples off `run_in_actor` The 8 `examples/debugging/` scripts driven by the pexpect'd `test_debugger.py` REPL-flows (#477 removal), - blocking one-shots (`subactor_error`, `subactor_breakpoint`, `shielded_pause`): straight `to_actor.run(fn, an=an)` — the boxed error/`BdbQuit` raises in the root's task. - `multi_subactors`: introduces the "collect all errors" pattern — each one-shot catches + stashes its `RemoteActorError` (vs raising) so no child's crash cancels its siblings before they've had their own REPL sessions, then a `BaseExceptionGroup` of the lot raises at the end; preserves the legacy teardown-reap REPL flow exactly (28/28 debugger suite unchanged). - `multi_nested_subactors_error_up_through_nurseries` + `root_cancelled_but_child_is_in_tty_lock`: recursive `spawn_until` levels each block on their one-shot child; the parallel spawner-trees run as bg task-nursery one-shots where the first tree's error cancels the other. - `multi_subactor_root_errors` + `root_timeout_while_child_crashed`: `start_actor()` + bg `Portal.run()` tasks so the root's own error/timeout races the already-crashed children, same as before. - `sync_bp`: TODO-comment x-ref update only. `test_debugger.py`: the nested-nurseries test's final-output patterns update to the new relay shape — the LAST-released leaf REPL's error chain wins each level's relay-vs-cancel race and relays as a `collapse_eg()`-annotated collapsed chain, while the sibling tree is cancelled + absorbed. (The legacy teardown-reap grouped BOTH the `name_error` and bp-quit chains — explaining the previously-mysterious "extra" `src_uid`/`relay_uid` patterns noted in the old TODO.) Gate: `tests/devx/test_debugger.py` = 28 passed, 6 skipped — identical to the pre-migration baseline. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
2026-07-06 16:24:29 +00:00
return await tractor.to_actor.run(
name_error,
an=an,
name='name_error_1',
)
2020-10-04 21:31:02 +00:00
async def main():
"""The main ``tractor`` routine.
The process tree should look as approximately as follows:
-python examples/debugging/multi_subactors.py
|-python -m tractor._child --uid ('name_error', 'a7caf490 ...)
|-python -m tractor._child --uid ('bp_forever', '1f787a7e ...)
`-python -m tractor._child --uid ('spawn_error', '52ee14a5 ...)
`-python -m tractor._child --uid ('name_error', '3391222c ...)
"""
Port debugging examples off `run_in_actor` The 8 `examples/debugging/` scripts driven by the pexpect'd `test_debugger.py` REPL-flows (#477 removal), - blocking one-shots (`subactor_error`, `subactor_breakpoint`, `shielded_pause`): straight `to_actor.run(fn, an=an)` — the boxed error/`BdbQuit` raises in the root's task. - `multi_subactors`: introduces the "collect all errors" pattern — each one-shot catches + stashes its `RemoteActorError` (vs raising) so no child's crash cancels its siblings before they've had their own REPL sessions, then a `BaseExceptionGroup` of the lot raises at the end; preserves the legacy teardown-reap REPL flow exactly (28/28 debugger suite unchanged). - `multi_nested_subactors_error_up_through_nurseries` + `root_cancelled_but_child_is_in_tty_lock`: recursive `spawn_until` levels each block on their one-shot child; the parallel spawner-trees run as bg task-nursery one-shots where the first tree's error cancels the other. - `multi_subactor_root_errors` + `root_timeout_while_child_crashed`: `start_actor()` + bg `Portal.run()` tasks so the root's own error/timeout races the already-crashed children, same as before. - `sync_bp`: TODO-comment x-ref update only. `test_debugger.py`: the nested-nurseries test's final-output patterns update to the new relay shape — the LAST-released leaf REPL's error chain wins each level's relay-vs-cancel race and relays as a `collapse_eg()`-annotated collapsed chain, while the sibling tree is cancelled + absorbed. (The legacy teardown-reap grouped BOTH the `name_error` and bp-quit chains — explaining the previously-mysterious "extra" `src_uid`/`relay_uid` patterns noted in the old TODO.) Gate: `tests/devx/test_debugger.py` = 28 passed, 6 skipped — identical to the pre-migration baseline. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
2026-07-06 16:24:29 +00:00
errors: list[BaseException] = []
2021-02-24 18:39:14 +00:00
async with tractor.open_nursery(
debug_mode=True,
# loglevel='runtime',
Port debugging examples off `run_in_actor` The 8 `examples/debugging/` scripts driven by the pexpect'd `test_debugger.py` REPL-flows (#477 removal), - blocking one-shots (`subactor_error`, `subactor_breakpoint`, `shielded_pause`): straight `to_actor.run(fn, an=an)` — the boxed error/`BdbQuit` raises in the root's task. - `multi_subactors`: introduces the "collect all errors" pattern — each one-shot catches + stashes its `RemoteActorError` (vs raising) so no child's crash cancels its siblings before they've had their own REPL sessions, then a `BaseExceptionGroup` of the lot raises at the end; preserves the legacy teardown-reap REPL flow exactly (28/28 debugger suite unchanged). - `multi_nested_subactors_error_up_through_nurseries` + `root_cancelled_but_child_is_in_tty_lock`: recursive `spawn_until` levels each block on their one-shot child; the parallel spawner-trees run as bg task-nursery one-shots where the first tree's error cancels the other. - `multi_subactor_root_errors` + `root_timeout_while_child_crashed`: `start_actor()` + bg `Portal.run()` tasks so the root's own error/timeout races the already-crashed children, same as before. - `sync_bp`: TODO-comment x-ref update only. `test_debugger.py`: the nested-nurseries test's final-output patterns update to the new relay shape — the LAST-released leaf REPL's error chain wins each level's relay-vs-cancel race and relays as a `collapse_eg()`-annotated collapsed chain, while the sibling tree is cancelled + absorbed. (The legacy teardown-reap grouped BOTH the `name_error` and bp-quit chains — explaining the previously-mysterious "extra" `src_uid`/`relay_uid` patterns noted in the old TODO.) Gate: `tests/devx/test_debugger.py` = 28 passed, 6 skipped — identical to the pre-migration baseline. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
2026-07-06 16:24:29 +00:00
) as an:
async def run_and_collect(fn):
'''
One-shot whose (boxed) error is stashed instead of
raised so a sibling's crash never cancels the others
before they've had their own debugger sessions (the
"collect all errors" the legacy `run_in_actor()` API
did implicitly at nursery teardown).
'''
try:
await tractor.to_actor.run(fn, an=an)
except tractor.RemoteActorError as rae:
errors.append(rae)
# Spawn all one-shot task actors, collecting (vs.
# raising) their errors.
async with trio.open_nursery() as tn:
tn.start_soon(run_and_collect, breakpoint_forever)
tn.start_soon(run_and_collect, name_error)
tn.start_soon(run_and_collect, spawn_error)
if errors:
raise BaseExceptionGroup(
'multi_subactors errored!',
errors,
)
2020-10-04 21:31:02 +00:00
if __name__ == '__main__':
2021-02-24 18:39:14 +00:00
trio.run(main)