tractor icon indicating copy to clipboard operation
tractor copied to clipboard

Denoising beg tracebacks

Open goodboy opened this issue 8 months ago • 0 comments

With the move to py3.13 requiring that we use a trio version which turns on strict egs by default, there's definitely some tracebacks-on-console output to be reduced when a process tree is cancelled (commonly via SIGINT or similar from a terminal).

Further trio will be removing the loose-ify-flag in an upcoming version as per their tb warning:

TrioDeprecationWarning: open_nursery(strict_exception_groups=False) is deprecated since Trio 0.25.0; use the default value of True and rewrite exception handlers to handle ExceptionGroups. See See https://trio.readthedocs.io/en/stable/reference-core.html#designing-for-multiple-errors instead

It'd be better to have (a) soln(s) sooner then later.

The main obvious noise issue in tbs is that for many situations there is optional conc via a tn: trio.Nursery but the parent scope doesn't need to know that impl detail and shouldn't need to handle any eg nor use except*.

I've already done a bit of re-tooling to try and suppress ExceptionGroups where appropriate to avoid needless tb noise emanating from some more obvious parts of the runtime where egs definitely should be suppressed:

  • remote errors relayed from the ._rpc._invoke() machinery which allocates an internal trio.Nursery per @context request.

    • it's not useful to see a RemoteActorError boxed runtime-eg (i.e. an eg due to the runtime's impl) when normally the parent task is expecting RemoteAcctorError|ContextCancelled with various fields set to the child-side's application-level error(s) (i.e. errors raised in user code that need to be relayed over IPC).
  • no beg should ever be raised from open_root_actor().

  • the same but from the ._discovery apis which often do task-fan-outs for discovery scans.

  • any egs causes by implicit nurseries in various conc-machineries as part of tractor.trionics apis.

There's prolly more minor cases I could document inside tractor's core that i likely already muted (via the loose-flag) but they should be assessed in the context of all other cases so we can know which muting soln best fits where.


Rsrcs on egs

In case you need a primer/refresher,

  • pep: https://peps.python.org/pep-0654/
  • eg docs: https://docs.python.org/3/library/exceptions.html#exception-groups
  • except* docs: https://docs.python.org/3/reference/compound_stmts.html#except-star

Current internal solns

Besides the deprecated flag usage i already started a module with some different semantic approaches:

  • tractor.trionics._beg contains,

    • collapse_eg() an @acm stackable equivalent of the strict_exception_groups=False flag.

    • is_multi_cancelled() an eg .subgroup() filtering predicate which we should generalize and make more useful for various cases where a beg containing a particular .exceptions set should be absorbed by the runtime.

  • tractor._testing.expect_ctxc gives an outline for how we might want to offer dynamic suppression via the caller/parent-task,

    • using the new from tractor.devx.debug.BoxedMaybeException as a post-raise inspectable to possibly allow for filtering repeated specific cases of .exceptions-sets.
    • see how it could be used per the .devx.debug._post_mortem.[maybe_]open_crash_handler() apis.
    • the new repl_fixture feat's usage may also have some overlaps.

Examples from client apps

