Reraise RAEs in `MsgStream.receive()`; truncate tbs
To avoid showing lowlevel details of exception handling around the underlying call to `return await self._ctx._pld_rx.recv_pld(ipc=self)`, any time a `RemoteActorError` is unpacked (an raised locally) we re-raise it directly from the captured `src_err` captured so as to present to the user/app caller-code an exception raised directly from the `.receive()` frame. This simplifies traceback call-stacks for any `log.exception()` or `pdb`-REPL output filtering out the lower `PldRx` frames by default.multihost_exs
parent
3c5816c977
commit
02812b9f51
|
@ -36,8 +36,8 @@ import warnings
|
||||||
import trio
|
import trio
|
||||||
|
|
||||||
from ._exceptions import (
|
from ._exceptions import (
|
||||||
# _raise_from_no_key_in_msg,
|
|
||||||
ContextCancelled,
|
ContextCancelled,
|
||||||
|
RemoteActorError,
|
||||||
)
|
)
|
||||||
from .log import get_logger
|
from .log import get_logger
|
||||||
from .trionics import (
|
from .trionics import (
|
||||||
|
@ -101,7 +101,7 @@ class MsgStream(trio.abc.Channel):
|
||||||
@property
|
@property
|
||||||
def ctx(self) -> Context:
|
def ctx(self) -> Context:
|
||||||
'''
|
'''
|
||||||
This stream's IPC `Context` ref.
|
A read-only ref to this stream's inter-actor-task `Context`.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
return self._ctx
|
return self._ctx
|
||||||
|
@ -145,9 +145,8 @@ class MsgStream(trio.abc.Channel):
|
||||||
'''
|
'''
|
||||||
__tracebackhide__: bool = hide_tb
|
__tracebackhide__: bool = hide_tb
|
||||||
|
|
||||||
# NOTE: `trio.ReceiveChannel` implements
|
# NOTE FYI: `trio.ReceiveChannel` implements EOC handling as
|
||||||
# EOC handling as follows (aka uses it
|
# follows (aka uses it to gracefully exit async for loops):
|
||||||
# to gracefully exit async for loops):
|
|
||||||
#
|
#
|
||||||
# async def __anext__(self) -> ReceiveType:
|
# async def __anext__(self) -> ReceiveType:
|
||||||
# try:
|
# try:
|
||||||
|
@ -165,48 +164,29 @@ class MsgStream(trio.abc.Channel):
|
||||||
|
|
||||||
src_err: Exception|None = None # orig tb
|
src_err: Exception|None = None # orig tb
|
||||||
try:
|
try:
|
||||||
|
|
||||||
ctx: Context = self._ctx
|
ctx: Context = self._ctx
|
||||||
return await ctx._pld_rx.recv_pld(ipc=self)
|
return await ctx._pld_rx.recv_pld(ipc=self)
|
||||||
|
|
||||||
# XXX: the stream terminates on either of:
|
# XXX: the stream terminates on either of:
|
||||||
# - via `self._rx_chan.receive()` raising after manual closure
|
# - `self._rx_chan.receive()` raising after manual closure
|
||||||
# by the rpc-runtime OR,
|
# by the rpc-runtime,
|
||||||
# - via a received `{'stop': ...}` msg from remote side.
|
# OR
|
||||||
# |_ NOTE: previously this was triggered by calling
|
# - via a `Stop`-msg received from remote peer task.
|
||||||
# ``._rx_chan.aclose()`` on the send side of the channel inside
|
# NOTE
|
||||||
# `Actor._deliver_ctx_payload()`, but now the 'stop' message handling
|
# |_ previously this was triggered by calling
|
||||||
# has been put just above inside `_raise_from_no_key_in_msg()`.
|
# ``._rx_chan.aclose()`` on the send side of the channel
|
||||||
except (
|
# inside `Actor._deliver_ctx_payload()`, but now the 'stop'
|
||||||
trio.EndOfChannel,
|
# message handling gets delegated to `PldRFx.recv_pld()`
|
||||||
) as eoc:
|
# internals.
|
||||||
src_err = eoc
|
except trio.EndOfChannel as eoc:
|
||||||
|
# a graceful stream finished signal
|
||||||
self._eoc = eoc
|
self._eoc = eoc
|
||||||
|
src_err = eoc
|
||||||
|
|
||||||
# TODO: Locally, we want to close this stream gracefully, by
|
# a `ClosedResourceError` indicates that the internal feeder
|
||||||
# terminating any local consumers tasks deterministically.
|
# memory receive channel was closed likely by the runtime
|
||||||
# Once we have broadcast support, we **don't** want to be
|
# after the associated transport-channel disconnected or
|
||||||
# closing this stream and not flushing a final value to
|
# broke.
|
||||||
# remaining (clone) consumers who may not have been
|
|
||||||
# scheduled to receive it yet.
|
|
||||||
# try:
|
|
||||||
# maybe_err_msg_or_res: dict = self._rx_chan.receive_nowait()
|
|
||||||
# if maybe_err_msg_or_res:
|
|
||||||
# log.warning(
|
|
||||||
# 'Discarding un-processed msg:\n'
|
|
||||||
# f'{maybe_err_msg_or_res}'
|
|
||||||
# )
|
|
||||||
# except trio.WouldBlock:
|
|
||||||
# # no queued msgs that might be another remote
|
|
||||||
# # error, so just raise the original EoC
|
|
||||||
# pass
|
|
||||||
|
|
||||||
# raise eoc
|
|
||||||
|
|
||||||
# a ``ClosedResourceError`` indicates that the internal
|
|
||||||
# feeder memory receive channel was closed likely by the
|
|
||||||
# runtime after the associated transport-channel
|
|
||||||
# disconnected or broke.
|
|
||||||
except trio.ClosedResourceError as cre: # by self._rx_chan.receive()
|
except trio.ClosedResourceError as cre: # by self._rx_chan.receive()
|
||||||
src_err = cre
|
src_err = cre
|
||||||
log.warning(
|
log.warning(
|
||||||
|
@ -218,14 +198,15 @@ class MsgStream(trio.abc.Channel):
|
||||||
# terminated and signal this local iterator to stop
|
# terminated and signal this local iterator to stop
|
||||||
drained: list[Exception|dict] = await self.aclose()
|
drained: list[Exception|dict] = await self.aclose()
|
||||||
if drained:
|
if drained:
|
||||||
|
# ?TODO? pass these to the `._ctx._drained_msgs: deque`
|
||||||
|
# and then iterate them as part of any `.wait_for_result()` call?
|
||||||
|
#
|
||||||
# from .devx import pause
|
# from .devx import pause
|
||||||
# await pause()
|
# await pause()
|
||||||
log.warning(
|
log.warning(
|
||||||
'Drained context msgs during closure:\n'
|
'Drained context msgs during closure\n\n'
|
||||||
f'{drained}'
|
f'{drained}'
|
||||||
)
|
)
|
||||||
# TODO: pass these to the `._ctx._drained_msgs: deque`
|
|
||||||
# and then iterate them as part of any `.result()` call?
|
|
||||||
|
|
||||||
# NOTE XXX: if the context was cancelled or remote-errored
|
# NOTE XXX: if the context was cancelled or remote-errored
|
||||||
# but we received the stream close msg first, we
|
# but we received the stream close msg first, we
|
||||||
|
@ -238,28 +219,36 @@ class MsgStream(trio.abc.Channel):
|
||||||
from_src_exc=src_err,
|
from_src_exc=src_err,
|
||||||
)
|
)
|
||||||
|
|
||||||
# propagate any error but hide low-level frame details
|
# propagate any error but hide low-level frame details from
|
||||||
# from the caller by default for debug noise reduction.
|
# the caller by default for console/debug-REPL noise
|
||||||
|
# reduction.
|
||||||
if (
|
if (
|
||||||
hide_tb
|
hide_tb
|
||||||
|
and (
|
||||||
|
|
||||||
# XXX NOTE XXX don't reraise on certain
|
# XXX NOTE special conditions: don't reraise on
|
||||||
# stream-specific internal error types like,
|
# certain stream-specific internal error types like,
|
||||||
#
|
#
|
||||||
# - `trio.EoC` since we want to use the exact instance
|
# - `trio.EoC` since we want to use the exact instance
|
||||||
# to ensure that it is the error that bubbles upward
|
# to ensure that it is the error that bubbles upward
|
||||||
# for silent absorption by `Context.open_stream()`.
|
# for silent absorption by `Context.open_stream()`.
|
||||||
and not self._eoc
|
not self._eoc
|
||||||
|
|
||||||
# - `RemoteActorError` (or `ContextCancelled`) if it gets
|
# - `RemoteActorError` (or subtypes like ctxc)
|
||||||
# raised from `_raise_from_no_key_in_msg()` since we
|
# since we want to present the error as though it is
|
||||||
# want the same (as the above bullet) for any
|
# "sourced" directly from this `.receive()` call and
|
||||||
# `.open_context()` block bubbled error raised by
|
# generally NOT include the stack frames raised from
|
||||||
# any nearby ctx API remote-failures.
|
# inside the `PldRx` and/or the transport stack
|
||||||
# and not isinstance(src_err, RemoteActorError)
|
# layers.
|
||||||
|
or isinstance(src_err, RemoteActorError)
|
||||||
|
)
|
||||||
):
|
):
|
||||||
raise type(src_err)(*src_err.args) from src_err
|
raise type(src_err)(*src_err.args) from src_err
|
||||||
else:
|
else:
|
||||||
|
# for any non-graceful-EOC we want to NOT hide this frame
|
||||||
|
if not self._eoc:
|
||||||
|
__tracebackhide__: bool = False
|
||||||
|
|
||||||
raise src_err
|
raise src_err
|
||||||
|
|
||||||
async def aclose(self) -> list[Exception|dict]:
|
async def aclose(self) -> list[Exception|dict]:
|
||||||
|
@ -385,6 +374,8 @@ class MsgStream(trio.abc.Channel):
|
||||||
if not self._eoc:
|
if not self._eoc:
|
||||||
message: str = (
|
message: str = (
|
||||||
f'Stream self-closed by {self._ctx.side!r}-side before EoC\n'
|
f'Stream self-closed by {self._ctx.side!r}-side before EoC\n'
|
||||||
|
# } bc a stream is a "scope"/msging-phase inside an IPC
|
||||||
|
f'x}}>\n'
|
||||||
f'|_{self}\n'
|
f'|_{self}\n'
|
||||||
)
|
)
|
||||||
log.cancel(message)
|
log.cancel(message)
|
||||||
|
|
Loading…
Reference in New Issue