From 79c8b75b5dd17a622eff96522a80acf5ca65e123 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Sun, 13 Jun 2021 18:01:49 -0400 Subject: [PATCH] Add a specially handled `ContextCancelled` error --- tractor/_exceptions.py | 53 +++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/tractor/_exceptions.py b/tractor/_exceptions.py index f6d9f47..07ea4e6 100644 --- a/tractor/_exceptions.py +++ b/tractor/_exceptions.py @@ -1,7 +1,7 @@ """ Our classy exception set. """ -from typing import Dict, Any +from typing import Dict, Any, Optional import importlib import builtins import traceback @@ -15,17 +15,16 @@ _this_mod = importlib.import_module(__name__) class RemoteActorError(Exception): # TODO: local recontruction of remote exception deats "Remote actor exception bundled locally" - def __init__(self, message, type_str, **msgdata) -> None: - super().__init__(message) - for ns in [builtins, _this_mod, trio]: - try: - self.type = getattr(ns, type_str) - break - except AttributeError: - continue - else: - self.type = Exception + def __init__( + self, + message: str, + suberror_type: Optional[Exception] = None, + **msgdata + ) -> None: + super().__init__(message) + + self.type = suberror_type self.msgdata = msgdata # TODO: a trio.MultiError.catch like context manager @@ -41,6 +40,9 @@ class InternalActorError(RemoteActorError): 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" @@ -77,12 +79,35 @@ def unpack_error( into a local ``RemoteActorError``. """ - tb_str = msg['error'].get('tb_str', '') - return err_type( - f"{chan.uid}\n" + tb_str, + error = msg['error'] + + tb_str = error.get('tb_str', '') + message = f"{chan.uid}\n" + tb_str + type_name = error['type_str'] + suberror_type = Exception + + if type_name == 'ContextCancelled': + err_type = ContextCancelled + suberror_type = trio.Cancelled + + else: # try to lookup a suitable local error type + for ns in [builtins, _this_mod, trio]: + try: + suberror_type = getattr(ns, type_name) + break + except AttributeError: + continue + + exc = err_type( + message, + suberror_type=suberror_type, + + # unpack other fields into error type init **msg['error'], ) + return exc + def is_multi_cancelled(exc: BaseException) -> bool: """Predicate to determine if a ``trio.MultiError`` contains only