Improve `TransportClosed.__repr__()`, add `src_exc`
By borrowing from the implementation of `RemoteActorError.pformat()` which is now factored into a new `.devx.pformat_exc()` and re-used for both error types while maintaining the same func-sig. Obviously delegate `RemoteActorError.pformat()` to the new helper accordingly and keeping the prior `body` generation from `.devx.pformat_boxed_tb()` as before. The new helper allows for, - passing any of a `header|message|body: str` which are all combined in that order in the final output. - getting the `exc.message` as the default `message` part. - generating an objecty-looking "type-name" header to be rendered by default when `header` is not overridden. - "first-line-of `message`" processing which we split-off and then re-inject as a `f'<{type(exc).__name__}( {first} )>'` top line header. - an optional `tail: str = '>'` to "close the object"-look only added when `with_type_header: bool = True`. Adjustments to `TransportClosed` around this include, - replacing the init `cause` arg for a `src_exc` which is now always assigned to a same named instance var. - displaying that new `.src_exc` in the `body: str` arg to the `.devx.pformat.pformat_exc()` call so you can always see the underlying (normally `trio`) source error. - just make it inherit from `Exception` not `trio.BrokenResourceError` to avoid handlers catching `TransportClosed` as the former particularly in testing when we want to sometimes to distinguish them.structural_dynamics_of_flow
parent
692bd0edf6
commit
74df5034c0
|
@ -23,7 +23,6 @@ import builtins
|
||||||
import importlib
|
import importlib
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from pdb import bdb
|
from pdb import bdb
|
||||||
import sys
|
|
||||||
from types import (
|
from types import (
|
||||||
TracebackType,
|
TracebackType,
|
||||||
)
|
)
|
||||||
|
@ -543,7 +542,6 @@ class RemoteActorError(Exception):
|
||||||
if val:
|
if val:
|
||||||
_repr += f'{key}={val_str}{end_char}'
|
_repr += f'{key}={val_str}{end_char}'
|
||||||
|
|
||||||
|
|
||||||
return _repr
|
return _repr
|
||||||
|
|
||||||
def reprol(self) -> str:
|
def reprol(self) -> str:
|
||||||
|
@ -622,56 +620,9 @@ class RemoteActorError(Exception):
|
||||||
the type name is already implicitly shown by python).
|
the type name is already implicitly shown by python).
|
||||||
|
|
||||||
'''
|
'''
|
||||||
header: str = ''
|
|
||||||
body: str = ''
|
|
||||||
message: str = ''
|
|
||||||
|
|
||||||
# XXX when the currently raised exception is this instance,
|
|
||||||
# we do not ever use the "type header" style repr.
|
|
||||||
is_being_raised: bool = False
|
|
||||||
if (
|
|
||||||
(exc := sys.exception())
|
|
||||||
and
|
|
||||||
exc is self
|
|
||||||
):
|
|
||||||
is_being_raised: bool = True
|
|
||||||
|
|
||||||
with_type_header: bool = (
|
|
||||||
with_type_header
|
|
||||||
and
|
|
||||||
not is_being_raised
|
|
||||||
)
|
|
||||||
|
|
||||||
# <RemoteActorError( .. )> style
|
|
||||||
if with_type_header:
|
|
||||||
header: str = f'<{type(self).__name__}('
|
|
||||||
|
|
||||||
if message := self._message:
|
|
||||||
|
|
||||||
# split off the first line so, if needed, it isn't
|
|
||||||
# indented the same like the "boxed content" which
|
|
||||||
# since there is no `.tb_str` is just the `.message`.
|
|
||||||
lines: list[str] = message.splitlines()
|
|
||||||
first: str = lines[0]
|
|
||||||
message: str = message.removeprefix(first)
|
|
||||||
|
|
||||||
# with a type-style header we,
|
|
||||||
# - have no special message "first line" extraction/handling
|
|
||||||
# - place the message a space in from the header:
|
|
||||||
# `MsgTypeError( <message> ..`
|
|
||||||
# ^-here
|
|
||||||
# - indent the `.message` inside the type body.
|
|
||||||
if with_type_header:
|
|
||||||
first = f' {first} )>'
|
|
||||||
|
|
||||||
message: str = textwrap.indent(
|
|
||||||
message,
|
|
||||||
prefix=' '*2,
|
|
||||||
)
|
|
||||||
message: str = first + message
|
|
||||||
|
|
||||||
# IFF there is an embedded traceback-str we always
|
# IFF there is an embedded traceback-str we always
|
||||||
# draw the ascii-box around it.
|
# draw the ascii-box around it.
|
||||||
|
body: str = ''
|
||||||
if tb_str := self.tb_str:
|
if tb_str := self.tb_str:
|
||||||
fields: str = self._mk_fields_str(
|
fields: str = self._mk_fields_str(
|
||||||
_body_fields
|
_body_fields
|
||||||
|
@ -692,21 +643,15 @@ class RemoteActorError(Exception):
|
||||||
boxer_header=self.relay_uid,
|
boxer_header=self.relay_uid,
|
||||||
)
|
)
|
||||||
|
|
||||||
tail = ''
|
# !TODO, it'd be nice to import these top level without
|
||||||
if (
|
# cycles!
|
||||||
with_type_header
|
from tractor.devx.pformat import (
|
||||||
and not message
|
pformat_exc,
|
||||||
):
|
)
|
||||||
tail: str = '>'
|
return pformat_exc(
|
||||||
|
exc=self,
|
||||||
return (
|
with_type_header=with_type_header,
|
||||||
header
|
body=body,
|
||||||
+
|
|
||||||
message
|
|
||||||
+
|
|
||||||
f'{body}'
|
|
||||||
+
|
|
||||||
tail
|
|
||||||
)
|
)
|
||||||
|
|
||||||
__repr__ = pformat
|
__repr__ = pformat
|
||||||
|
@ -984,7 +929,7 @@ class StreamOverrun(
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
class TransportClosed(trio.BrokenResourceError):
|
class TransportClosed(Exception):
|
||||||
'''
|
'''
|
||||||
IPC transport (protocol) connection was closed or broke and
|
IPC transport (protocol) connection was closed or broke and
|
||||||
indicates that the wrapping communication `Channel` can no longer
|
indicates that the wrapping communication `Channel` can no longer
|
||||||
|
@ -995,16 +940,21 @@ class TransportClosed(trio.BrokenResourceError):
|
||||||
self,
|
self,
|
||||||
message: str,
|
message: str,
|
||||||
loglevel: str = 'transport',
|
loglevel: str = 'transport',
|
||||||
cause: BaseException|None = None,
|
src_exc: Exception|None = None,
|
||||||
raise_on_report: bool = False,
|
raise_on_report: bool = False,
|
||||||
|
|
||||||
) -> None:
|
) -> None:
|
||||||
self.message: str = message
|
self.message: str = message
|
||||||
self._loglevel = loglevel
|
self._loglevel: str = loglevel
|
||||||
super().__init__(message)
|
super().__init__(message)
|
||||||
|
|
||||||
if cause is not None:
|
self.src_exc = src_exc
|
||||||
self.__cause__ = cause
|
if (
|
||||||
|
src_exc is not None
|
||||||
|
and
|
||||||
|
not self.__cause__
|
||||||
|
):
|
||||||
|
self.__cause__ = src_exc
|
||||||
|
|
||||||
# flag to toggle whether the msg loop should raise
|
# flag to toggle whether the msg loop should raise
|
||||||
# the exc in its `TransportClosed` handler block.
|
# the exc in its `TransportClosed` handler block.
|
||||||
|
@ -1041,6 +991,26 @@ class TransportClosed(trio.BrokenResourceError):
|
||||||
if self._raise_on_report:
|
if self._raise_on_report:
|
||||||
raise self from cause
|
raise self from cause
|
||||||
|
|
||||||
|
def pformat(self) -> str:
|
||||||
|
from tractor.devx.pformat import (
|
||||||
|
pformat_exc,
|
||||||
|
)
|
||||||
|
src_err: Exception|None = self.src_exc or '<unknown>'
|
||||||
|
src_msg: tuple[str] = src_err.args
|
||||||
|
src_exc_repr: str = (
|
||||||
|
f'{type(src_err).__name__}[ {src_msg} ]'
|
||||||
|
)
|
||||||
|
return pformat_exc(
|
||||||
|
exc=self,
|
||||||
|
# message=self.message, # implicit!
|
||||||
|
body=(
|
||||||
|
f' |_src_exc: {src_exc_repr}\n'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# delegate to `str`-ified pformat
|
||||||
|
__repr__ = pformat
|
||||||
|
|
||||||
|
|
||||||
class NoResult(RuntimeError):
|
class NoResult(RuntimeError):
|
||||||
"No final result is expected for this actor"
|
"No final result is expected for this actor"
|
||||||
|
|
|
@ -19,6 +19,7 @@ Pretty formatters for use throughout the code base.
|
||||||
Mostly handy for logging and exception message content.
|
Mostly handy for logging and exception message content.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
@ -115,6 +116,85 @@ def pformat_boxed_tb(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def pformat_exc(
|
||||||
|
exc: Exception,
|
||||||
|
header: str = '',
|
||||||
|
message: str = '',
|
||||||
|
body: str = '',
|
||||||
|
with_type_header: bool = True,
|
||||||
|
) -> str:
|
||||||
|
|
||||||
|
# XXX when the currently raised exception is this instance,
|
||||||
|
# we do not ever use the "type header" style repr.
|
||||||
|
is_being_raised: bool = False
|
||||||
|
if (
|
||||||
|
(curr_exc := sys.exception())
|
||||||
|
and
|
||||||
|
curr_exc is exc
|
||||||
|
):
|
||||||
|
is_being_raised: bool = True
|
||||||
|
|
||||||
|
with_type_header: bool = (
|
||||||
|
with_type_header
|
||||||
|
and
|
||||||
|
not is_being_raised
|
||||||
|
)
|
||||||
|
|
||||||
|
# <RemoteActorError( .. )> style
|
||||||
|
if (
|
||||||
|
with_type_header
|
||||||
|
and
|
||||||
|
not header
|
||||||
|
):
|
||||||
|
header: str = f'<{type(exc).__name__}('
|
||||||
|
|
||||||
|
message: str = (
|
||||||
|
message
|
||||||
|
or
|
||||||
|
exc.message
|
||||||
|
)
|
||||||
|
if message:
|
||||||
|
# split off the first line so, if needed, it isn't
|
||||||
|
# indented the same like the "boxed content" which
|
||||||
|
# since there is no `.tb_str` is just the `.message`.
|
||||||
|
lines: list[str] = message.splitlines()
|
||||||
|
first: str = lines[0]
|
||||||
|
message: str = message.removeprefix(first)
|
||||||
|
|
||||||
|
# with a type-style header we,
|
||||||
|
# - have no special message "first line" extraction/handling
|
||||||
|
# - place the message a space in from the header:
|
||||||
|
# `MsgTypeError( <message> ..`
|
||||||
|
# ^-here
|
||||||
|
# - indent the `.message` inside the type body.
|
||||||
|
if with_type_header:
|
||||||
|
first = f' {first} )>'
|
||||||
|
|
||||||
|
message: str = textwrap.indent(
|
||||||
|
message,
|
||||||
|
prefix=' '*2,
|
||||||
|
)
|
||||||
|
message: str = first + message
|
||||||
|
|
||||||
|
tail: str = ''
|
||||||
|
if (
|
||||||
|
with_type_header
|
||||||
|
and
|
||||||
|
not message
|
||||||
|
):
|
||||||
|
tail: str = '>'
|
||||||
|
|
||||||
|
return (
|
||||||
|
header
|
||||||
|
+
|
||||||
|
message
|
||||||
|
+
|
||||||
|
f'{body}'
|
||||||
|
+
|
||||||
|
tail
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def pformat_caller_frame(
|
def pformat_caller_frame(
|
||||||
stack_limit: int = 1,
|
stack_limit: int = 1,
|
||||||
box_tb: bool = True,
|
box_tb: bool = True,
|
||||||
|
|
|
@ -208,6 +208,7 @@ class MsgpackTransport(MsgTransport):
|
||||||
'''
|
'''
|
||||||
decodes_failed: int = 0
|
decodes_failed: int = 0
|
||||||
|
|
||||||
|
tpt_name: str = f'{type(self).__name__!r}'
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
header: bytes = await self.recv_stream.receive_exactly(4)
|
header: bytes = await self.recv_stream.receive_exactly(4)
|
||||||
|
@ -252,10 +253,9 @@ class MsgpackTransport(MsgTransport):
|
||||||
|
|
||||||
raise TransportClosed(
|
raise TransportClosed(
|
||||||
message=(
|
message=(
|
||||||
f'IPC transport already closed by peer\n'
|
f'{tpt_name} already closed by peer\n'
|
||||||
f'x)> {type(trans_err)}\n'
|
|
||||||
f' |_{self}\n'
|
|
||||||
),
|
),
|
||||||
|
src_exc=trans_err,
|
||||||
loglevel=loglevel,
|
loglevel=loglevel,
|
||||||
) from trans_err
|
) from trans_err
|
||||||
|
|
||||||
|
@ -267,18 +267,17 @@ class MsgpackTransport(MsgTransport):
|
||||||
#
|
#
|
||||||
# NOTE: as such we always re-raise this error from the
|
# NOTE: as such we always re-raise this error from the
|
||||||
# RPC msg loop!
|
# RPC msg loop!
|
||||||
except trio.ClosedResourceError as closure_err:
|
except trio.ClosedResourceError as cre:
|
||||||
|
closure_err = cre
|
||||||
|
|
||||||
raise TransportClosed(
|
raise TransportClosed(
|
||||||
message=(
|
message=(
|
||||||
f'IPC transport already manually closed locally?\n'
|
f'{tpt_name} was already closed locally ?\n'
|
||||||
f'x)> {type(closure_err)} \n'
|
|
||||||
f' |_{self}\n'
|
|
||||||
),
|
),
|
||||||
|
src_exc=closure_err,
|
||||||
loglevel='error',
|
loglevel='error',
|
||||||
raise_on_report=(
|
raise_on_report=(
|
||||||
closure_err.args[0] == 'another task closed this fd'
|
'another task closed this fd' in closure_err.args
|
||||||
or
|
|
||||||
closure_err.args[0] in ['another task closed this fd']
|
|
||||||
),
|
),
|
||||||
) from closure_err
|
) from closure_err
|
||||||
|
|
||||||
|
@ -286,12 +285,9 @@ class MsgpackTransport(MsgTransport):
|
||||||
if header == b'':
|
if header == b'':
|
||||||
raise TransportClosed(
|
raise TransportClosed(
|
||||||
message=(
|
message=(
|
||||||
f'IPC transport already gracefully closed\n'
|
f'{tpt_name} already gracefully closed\n'
|
||||||
f')>\n'
|
|
||||||
f'|_{self}\n'
|
|
||||||
),
|
),
|
||||||
loglevel='transport',
|
loglevel='transport',
|
||||||
# cause=??? # handy or no?
|
|
||||||
)
|
)
|
||||||
|
|
||||||
size: int
|
size: int
|
||||||
|
|
Loading…
Reference in New Issue