From e16e7ca82a3fa812f49458b2f192acf3e3891427 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Wed, 12 Apr 2023 18:18:46 -0400 Subject: [PATCH] Add new remote error introspection attrs To handle both remote cancellation this adds `ContextCanceled.canceller: tuple` the uid of the cancel requesting actor and is expected to be set by the runtime when servicing any remote cancel request. This makes it possible for `ContextCancelled` receivers to know whether "their actor runtime" is the source of the cancellation. Also add an explicit `RemoteActor.src_actor_uid` which better formalizes the notion of "which remote actor" the error originated from. Both of these new attrs are expected to be packed in the `.msgdata` when the errors are loaded locally. --- tractor/_exceptions.py | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/tractor/_exceptions.py b/tractor/_exceptions.py index 58caada..07f4627 100644 --- a/tractor/_exceptions.py +++ b/tractor/_exceptions.py @@ -18,18 +18,18 @@ Our classy exception set. """ +import builtins +import importlib from typing import ( Any, - Optional, Type, ) -import importlib -import builtins import traceback import exceptiongroup as eg import trio +from ._state import current_actor _this_mod = importlib.import_module(__name__) @@ -44,7 +44,7 @@ class RemoteActorError(Exception): def __init__( self, message: str, - suberror_type: Optional[Type[BaseException]] = None, + suberror_type: Type[BaseException] | None = None, **msgdata ) -> None: @@ -53,6 +53,10 @@ class RemoteActorError(Exception): self.type = suberror_type self.msgdata = msgdata + @property + def src_actor_uid(self) -> tuple[str, str] | None: + return self.msgdata.get('src_actor_uid') + class InternalActorError(RemoteActorError): ''' @@ -62,14 +66,21 @@ class InternalActorError(RemoteActorError): ''' +class ContextCancelled(RemoteActorError): + ''' + Inter-actor task context was cancelled by either a call to + ``Portal.cancel_actor()`` or ``Context.cancel()``. + + ''' + @property + def canceller(self) -> tuple[str, str] | None: + return self.msgdata.get('canceller') + + class TransportClosed(trio.ClosedResourceError): "Underlying channel transport was closed prior to use" -class ContextCancelled(RemoteActorError): - "Inter-actor task context cancelled itself on the callee side." - - class NoResult(RuntimeError): "No final result is expected for this actor" @@ -108,13 +119,17 @@ def pack_error( else: tb_str = traceback.format_exc() - return { - 'error': { - 'tb_str': tb_str, - 'type_str': type(exc).__name__, - } + error_msg = { + 'tb_str': tb_str, + 'type_str': type(exc).__name__, + 'src_actor_uid': current_actor().uid, } + if isinstance(exc, ContextCancelled): + error_msg.update(exc.msgdata) + + return {'error': error_msg} + def unpack_error(