Fix unregistered-remote-error-type relay crash
Make `RemoteActorError` resilient to unresolved custom error types so that errors from remote actors always relay back to the caller - even when the user hasn't called `reg_err_types()` to register the exc type. Deats, - `.src_type`: log warning + return `None` instead of raising `TypeError` which was crashing the entire `_deliver_msg()` -> `pformat()` chain before the error could be relayed. - `.boxed_type_str`: fallback to `_ipc_msg.boxed_type_str` when the type obj can't be resolved so the type *name* is always available. - `unwrap_src_err()`: fallback to `RuntimeError` preserving original type name + traceback. - `unpack_error()`: log warning when `get_err_type()` returns `None` telling the user to call `reg_err_types()`. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-codemulticast_revertable_streams
parent
9c37b3f956
commit
a41c6d5c70
|
|
@ -434,24 +434,36 @@ class RemoteActorError(Exception):
|
|||
return self._ipc_msg.src_type_str
|
||||
|
||||
@property
|
||||
def src_type(self) -> str:
|
||||
def src_type(self) -> Type[BaseException]|None:
|
||||
'''
|
||||
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`.
|
||||
When the error has only been relayed a single
|
||||
actor-hop this will be the same as
|
||||
`.boxed_type`.
|
||||
|
||||
If the type can not be resolved locally (i.e.
|
||||
it was not registered via `reg_err_types()`)
|
||||
a warning is logged and `None` is returned;
|
||||
all string-level error info (`.src_type_str`,
|
||||
`.tb_str`, etc.) remains available.
|
||||
|
||||
'''
|
||||
if self._src_type is None:
|
||||
self._src_type = get_err_type(
|
||||
self._ipc_msg.src_type_str
|
||||
)
|
||||
|
||||
if not self._src_type:
|
||||
raise TypeError(
|
||||
f'Failed to lookup src error type with '
|
||||
f'`tractor._exceptions.get_err_type()` :\n'
|
||||
f'{self.src_type_str}'
|
||||
log.warning(
|
||||
f'Failed to lookup src error type via\n'
|
||||
f'`tractor._exceptions.get_err_type()`:\n'
|
||||
f'\n'
|
||||
f'`{self._ipc_msg.src_type_str}`'
|
||||
f' is not registered!\n'
|
||||
f'\n'
|
||||
f'Call `reg_err_types()` to enable'
|
||||
f' full type reconstruction.\n'
|
||||
)
|
||||
|
||||
return self._src_type
|
||||
|
|
@ -459,16 +471,26 @@ class RemoteActorError(Exception):
|
|||
@property
|
||||
def boxed_type_str(self) -> str:
|
||||
'''
|
||||
String-name of the (last hop's) boxed error type.
|
||||
String-name of the (last hop's) boxed error
|
||||
type.
|
||||
|
||||
Falls back to the IPC-msg-encoded type-name
|
||||
str when the type can not be resolved locally
|
||||
(e.g. unregistered custom errors).
|
||||
|
||||
'''
|
||||
# TODO, maybe support also serializing the
|
||||
# `ExceptionGroup.exeptions: list[BaseException]` set under
|
||||
# certain conditions?
|
||||
# `ExceptionGroup.exeptions: list[BaseException]`
|
||||
# set under certain conditions?
|
||||
bt: Type[BaseException] = self.boxed_type
|
||||
if bt:
|
||||
return str(bt.__name__)
|
||||
|
||||
# fallback to the str name from the IPC msg
|
||||
# when the type obj can't be resolved.
|
||||
if self._ipc_msg:
|
||||
return self._ipc_msg.boxed_type_str
|
||||
|
||||
return ''
|
||||
|
||||
@property
|
||||
|
|
@ -701,10 +723,22 @@ class RemoteActorError(Exception):
|
|||
failing actor's remote env.
|
||||
|
||||
'''
|
||||
# TODO: better tb insertion and all the fancier dunder
|
||||
# metadata stuff as per `.__context__` etc. and friends:
|
||||
# TODO: better tb insertion and all the fancier
|
||||
# dunder metadata stuff as per `.__context__`
|
||||
# etc. and friends:
|
||||
# https://github.com/python-trio/trio/issues/611
|
||||
src_type_ref: Type[BaseException] = self.src_type
|
||||
src_type_ref: Type[BaseException]|None = (
|
||||
self.src_type
|
||||
)
|
||||
if src_type_ref is None:
|
||||
# unresolvable type: fall back to
|
||||
# a `RuntimeError` preserving original
|
||||
# traceback + type name.
|
||||
return RuntimeError(
|
||||
f'{self.src_type_str}: '
|
||||
f'{self.tb_str}'
|
||||
)
|
||||
|
||||
return src_type_ref(self.tb_str)
|
||||
|
||||
# TODO: local recontruction of nested inception for a given
|
||||
|
|
@ -1233,14 +1267,31 @@ def unpack_error(
|
|||
if not isinstance(msg, Error):
|
||||
return None
|
||||
|
||||
# try to lookup a suitable error type from the local runtime
|
||||
# env then use it to construct a local instance.
|
||||
# boxed_type_str: str = error_dict['boxed_type_str']
|
||||
# try to lookup a suitable error type from the
|
||||
# local runtime env then use it to construct a
|
||||
# local instance.
|
||||
boxed_type_str: str = msg.boxed_type_str
|
||||
boxed_type: Type[BaseException] = get_err_type(boxed_type_str)
|
||||
boxed_type: Type[BaseException] = get_err_type(
|
||||
boxed_type_str
|
||||
)
|
||||
|
||||
# retrieve the error's msg-encoded remotoe-env info
|
||||
message: str = f'remote task raised a {msg.boxed_type_str!r}\n'
|
||||
if boxed_type is None:
|
||||
log.warning(
|
||||
f'Failed to resolve remote error type\n'
|
||||
f'`{boxed_type_str}` - boxing as\n'
|
||||
f'`RemoteActorError` with original\n'
|
||||
f'traceback preserved.\n'
|
||||
f'\n'
|
||||
f'Call `reg_err_types()` to enable\n'
|
||||
f'full type reconstruction.\n'
|
||||
)
|
||||
|
||||
# retrieve the error's msg-encoded remote-env
|
||||
# info
|
||||
message: str = (
|
||||
f'remote task raised a '
|
||||
f'{msg.boxed_type_str!r}\n'
|
||||
)
|
||||
|
||||
# TODO: do we even really need these checks for RAEs?
|
||||
if boxed_type_str in [
|
||||
|
|
|
|||
Loading…
Reference in New Issue