forked from goodboy/tractor
1
0
Fork 0

Propagate module import and func lookup errors

RPC module/function lookups should not cause the target actor to crash.
This change instead ships the error back to the calling actor allowing
for the remote actor to continue running depending on the caller's
error handling logic. Adds a new `ModuleNotExposed` error to accommodate.
remote_module_errors
Tyler Goodlet 2019-01-01 15:58:38 -05:00
parent aa479d64b0
commit 5fab61412c
3 changed files with 34 additions and 4 deletions

View File

@ -17,7 +17,7 @@ from ._actor import (
)
from ._trionics import open_nursery
from ._state import current_actor
from ._exceptions import RemoteActorError
from ._exceptions import RemoteActorError, ModuleNotExposed
__all__ = [
@ -29,6 +29,7 @@ __all__ = [
'Channel',
'MultiError',
'RemoteActorError',
'ModuleNotExposed',
]

View File

@ -15,7 +15,7 @@ from async_generator import asynccontextmanager, aclosing
from ._ipc import Channel, _connect_chan
from .log import get_console_log, get_logger
from ._exceptions import pack_error, InternalActorError
from ._exceptions import pack_error, InternalActorError, ModuleNotExposed
from ._portal import (
Portal,
open_portal,
@ -236,6 +236,12 @@ class Actor:
# self._mods.pop('test_discovery')
# TODO: how to test the above?
def _get_rpc_func(self, ns, funcname):
try:
return getattr(self._mods[ns], funcname)
except KeyError as err:
raise ModuleNotExposed(*err.args)
async def _stream_handler(
self,
stream: trio.SocketStream,
@ -398,7 +404,14 @@ class Actor:
if ns == 'self':
func = getattr(self, funcname)
else:
func = getattr(self._mods[ns], funcname)
# complain to client about restricted modules
try:
func = self._get_rpc_func(ns, funcname)
except (ModuleNotExposed, AttributeError) as err:
err_msg = pack_error(err)
err_msg['cid'] = cid
await chan.send(err_msg)
continue
# spin up a task for the requested function
log.debug(f"Spawning task for {func}")

View File

@ -1,16 +1,28 @@
"""
Our classy exception set.
"""
import importlib
import builtins
import traceback
_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):
super().__init__(message)
self.type = getattr(builtins, type_str, Exception)
for ns in [builtins, _this_mod]:
try:
self.type = getattr(ns, type_str)
break
except AttributeError:
continue
else:
self.type = Exception
self.msgdata = msgdata
# TODO: a trio.MultiError.catch like context manager
@ -27,6 +39,10 @@ class NoResult(RuntimeError):
"No final result is expected for this actor"
class ModuleNotExposed(RuntimeError):
"The requested module is not exposed for RPC"
def pack_error(exc):
"""Create an "error message" for tranmission over
a channel (aka the wire).