From f50202a6af2496296f8dde4f85d7a6ffe3db8406 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 26 Jun 2025 11:00:20 -0400 Subject: [PATCH] Adjust to `trio`'s strict eg nurseries throughout! Using `tractor.trionics.collapse_eg()` as needed to avoid, at the least, crash-worthy (in debug-mode REPL-ing terms) nested cancellation egs that exhibit on SIGINT/ctl-c of each "app" (chart & daemon). Also a bit of renaming of all `trio.Nursery`s to `tn`, the new "task nursery" shorthand-var-name being used in all our other `tractor` related projects. --- piker/brokers/_daemon.py | 5 ++++- piker/clearing/_client.py | 14 ++++++++++---- piker/fsp/_engine.py | 1 + piker/service/_actor_runtime.py | 7 ++++--- piker/ui/_app.py | 2 ++ piker/ui/_chart.py | 1 - piker/ui/_display.py | 5 ++++- piker/ui/_event.py | 11 +++++++++-- piker/ui/_fsp.py | 3 +++ piker/ui/order_mode.py | 1 + 10 files changed, 38 insertions(+), 12 deletions(-) diff --git a/piker/brokers/_daemon.py b/piker/brokers/_daemon.py index 5efb03dd..5414bfb9 100644 --- a/piker/brokers/_daemon.py +++ b/piker/brokers/_daemon.py @@ -96,7 +96,10 @@ async def _setup_persistent_brokerd( # - `open_symbol_search()` # NOTE: see ep invocation details inside `.data.feed`. try: - async with trio.open_nursery() as service_nursery: + async with ( + tractor.trionics.collapse_eg(), + trio.open_nursery() as service_nursery + ): bus: _FeedsBus = feed.get_feed_bus( brokername, service_nursery, diff --git a/piker/clearing/_client.py b/piker/clearing/_client.py index 9bb2aa74..563f7293 100644 --- a/piker/clearing/_client.py +++ b/piker/clearing/_client.py @@ -25,7 +25,10 @@ from typing import TYPE_CHECKING import trio import tractor -from tractor.trionics import broadcast_receiver +from tractor.trionics import ( + broadcast_receiver, + collapse_eg, +) from ._util import ( log, # sub-sys logger @@ -281,8 +284,11 @@ async def open_ems( client._ems_stream = trades_stream # start sync code order msg delivery task - async with trio.open_nursery() as n: - n.start_soon( + async with ( + collapse_eg(), + trio.open_nursery() as tn, + ): + tn.start_soon( relay_orders_from_sync_code, client, fqme, @@ -298,4 +304,4 @@ async def open_ems( ) # stop the sync-msg-relay task on exit. - n.cancel_scope.cancel() + tn.cancel_scope.cancel() diff --git a/piker/fsp/_engine.py b/piker/fsp/_engine.py index acc7309e..5d1fd45a 100644 --- a/piker/fsp/_engine.py +++ b/piker/fsp/_engine.py @@ -498,6 +498,7 @@ async def cascade( func_name: str = func.__name__ async with ( + tractor.trionics.collapse_eg(), # avoid multi-taskc tb in console trio.open_nursery() as tn, ): # TODO: might be better to just make a "restart" method where diff --git a/piker/service/_actor_runtime.py b/piker/service/_actor_runtime.py index a4e3ccf2..91157451 100644 --- a/piker/service/_actor_runtime.py +++ b/piker/service/_actor_runtime.py @@ -200,7 +200,8 @@ async def open_pikerd( reg_addrs, ), tractor.open_nursery() as actor_nursery, - trio.open_nursery() as service_nursery, + tractor.trionics.collapse_eg(), + trio.open_nursery() as service_tn, ): for addr in reg_addrs: if addr not in root_actor.accept_addrs: @@ -211,7 +212,7 @@ async def open_pikerd( # assign globally for future daemon/task creation Services.actor_n = actor_nursery - Services.service_n = service_nursery + Services.service_n = service_tn Services.debug_mode = debug_mode try: @@ -221,7 +222,7 @@ async def open_pikerd( # TODO: is this more clever/efficient? # if 'samplerd' in Services.service_tasks: # await Services.cancel_service('samplerd') - service_nursery.cancel_scope.cancel() + service_tn.cancel_scope.cancel() # TODO: do we even need this? diff --git a/piker/ui/_app.py b/piker/ui/_app.py index 5733e372..68ecb3dd 100644 --- a/piker/ui/_app.py +++ b/piker/ui/_app.py @@ -21,6 +21,7 @@ Main app startup and run. from functools import partial from types import ModuleType +import tractor import trio from piker.ui.qt import ( @@ -116,6 +117,7 @@ async def _async_main( needed_brokermods[brokername] = brokers[brokername] async with ( + tractor.trionics.collapse_eg(), trio.open_nursery() as root_n, ): # set root nursery and task stack for spawning other charts/feeds diff --git a/piker/ui/_chart.py b/piker/ui/_chart.py index afcd7dd0..98b25398 100644 --- a/piker/ui/_chart.py +++ b/piker/ui/_chart.py @@ -33,7 +33,6 @@ import trio from piker.ui.qt import ( QtCore, - QtWidgets, Qt, QLineF, QFrame, diff --git a/piker/ui/_display.py b/piker/ui/_display.py index 46e1b922..690bfb18 100644 --- a/piker/ui/_display.py +++ b/piker/ui/_display.py @@ -1445,7 +1445,10 @@ async def display_symbol_data( # for pause/resume on mouse interaction rt_chart.feed = feed - async with trio.open_nursery() as ln: + async with ( + tractor.trionics.collapse_eg(), + trio.open_nursery() as ln, + ): # if available load volume related built-in display(s) vlm_charts: dict[ str, diff --git a/piker/ui/_event.py b/piker/ui/_event.py index 44797fa4..28d35de0 100644 --- a/piker/ui/_event.py +++ b/piker/ui/_event.py @@ -22,7 +22,10 @@ from contextlib import asynccontextmanager as acm from typing import Callable import trio -from tractor.trionics import gather_contexts +from tractor.trionics import ( + gather_contexts, + collapse_eg, +) from piker.ui.qt import ( QtCore, @@ -207,7 +210,10 @@ async def open_signal_handler( async for args in recv: await async_handler(*args) - async with trio.open_nursery() as tn: + async with ( + collapse_eg(), + trio.open_nursery() as tn + ): tn.start_soon(proxy_to_handler) async with send: yield @@ -242,6 +248,7 @@ async def open_handlers( widget: QWidget streams: list[trio.abc.ReceiveChannel] async with ( + collapse_eg(), trio.open_nursery() as tn, gather_contexts([ open_event_stream( diff --git a/piker/ui/_fsp.py b/piker/ui/_fsp.py index 2e3e392e..ca43ed77 100644 --- a/piker/ui/_fsp.py +++ b/piker/ui/_fsp.py @@ -600,6 +600,7 @@ async def open_fsp_admin( kwargs=kwargs, ) as (cache_hit, cluster_map), + tractor.trionics.collapse_eg(), trio.open_nursery() as tn, ): if cache_hit: @@ -613,6 +614,8 @@ async def open_fsp_admin( ) try: yield admin + + # ??TODO, does this *need* to be inside a finally? finally: # terminate all tasks via signals for key, entry in admin._registry.items(): diff --git a/piker/ui/order_mode.py b/piker/ui/order_mode.py index 47a3bb97..ea6d498a 100644 --- a/piker/ui/order_mode.py +++ b/piker/ui/order_mode.py @@ -792,6 +792,7 @@ async def open_order_mode( brokerd_accounts, ems_dialog_msgs, ), + tractor.trionics.collapse_eg(), trio.open_nursery() as tn, ):