Use new `Msg[Co]Dec` repr meths in `._exceptions`

Particularly when logging around `MsgTypeError`s.

Other:
- make `_raise_from_unexpected_msg()`'s `expect_msg` a non-default value
  arg, must always be passed by caller.
- drop `'canceller'` from `_body_fields` ow it shows up twice for ctxc.
- use `.msg.pretty_struct.pformat()`.
- parameterize `RemoteActorError.reprol()` (repr-one-line method) to
  show `RemoteActorError[<self.boxed_type_str>]( ..` to make obvi
  the boxed remote error type.
- re-impl `.boxed_type_str` as `str`-casting the `.boxed_type` value
  which is guaranteed to render non-`None`.
runtime_to_msgspec
Tyler Goodlet 2024-04-26 13:03:07 -04:00
parent c383978402
commit a5a0e6854b
1 changed files with 23 additions and 13 deletions

View File

@ -54,6 +54,7 @@ from tractor.msg import (
from tractor.msg.pretty_struct import ( from tractor.msg.pretty_struct import (
iter_fields, iter_fields,
Struct, Struct,
pformat as struct_format,
) )
if TYPE_CHECKING: if TYPE_CHECKING:
@ -108,6 +109,10 @@ _body_fields: list[str] = list(
'relay_path', 'relay_path',
'_msg_dict', '_msg_dict',
'cid', 'cid',
# since only ctxc should show it but `Error` does
# have it as an optional field.
'canceller',
} }
) )
@ -382,6 +387,9 @@ class RemoteActorError(Exception):
''' '''
Error type raised by original remote faulting actor. Error type raised by original remote faulting actor.
When the error has only been relayed a single actor-hop
this will be the same as the `.boxed_type`.
''' '''
if self._src_type is None: if self._src_type is None:
self._src_type = get_err_type( self._src_type = get_err_type(
@ -396,7 +404,8 @@ class RemoteActorError(Exception):
String-name of the (last hop's) boxed error type. String-name of the (last hop's) boxed error type.
''' '''
return self._ipc_msg.boxed_type_str bt: Type[BaseException] = self.boxed_type
return str(bt.__name__)
@property @property
def boxed_type(self) -> str: def boxed_type(self) -> str:
@ -492,7 +501,11 @@ class RemoteActorError(Exception):
''' '''
# TODO: use this matryoshka emjoi XD # TODO: use this matryoshka emjoi XD
# => 🪆 # => 🪆
reprol_str: str = f'{type(self).__name__}(' reprol_str: str = (
f'{type(self).__name__}' # type name
f'[{self.boxed_type_str}]' # parameterized by boxed type
'(' # init-style look
)
_repr: str = self._mk_fields_str( _repr: str = self._mk_fields_str(
self.reprol_fields, self.reprol_fields,
end_char=' ', end_char=' ',
@ -653,8 +666,8 @@ class MsgTypeError(
- `Yield` - `Yield`
- TODO: any embedded `.pld` type defined by user code? - TODO: any embedded `.pld` type defined by user code?
Normally the source of an error is re-raised from some `.msg._codec` Normally the source of an error is re-raised from some
decode which itself raises in a backend interchange `.msg._codec` decode which itself raises in a backend interchange
lib (eg. a `msgspec.ValidationError`). lib (eg. a `msgspec.ValidationError`).
''' '''
@ -939,7 +952,7 @@ def _raise_from_unexpected_msg(
src_err: AttributeError, src_err: AttributeError,
log: StackLevelAdapter, # caller specific `log` obj log: StackLevelAdapter, # caller specific `log` obj
expect_msg: str = Yield, expect_msg: Type[MsgType],
# allow "deeper" tbs when debugging B^o # allow "deeper" tbs when debugging B^o
hide_tb: bool = True, hide_tb: bool = True,
@ -1037,16 +1050,16 @@ def _raise_from_unexpected_msg(
ctx.maybe_raise() ctx.maybe_raise()
raise eoc from src_err raise eoc from src_err
# TODO: our own transport/IPC-broke error subtype?
if stream._closed: if stream._closed:
# TODO: our own error subtype?
raise trio.ClosedResourceError('This stream was closed') raise trio.ClosedResourceError('This stream was closed')
# always re-raise the source error if no translation error case # always re-raise the source error if no translation error case
# is activated above. # is activated above.
raise MessagingError( raise MessagingError(
f"{_type} was expecting a {expect_msg} message" f'{_type} was expecting a {expect_msg.__name__!r} message'
" BUT received a non-error msg:\n" ' BUT received a non-error msg:\n\n'
f'{pformat(msg)}' f'{struct_format(msg)}'
) from src_err ) from src_err
@ -1079,13 +1092,11 @@ def _mk_msg_type_err(
# no src error from `msgspec.msgpack.Decoder.decode()` so # no src error from `msgspec.msgpack.Decoder.decode()` so
# prolly a manual type-check on our part. # prolly a manual type-check on our part.
if message is None: if message is None:
fmt_spec: str = codec.pformat_msg_spec()
fmt_stack: str = ( fmt_stack: str = (
'\n'.join(traceback.format_stack(limit=3)) '\n'.join(traceback.format_stack(limit=3))
) )
tb_fmt: str = pformat_boxed_tb( tb_fmt: str = pformat_boxed_tb(
tb_str=fmt_stack, tb_str=fmt_stack,
# fields_str=header,
field_prefix=' ', field_prefix=' ',
indent='', indent='',
) )
@ -1093,8 +1104,7 @@ def _mk_msg_type_err(
f'invalid msg -> {msg}: {type(msg)}\n\n' f'invalid msg -> {msg}: {type(msg)}\n\n'
f'{tb_fmt}\n' f'{tb_fmt}\n'
f'Valid IPC msgs are:\n\n' f'Valid IPC msgs are:\n\n'
# f' ------ - ------\n' f'{codec.msg_spec_str}\n',
f'{fmt_spec}\n',
) )
elif src_type_error: elif src_type_error:
src_message: str = str(src_type_error) src_message: str = str(src_type_error)