Some ugly tbs in consumer code-bases/apps of tractor

  • [ ] here's a big one from the charting UI in piker which seems to not be fully cleaned up by the loose-flag. Specifically it was triggered via a i3wm "close window" request (via the kill bindsym) but you get similar output from a ctl-c on console.

    Jun 13 11:33:38 (chart[0e224a], 15155,
    piker.ui._display.display_symbol_data[085760])) [ERROR] tractor
    _context.py:2267 ctx 'parent'-side exited with BaseExceptionGroup
    
      + Exception Group Traceback (most recent call last):
      |   File "/home/goodboy/repos/tractor/tractor/_context.py", line 2090, in open_context_from_portal
      |     yield ctx, first
      |   File "/home/goodboy/repos/piker/piker/clearing/_client.py", line 288, in open_ems
      |     collapse_eg(),
      |     ~~~~~~~~~~~^^
      |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
      |     await self.gen.athrow(value)
      |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 63, in collapse_eg
      |     raise beg
      |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 56, in collapse_eg
      |     yield
      |   File "/home/goodboy/repos/piker/piker/clearing/_client.py", line 289, in open_ems
      |     trio.open_nursery(
      |     ~~~~~~~~~~~~~~~~~^
      |         # strict_exception_groups=False,
      |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      |     ) as tn,
      |     ^
      |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1058, in __aexit__
      |     raise combined_error_from_nursery
      | BaseExceptionGroup: Exceptions from Trio nursery (2 sub-exceptions)
      +-+---------------- 1 ----------------
        | Traceback (most recent call last):
        |   File "/home/goodboy/repos/piker/piker/clearing/_client.py", line 199, in relay_orders_from_sync_code
        |     async for cmd in sync_order_cmds:
        |     ...<12 lines>...
        |             )
        |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 689, in __anext__
        |     return await self.receive()
        |            ^^^^^^^^^^^^^^^^^^^^
        |   File "/home/goodboy/repos/tractor/tractor/trionics/_broadcast.py", line 355, in receive
        |     return await self._receive_from_underlying(key, state)
        |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        |   File "/home/goodboy/repos/tractor/tractor/trionics/_broadcast.py", line 280, in _receive_from_underlying
        |     value = await self._recv()
        |             ^^^^^^^^^^^^^^^^^^
        |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 362, in receive
        |     return await trio.lowlevel.wait_task_rescheduled(abort_fn)  # type: ignore[no-any-return]
        |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
        |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
        |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
        |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
        |     raise captured_error
        |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
        |     raise Cancelled._create()
        | trio.Cancelled: Cancelled
        +---------------- 2 ----------------
        | Exception Group Traceback (most recent call last):
        |   File "/home/goodboy/repos/piker/piker/clearing/_client.py", line 300, in open_ems
        |     yield (
        |     ...<5 lines>...
        |     )
        |   File "/home/goodboy/repos/piker/piker/ui/order_mode.py", line 795, in open_order_mode
        |     trio.open_nursery(
        |     ~~~~~~~~~~~~~~~~~^
        |         strict_exception_groups=False,
        |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        |     ) as tn,
        |     ^
        |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1058, in __aexit__
        |     raise combined_error_from_nursery
        | BaseExceptionGroup: Exceptions from Trio nursery (3 sub-exceptions)
        | This is a "loose" ExceptionGroup, and may be collapsed by Trio if it only contains one exception - typically after `Cancelled` has been stripped from it. Note this has consequences for exception handling, and strict_exception_groups=True is recommended.
        +-+---------------- 1 ----------------
          | Traceback (most recent call last):
          |   File "/home/goodboy/repos/piker/piker/ui/_position.py", line 113, in update_pnl_from_feed
          |     async for quotes in bstream:
          |     ...<39 lines>...
          |                     order_mode.pane.pnl_label.format(pnl=pnl_val)
          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 689, in __anext__
          |     return await self.receive()
          |            ^^^^^^^^^^^^^^^^^^^^
          |   File "/home/goodboy/repos/tractor/tractor/trionics/_broadcast.py", line 365, in receive
          |     await ev.wait()
          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_sync.py", line 102, in wait
          |     await _core.wait_task_rescheduled(abort_fn)
          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
          |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
          |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
          |     raise captured_error
          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
          |     raise Cancelled._create()
          | trio.Cancelled: Cancelled
          +---------------- 2 ----------------
          | Traceback (most recent call last):
          |   File "/home/goodboy/repos/piker/piker/ui/order_mode.py", line 1028, in process_trades_and_update_ui
          |     async for msg in trades_stream:
          |     ...<4 lines>...
          |         )
          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 689, in __anext__
          |     return await self.receive()
          |            ^^^^^^^^^^^^^^^^^^^^
          |   File "/home/goodboy/repos/tractor/tractor/_streaming.py", line 223, in receive
          |     pld = await ctx._pld_rx.recv_pld(
          |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
          |     ...<2 lines>...
          |     )
          |     ^
          |   File "/home/goodboy/repos/tractor/tractor/msg/_ops.py", line 259, in recv_pld
          |     await ipc._rx_chan.receive()
          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 362, in receive
          |     return await trio.lowlevel.wait_task_rescheduled(abort_fn)  # type: ignore[no-any-return]
          |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
          |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
          |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
          |     raise captured_error
          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
          |     raise Cancelled._create()
          | trio.Cancelled: Cancelled
          +---------------- 3 ----------------
          | Exception Group Traceback (most recent call last):
          |   File "/home/goodboy/repos/piker/piker/ui/order_mode.py", line 982, in open_order_mode
          |     open_form_input_handling(
          |     ~~~~~~~~~~~~~~~~~~~~~~~~^
          |         form,
          |         ^^^^^
          |         focus_next=chart.linked.godwidget,
          |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
          |         on_value_change=order_pane.on_ui_settings_change,
          |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
          |     ),
          |     ^
          |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
          |     await self.gen.athrow(value)
          |   File "/home/goodboy/repos/piker/piker/ui/_forms.py", line 531, in open_form_input_handling
          |     async with open_handlers(
          |                ~~~~~~~~~~~~~^
          |
          |
          |     ...<13 lines>...
          |         filter_auto_repeats=True,
          |         ^^^^^^^^^^^^^^^^^^^^^^^^^
          |     ):
          |     ^
          |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
          |     await self.gen.athrow(value)
          |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 251, in open_handlers
          |     collapse_eg(),
          |     ~~~~~~~~~~~^^
          |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
          |     await self.gen.athrow(value)
          |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 63, in collapse_eg
          |     raise beg
          |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 56, in collapse_eg
          |     yield
          |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 252, in open_handlers
          |     trio.open_nursery(
          |     ~~~~~~~~~~~~~~~~~^
          |         strict_exception_groups=False,
          |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
          |     ) as tn,
          |     ^
          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1058, in __aexit__
          |     raise combined_error_from_nursery
          | BaseExceptionGroup: Exceptions from Trio nursery (5 sub-exceptions)
          | This is a "loose" ExceptionGroup, and may be collapsed by Trio if it only contains one exception - typically after `Cancelled` has been stripped from it. Note this has consequences for exception handling, and strict_exception_groups=True is recommended.
          +-+---------------- 1 ----------------
            | Traceback (most recent call last):
            |   File "/home/goodboy/repos/piker/piker/ui/_forms.py", line 445, in handle_field_input
            |     async for kbmsg in recv_chan:
            |     ...<30 lines>...
            |                 on_value_change(key, value)
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 689, in __anext__
            |     return await self.receive()
            |            ^^^^^^^^^^^^^^^^^^^^
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 362, in receive
            |     return await trio.lowlevel.wait_task_rescheduled(abort_fn)  # type: ignore[no-any-return]
            |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
            |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
            |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
            |     raise captured_error
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
            |     raise Cancelled._create()
            | trio.Cancelled: Cancelled
            +---------------- 2 ----------------
            | Traceback (most recent call last):
            |   File "/home/goodboy/repos/piker/piker/ui/_forms.py", line 445, in handle_field_input
            |     async for kbmsg in recv_chan:
            |     ...<30 lines>...
            |                 on_value_change(key, value)
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 689, in __anext__
            |     return await self.receive()
            |            ^^^^^^^^^^^^^^^^^^^^
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 362, in receive
            |     return await trio.lowlevel.wait_task_rescheduled(abort_fn)  # type: ignore[no-any-return]
            |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
            |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
            |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
            |     raise captured_error
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
            |     raise Cancelled._create()
            | trio.Cancelled: Cancelled
            +---------------- 3 ----------------
            | Traceback (most recent call last):
            |   File "/home/goodboy/repos/piker/piker/ui/_forms.py", line 445, in handle_field_input
            |     async for kbmsg in recv_chan:
            |     ...<30 lines>...
            |                 on_value_change(key, value)
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 689, in __anext__
            |     return await self.receive()
            |            ^^^^^^^^^^^^^^^^^^^^
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 362, in receive
            |     return await trio.lowlevel.wait_task_rescheduled(abort_fn)  # type: ignore[no-any-return]
            |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
            |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
            |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
            |     raise captured_error
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
            |     raise Cancelled._create()
            | trio.Cancelled: Cancelled
            +---------------- 4 ----------------
            | Traceback (most recent call last):
            |   File "/home/goodboy/repos/piker/piker/ui/_forms.py", line 445, in handle_field_input
            |     async for kbmsg in recv_chan:
            |     ...<30 lines>...
            |                 on_value_change(key, value)
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 689, in __anext__
            |     return await self.receive()
            |            ^^^^^^^^^^^^^^^^^^^^
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 362, in receive
            |     return await trio.lowlevel.wait_task_rescheduled(abort_fn)  # type: ignore[no-any-return]
            |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
            |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
            |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
            |     raise captured_error
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
            |     raise Cancelled._create()
            | trio.Cancelled: Cancelled
            +---------------- 5 ----------------
            | Exception Group Traceback (most recent call last):
            |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 255, in open_handlers
            |     gather_contexts([
            |     ~~~~~~~~~~~~~~~^^
            |         open_event_stream(
            |         ^^^^^^^^^^^^^^^^^^
            |     ...<4 lines>...
            |         for widget in source_widgets
            |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            |     ]) as streams,
            |     ^^
            |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
            |     await self.gen.athrow(value)
            |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 154, in gather_contexts
            |     collapse_eg(),
            |     ~~~~~~~~~~~^^
            |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
            |     await self.gen.athrow(value)
            |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 63, in collapse_eg
            |     raise beg
            |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 56, in collapse_eg
            |     yield
            |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 155, in gather_contexts
            |     trio.open_nursery(
            |     ~~~~~~~~~~~~~~~~~^
            |         # strict_exception_groups=False,
            |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            |     ...<2 lines>...
            |         # just raise that" interface?
            |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            |     ) as tn,
            |     ^
            |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1058, in __aexit__
            |     raise combined_error_from_nursery
            | BaseExceptionGroup: Exceptions from Trio nursery (5 sub-exceptions)
            +-+---------------- 1 ----------------
              | Traceback (most recent call last):
              |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 189, in open_event_stream
              |     yield recv
              |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 101, in _enter_and_wait
              |     await parent_exit.wait()
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_sync.py", line 102, in wait
              |     await _core.wait_task_rescheduled(abort_fn)
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
              |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
              |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
              |     raise captured_error
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
              |     raise Cancelled._create()
              | trio.Cancelled: Cancelled
              |
              | During handling of the above exception, another exception occurred:
              |
              | Traceback (most recent call last):
              |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 92, in _enter_and_wait
              |     async with mngr as value:
              |                ^^^^
              |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
              |     await self.gen.athrow(value)
              |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 188, in open_event_stream
              |     async with send:
              |                ^^^^
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 309, in __aexit__
              |     await self.aclose()
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 289, in aclose
              |     await trio.lowlevel.checkpoint()
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 2918, in checkpoint
              |     await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
              |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
              |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
              |     raise captured_error
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
              |     raise Cancelled._create()
              | trio.Cancelled: Cancelled
              +---------------- 2 ----------------
              | Traceback (most recent call last):
              |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 189, in open_event_stream
              |     yield recv
              |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 101, in _enter_and_wait
              |     await parent_exit.wait()
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_sync.py", line 102, in wait
              |     await _core.wait_task_rescheduled(abort_fn)
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
              |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
              |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
              |     raise captured_error
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
              |     raise Cancelled._create()
              | trio.Cancelled: Cancelled
              |
              | During handling of the above exception, another exception occurred:
              |
              | Traceback (most recent call last):
              |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 92, in _enter_and_wait
              |     async with mngr as value:
              |                ^^^^
              |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
              |     await self.gen.athrow(value)
              |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 188, in open_event_stream
              |     async with send:
              |                ^^^^
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 309, in __aexit__
              |     await self.aclose()
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 289, in aclose
              |     await trio.lowlevel.checkpoint()
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 2918, in checkpoint
              |     await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
              |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
              |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
              |     raise captured_error
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
              |     raise Cancelled._create()
              | trio.Cancelled: Cancelled
              +---------------- 3 ----------------
              | Traceback (most recent call last):
              |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 189, in open_event_stream
              |     yield recv
              |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 101, in _enter_and_wait
              |     await parent_exit.wait()
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_sync.py", line 102, in wait
              |     await _core.wait_task_rescheduled(abort_fn)
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
              |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
              |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
              |     raise captured_error
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
              |     raise Cancelled._create()
              | trio.Cancelled: Cancelled
              |
              | During handling of the above exception, another exception occurred:
              |
              | Traceback (most recent call last):
              |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 92, in _enter_and_wait
              |     async with mngr as value:
              |                ^^^^
              |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
              |     await self.gen.athrow(value)
              |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 188, in open_event_stream
              |     async with send:
              |                ^^^^
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 309, in __aexit__
              |     await self.aclose()
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 289, in aclose
              |     await trio.lowlevel.checkpoint()
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 2918, in checkpoint
              |     await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
              |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
              |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
              |     raise captured_error
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
              |     raise Cancelled._create()
              | trio.Cancelled: Cancelled
              +---------------- 4 ----------------
              | Traceback (most recent call last):
              |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 189, in open_event_stream
              |     yield recv
              |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 101, in _enter_and_wait
              |     await parent_exit.wait()
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_sync.py", line 102, in wait
              |     await _core.wait_task_rescheduled(abort_fn)
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
              |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
              |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
              |     raise captured_error
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
              |     raise Cancelled._create()
              | trio.Cancelled: Cancelled
              |
              | During handling of the above exception, another exception occurred:
              |
              | Traceback (most recent call last):
              |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 92, in _enter_and_wait
              |     async with mngr as value:
              |                ^^^^
              |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
              |     await self.gen.athrow(value)
              |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 188, in open_event_stream
              |     async with send:
              |                ^^^^
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 309, in __aexit__
              |     await self.aclose()
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 289, in aclose
              |     await trio.lowlevel.checkpoint()
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 2918, in checkpoint
              |     await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
              |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
              |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
              |     raise captured_error
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
              |     raise Cancelled._create()
              | trio.Cancelled: Cancelled
              +---------------- 5 ----------------
              | Exception Group Traceback (most recent call last):
              |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 176, in gather_contexts
              |     yield tuple(unwrapped.values())
              |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 274, in open_handlers
              |     yield
              |   File "/home/goodboy/repos/piker/piker/ui/_forms.py", line 548, in open_form_input_handling
              |     yield form
              |   File "/home/goodboy/repos/piker/piker/ui/order_mode.py", line 1015, in open_order_mode
              |     yield mode
              |   File "/home/goodboy/repos/piker/piker/ui/_display.py", line 1623, in display_symbol_data
              |     rt_chart.view.open_async_input_handler(
              |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
              |         dss=dss,
              |         ^^^^^^^^
              |     ),
              |     ^
              |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
              |     await self.gen.athrow(value)
              |   File "/home/goodboy/repos/piker/piker/ui/_interaction.py", line 604, in open_async_input_handler
              |     _event.open_handlers(
              |     ~~~~~~~~~~~~~~~~~~~~^
              |         [self],
              |         ^^^^^^^
              |     ...<7 lines>...
              |         ),
              |         ^^
              |     ),
              |     ^
              |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
              |     await self.gen.athrow(value)
              |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 251, in open_handlers
              |     collapse_eg(),
              |     ~~~~~~~~~~~^^
              |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
              |     await self.gen.athrow(value)
              |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 63, in collapse_eg
              |     raise beg
              |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 56, in collapse_eg
              |     yield
              |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 252, in open_handlers
              |     trio.open_nursery(
              |     ~~~~~~~~~~~~~~~~~^
              |         strict_exception_groups=False,
              |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
              |     ) as tn,
              |     ^
              |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1058, in __aexit__
              |     raise combined_error_from_nursery
              | BaseExceptionGroup: Exceptions from Trio nursery (2 sub-exceptions)
              | This is a "loose" ExceptionGroup, and may be collapsed by Trio if it only contains one exception - typically after `Cancelled` has been stripped from it. Note this has consequences for exception handling, and strict_exception_groups=True is recommended.
              +-+---------------- 1 ----------------
                | Traceback (most recent call last):
                |   File "/home/goodboy/repos/piker/piker/ui/_interaction.py", line 131, in handle_viewmode_kb_inputs
                |     async for kbmsg in recv_chan:
                |     ...<323 lines>...
                |         last = time.time()
                |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 689, in __anext__
                |     return await self.receive()
                |            ^^^^^^^^^^^^^^^^^^^^
                |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 362, in receive
                |     return await trio.lowlevel.wait_task_rescheduled(abort_fn)  # type: ignore[no-any-return]
                |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
                |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
                |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
                |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
                |     raise captured_error
                |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
                |     raise Cancelled._create()
                | trio.Cancelled: Cancelled
                +---------------- 2 ----------------
                | Exception Group Traceback (most recent call last):
                |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 255, in open_handlers
                |     gather_contexts([
                |     ~~~~~~~~~~~~~~~^^
                |         open_event_stream(
                |         ^^^^^^^^^^^^^^^^^^
                |     ...<4 lines>...
                |         for widget in source_widgets
                |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                |     ]) as streams,
                |     ^^
                |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                |     await self.gen.athrow(value)
                |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 154, in gather_contexts
                |     collapse_eg(),
                |     ~~~~~~~~~~~^^
                |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                |     await self.gen.athrow(value)
                |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 63, in collapse_eg
                |     raise beg
                |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 56, in collapse_eg
                |     yield
                |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 155, in gather_contexts
                |     trio.open_nursery(
                |     ~~~~~~~~~~~~~~~~~^
                |         # strict_exception_groups=False,
                |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                |     ...<2 lines>...
                |         # just raise that" interface?
                |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                |     ) as tn,
                |     ^
                |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1058, in __aexit__
                |     raise combined_error_from_nursery
                | BaseExceptionGroup: Exceptions from Trio nursery (2 sub-exceptions)
                +-+---------------- 1 ----------------
                  | Traceback (most recent call last):
                  |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 189, in open_event_stream
                  |     yield recv
                  |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 101, in _enter_and_wait
                  |     await parent_exit.wait()
                  |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_sync.py", line 102, in wait
                  |     await _core.wait_task_rescheduled(abort_fn)
                  |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
                  |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
                  |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
                  |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
                  |     raise captured_error
                  |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
                  |     raise Cancelled._create()
                  | trio.Cancelled: Cancelled
                  |
                  | During handling of the above exception, another exception occurred:
                  |
                  | Traceback (most recent call last):
                  |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 92, in _enter_and_wait
                  |     async with mngr as value:
                  |                ^^^^
                  |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                  |     await self.gen.athrow(value)
                  |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 188, in open_event_stream
                  |     async with send:
                  |                ^^^^
                  |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 309, in __aexit__
                  |     await self.aclose()
                  |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 289, in aclose
                  |     await trio.lowlevel.checkpoint()
                  |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 2918, in checkpoint
                  |     await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)
                  |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
                  |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
                  |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
                  |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
                  |     raise captured_error
                  |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
                  |     raise Cancelled._create()
                  | trio.Cancelled: Cancelled
                  +---------------- 2 ----------------
                  | Exception Group Traceback (most recent call last):
                  |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 176, in gather_contexts
                  |     yield tuple(unwrapped.values())
                  |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 274, in open_handlers
                  |     yield
                  |   File "/home/goodboy/repos/piker/piker/ui/_interaction.py", line 615, in open_async_input_handler
                  |     _event.open_handlers(
                  |     ~~~~~~~~~~~~~~~~~~~~^
                  |         [self],
                  |         ^^^^^^^
                  |     ...<6 lines>...
                  |         ),
                  |         ^^
                  |     ),
                  |     ^
                  |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                  |     await self.gen.athrow(value)
                  |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 251, in open_handlers
                  |     collapse_eg(),
                  |     ~~~~~~~~~~~^^
                  |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                  |     await self.gen.athrow(value)
                  |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 63, in collapse_eg
                  |     raise beg
                  |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 56, in collapse_eg
                  |     yield
                  |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 252, in open_handlers
                  |     trio.open_nursery(
                  |     ~~~~~~~~~~~~~~~~~^
                  |         strict_exception_groups=False,
                  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  |     ) as tn,
                  |     ^
                  |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1058, in __aexit__
                  |     raise combined_error_from_nursery
                  | BaseExceptionGroup: Exceptions from Trio nursery (2 sub-exceptions)
                  | This is a "loose" ExceptionGroup, and may be collapsed by Trio if it only contains one exception - typically after `Cancelled` has been stripped from it. Note this has consequences for exception handling, and strict_exception_groups=True is recommended.
                  +-+---------------- 1 ----------------
                    | Traceback (most recent call last):
                    |   File "/home/goodboy/repos/piker/piker/ui/_interaction.py", line 466, in handle_viewmode_mouse
                    |     async for msg in recv_chan:
                    |     ...<18 lines>...
                    |             view.order_mode.submit_order()
                    |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 689, in __anext__
                    |     return await self.receive()
                    |            ^^^^^^^^^^^^^^^^^^^^
                    |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 362, in receive
                    |     return await trio.lowlevel.wait_task_rescheduled(abort_fn)  # type: ignore[no-any-return]
                    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                    |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
                    |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
                    |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
                    |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
                    |     raise captured_error
                    |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
                    |     raise Cancelled._create()
                    | trio.Cancelled: Cancelled
                    +---------------- 2 ----------------
                    | Exception Group Traceback (most recent call last):
                    |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 255, in open_handlers
                    |     gather_contexts([
                    |     ~~~~~~~~~~~~~~~^^
                    |         open_event_stream(
                    |         ^^^^^^^^^^^^^^^^^^
                    |     ...<4 lines>...
                    |         for widget in source_widgets
                    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                    |     ]) as streams,
                    |     ^^
                    |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                    |     await self.gen.athrow(value)
                    |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 154, in gather_contexts
                    |     collapse_eg(),
                    |     ~~~~~~~~~~~^^
                    |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                    |     await self.gen.athrow(value)
                    |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 63, in collapse_eg
                    |     raise beg
                    |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 56, in collapse_eg
                    |     yield
                    |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 155, in gather_contexts
                    |     trio.open_nursery(
                    |     ~~~~~~~~~~~~~~~~~^
                    |         # strict_exception_groups=False,
                    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                    |     ...<2 lines>...
                    |         # just raise that" interface?
                    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                    |     ) as tn,
                    |     ^
                    |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1058, in __aexit__
                    |     raise combined_error_from_nursery
                    | BaseExceptionGroup: Exceptions from Trio nursery (2 sub-exceptions)
                    +-+---------------- 1 ----------------
                      | Traceback (most recent call last):
                      |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 189, in open_event_stream
                      |     yield recv
                      |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 101, in _enter_and_wait
                      |     await parent_exit.wait()
                      |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_sync.py", line 102, in wait
                      |     await _core.wait_task_rescheduled(abort_fn)
                      |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
                      |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
                      |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
                      |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
                      |     raise captured_error
                      |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
                      |     raise Cancelled._create()
                      | trio.Cancelled: Cancelled
                      |
                      | During handling of the above exception, another exception occurred:
                      |
                      | Traceback (most recent call last):
                      |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 92, in _enter_and_wait
                      |     async with mngr as value:
                      |                ^^^^
                      |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                      |     await self.gen.athrow(value)
                      |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 188, in open_event_stream
                      |     async with send:
                      |                ^^^^
                      |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 309, in __aexit__
                      |     await self.aclose()
                      |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 289, in aclose
                      |     await trio.lowlevel.checkpoint()
                      |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 2918, in checkpoint
                      |     await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)
                      |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
                      |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
                      |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
                      |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
                      |     raise captured_error
                      |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
                      |     raise Cancelled._create()
                      | trio.Cancelled: Cancelled
                      +---------------- 2 ----------------
                      | Exception Group Traceback (most recent call last):
                      |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 176, in gather_contexts
                      |     yield tuple(unwrapped.values())
                      |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 274, in open_handlers
                      |     yield
                      |   File "/home/goodboy/repos/piker/piker/ui/_interaction.py", line 626, in open_async_input_handler
                      |     yield self
                      |   File "/home/goodboy/repos/piker/piker/ui/_display.py", line 1626, in display_symbol_data
                      |     hist_chart.view.open_async_input_handler(
                      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
                      |         dss=dss,
                      |         ^^^^^^^^
                      |     ),
                      |     ^
                      |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                      |     await self.gen.athrow(value)
                      |   File "/home/goodboy/repos/piker/piker/ui/_interaction.py", line 604, in open_async_input_handler
                      |     _event.open_handlers(
                      |     ~~~~~~~~~~~~~~~~~~~~^
                      |         [self],
                      |         ^^^^^^^
                      |     ...<7 lines>...
                      |         ),
                      |         ^^
                      |     ),
                      |     ^
                      |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                      |     await self.gen.athrow(value)
                      |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 251, in open_handlers
                      |     collapse_eg(),
                      |     ~~~~~~~~~~~^^
                      |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                      |     await self.gen.athrow(value)
                      |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 63, in collapse_eg
                      |     raise beg
                      |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 56, in collapse_eg
                      |     yield
                      |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 252, in open_handlers
                      |     trio.open_nursery(
                      |     ~~~~~~~~~~~~~~~~~^
                      |         strict_exception_groups=False,
                      |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                      |     ) as tn,
                      |     ^
                      |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1058, in __aexit__
                      |     raise combined_error_from_nursery
                      | BaseExceptionGroup: Exceptions from Trio nursery (2 sub-exceptions)
                      | This is a "loose" ExceptionGroup, and may be collapsed by Trio if it only contains one exception - typically after `Cancelled` has been stripped from it. Note this has consequences for exception handling, and strict_exception_groups=True is recommended.
                      +-+---------------- 1 ----------------
                        | Traceback (most recent call last):
                        |   File "/home/goodboy/repos/piker/piker/ui/_interaction.py", line 131, in handle_viewmode_kb_inputs
                        |     async for kbmsg in recv_chan:
                        |     ...<323 lines>...
                        |         last = time.time()
                        |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 689, in __anext__
                        |     return await self.receive()
                        |            ^^^^^^^^^^^^^^^^^^^^
                        |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 362, in receive
                        |     return await trio.lowlevel.wait_task_rescheduled(abort_fn)  # type: ignore[no-any-return]
                        |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                        |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
                        |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
                        |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
                        |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
                        |     raise captured_error
                        |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
                        |     raise Cancelled._create()
                        | trio.Cancelled: Cancelled
                        +---------------- 2 ----------------
                        | Exception Group Traceback (most recent call last):
                        |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 255, in open_handlers
                        |     gather_contexts([
                        |     ~~~~~~~~~~~~~~~^^
                        |         open_event_stream(
                        |         ^^^^^^^^^^^^^^^^^^
                        |     ...<4 lines>...
                        |         for widget in source_widgets
                        |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                        |     ]) as streams,
                        |     ^^
                        |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                        |     await self.gen.athrow(value)
                        |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 154, in gather_contexts
                        |     collapse_eg(),
                        |     ~~~~~~~~~~~^^
                        |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                        |     await self.gen.athrow(value)
                        |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 63, in collapse_eg
                        |     raise beg
                        |   File "/home/goodboy/repos/tractor/tractor/trionics/_beg.py", line 56, in collapse_eg
                        |     yield
                        |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 155, in gather_contexts
                        |     trio.open_nursery(
                        |     ~~~~~~~~~~~~~~~~~^
                        |         # strict_exception_groups=False,
                        |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                        |     ...<2 lines>...
                        |         # just raise that" interface?
                        |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                        |     ) as tn,
                        |     ^
                        |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1058, in __aexit__
                        |     raise combined_error_from_nursery
                        | BaseExceptionGroup: Exceptions from Trio nursery (2 sub-exceptions)
                        +-+---------------- 1 ----------------
                          | Traceback (most recent call last):
                          |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 189, in open_event_stream
                          |     yield recv
                          |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 101, in _enter_and_wait
                          |     await parent_exit.wait()
                          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_sync.py", line 102, in wait
                          |     await _core.wait_task_rescheduled(abort_fn)
                          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
                          |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
                          |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
                          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
                          |     raise captured_error
                          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
                          |     raise Cancelled._create()
                          | trio.Cancelled: Cancelled
                          |
                          | During handling of the above exception, another exception occurred:
                          |
                          | Traceback (most recent call last):
                          |   File "/home/goodboy/repos/tractor/tractor/trionics/_mngrs.py", line 92, in _enter_and_wait
                          |     async with mngr as value:
                          |                ^^^^
                          |   File "/usr/lib/python3.13/contextlib.py", line 235, in __aexit__
                          |     await self.gen.athrow(value)
                          |   File "/home/goodboy/repos/piker/piker/ui/_event.py", line 188, in open_event_stream
                          |     async with send:
                          |                ^^^^
                          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_abc.py", line 309, in __aexit__
                          |     await self.aclose()
                          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_channel.py", line 289, in aclose
                          |     await trio.lowlevel.checkpoint()
                          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 2918, in checkpoint
                          |     await _core.wait_task_rescheduled(lambda _: _core.Abort.SUCCEEDED)
                          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_traps.py", line 208, in wait_task_rescheduled
                          |     return (await _async_yield(WaitTaskRescheduled(abort_func))).unwrap()
                          |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
                          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/outcome/_impl.py", line 213, in unwrap
                          |     raise captured_error
                          |   File "/home/goodboy/repos/piker/py313/lib/python3.13/site-packages/trio/_core/_run.py", line 1567, in raise_cancel
                          |     raise Cancelled._create()
                          | trio.Cancelled: Cancelled
                          +---------------- 2 ----------------
                          | ... (max_group_depth is 10)
                          +------------------------------------
    /home/goodboy/repos/tractor/tractor/_supervise.py:327: TrioDeprecationWarning: open_nursery(strict_exception_groups=False) is deprecated since Trio 0.25.0; use the default value of True and rewrite exception handlers to handle ExceptionGroups. See https://trio.readthedocs.io/en/stable/reference-core.html#designing-for-multiple-errors instead (https://github.com/python-trio/trio/issues/2929)
      async with trio.open_nursery(
    Terminated!
    

goodboy avatar Jun 13 '25 15:06 goodboy