From 9b812a4eec6205b98d9d5c12c14f82611832217a Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Wed, 18 May 2011 03:56:17 +0100 Subject: [PATCH] Extend RPC Protocol. While adding the multiuser auth related stuff, RPC_AUTH_EVENT was added, simply to not touch how RPC_ERROR was handled. It was created to allow recreating the exception on the client side. Instead of adding another rpc event type, extend RPC_ERROR to send the proper data to recreate the exception on the client side. --- deluge/core/rpcserver.py | 23 ++++------ deluge/error.py | 40 +++++++++-------- deluge/ui/client.py | 79 +++++++++------------------------- deluge/ui/console/commander.py | 14 +++--- 4 files changed, 57 insertions(+), 99 deletions(-) diff --git a/deluge/core/rpcserver.py b/deluge/core/rpcserver.py index 7069ec6e9..24c894dbb 100644 --- a/deluge/core/rpcserver.py +++ b/deluge/core/rpcserver.py @@ -57,13 +57,12 @@ import deluge.component as component import deluge.configmanager from deluge.core.authmanager import (AUTH_LEVEL_NONE, AUTH_LEVEL_DEFAULT, AUTH_LEVEL_ADMIN) -from deluge.error import (DelugeError, NotAuthorizedError, _PassthroughError, - IncompatibleClient) +from deluge.error import (DelugeError, NotAuthorizedError, + _ClientSideRecreateError, IncompatibleClient) RPC_RESPONSE = 1 RPC_ERROR = 2 RPC_EVENT = 3 -RPC_EVENT_EXCEPTION = 4 log = logging.getLogger(__name__) @@ -247,13 +246,13 @@ class DelugeRPCProtocol(Protocol): Sends an error response with the contents of the exception that was raised. """ exceptionType, exceptionValue, exceptionTraceback = sys.exc_info() - self.sendData(( RPC_ERROR, request_id, - (exceptionType.__name__, - exceptionValue.args[0] if len(exceptionValue.args) == 1 else "", - "".join(traceback.format_tb(exceptionTraceback))) + exceptionType.__name__, + exceptionValue._args, + exceptionValue._kwargs, + "".join(traceback.format_tb(exceptionTraceback)) )) if method == "daemon.info": @@ -273,14 +272,8 @@ class DelugeRPCProtocol(Protocol): self.factory.authorized_sessions[self.transport.sessionno] = (ret, args[0]) self.factory.session_protocols[self.transport.sessionno] = self except Exception, e: - if isinstance(e, _PassthroughError): - self.sendData( - (RPC_EVENT_EXCEPTION, request_id, - e.__class__.__name__, - e._args, e._kwargs, args[0]) - ) - else: - sendError() + sendError() + if not isinstance(e, _ClientSideRecreateError): log.exception(e) else: self.sendData((RPC_RESPONSE, request_id, (ret))) diff --git a/deluge/error.py b/deluge/error.py index 14579d88a..ea7c64671 100644 --- a/deluge/error.py +++ b/deluge/error.py @@ -36,7 +36,21 @@ class DelugeError(Exception): - pass + def _get_message(self): + return self._message + def _set_message(self, message): + self._message = message + message = property(_get_message, _set_message) + del _get_message, _set_message + + def __str__(self): + return self.message + + def __new__(cls, *args, **kwargs): + inst = super(DelugeError, cls).__new__(cls, *args, **kwargs) + inst._args = args + inst._kwargs = kwargs + return inst class NoCoreError(DelugeError): pass @@ -50,30 +64,18 @@ class InvalidTorrentError(DelugeError): class InvalidPathError(DelugeError): pass -class _PassthroughError(DelugeError): +class _ClientSideRecreateError(DelugeError): + pass - def _get_message(self): - return self._message - def _set_message(self, message): - self._message = message - message = property(_get_message, _set_message) - del _get_message, _set_message - - def __new__(cls, *args, **kwargs): - inst = super(_PassthroughError, cls).__new__(cls, *args, **kwargs) - inst._args = args - inst._kwargs = kwargs - return inst - -class IncompatibleClient(_PassthroughError): +class IncompatibleClient(_ClientSideRecreateError): def __init__(self, daemon_version): self.daemon_version = daemon_version self.message = _( "Your deluge client is not compatible with the daemon. " "Please upgrade your client to %(daemon_version)s" - ) % {'daemon_version': self.daemon_version} + ) % dict(daemon_version=self.daemon_version) -class NotAuthorizedError(_PassthroughError): +class NotAuthorizedError(_ClientSideRecreateError): def __init__(self, current_level, required_level): self.message = _( @@ -84,7 +86,7 @@ class NotAuthorizedError(_PassthroughError): self.required_level = required_level -class _UsernameBasedPasstroughError(_PassthroughError): +class _UsernameBasedPasstroughError(_ClientSideRecreateError): def _get_username(self): return self._username diff --git a/deluge/ui/client.py b/deluge/ui/client.py index 794f3ed21..d356b228b 100644 --- a/deluge/ui/client.py +++ b/deluge/ui/client.py @@ -56,39 +56,12 @@ else: RPC_RESPONSE = 1 RPC_ERROR = 2 RPC_EVENT = 3 -RPC_EVENT_EXCEPTION = 4 log = logging.getLogger(__name__) def format_kwargs(kwargs): return ", ".join([key + "=" + str(value) for key, value in kwargs.items()]) -class DelugeRPCError(object): - """ - This object is passed to errback handlers in the event of a RPCError from the - daemon. - """ - def __init__(self, method, args, kwargs, exception_type, exception_msg, traceback): - self.method = method - self.args = args - self.kwargs = kwargs - self.exception_type = exception_type - self.exception_msg = exception_msg - self.traceback = traceback - - def logable(self): - # Create a delugerpcrequest to print out a nice RPCRequest string - r = DelugeRPCRequest() - r.method = self.method - r.args = self.args - r.kwargs = self.kwargs - msg = "RPCError Message Received!" - msg += "\n" + "-" * 80 - msg += "\n" + "RPCRequest: " + r.__repr__() - msg += "\n" + "-" * 80 - msg += "\n" + self.traceback + "\n" + self.exception_type + ": " + self.exception_msg - msg += "\n" + "-" * 80 - return msg class DelugeRPCRequest(object): """ @@ -204,17 +177,29 @@ class DelugeRPCProtocol(Protocol): if message_type == RPC_RESPONSE: # Run the callbacks registered with this Deferred object d.callback(request[2]) - elif message_type == RPC_EVENT_EXCEPTION: - # Recreate exception and errback'it - d.errback(getattr(error, request[2])(*request[3], **request[4])) elif message_type == RPC_ERROR: - # Create the DelugeRPCError to pass to the errback - r = self.__rpc_requests[request_id] - e = DelugeRPCError(r.method, r.args, r.kwargs, request[2][0], - request[2][1], request[2][2]) - # Run the errbacks registered with this Deferred object - d.errback(e) + # Recreate exception and errback'it + exception_cls = getattr(error, request[2]) + exception = exception_cls(*request[3], **request[4]) + r = self.__rpc_requests[request_id] + msg = "RPCError Message Received!" + msg += "\n" + "-" * 80 + msg += "\n" + "RPCRequest: " + r.__repr__() + msg += "\n" + "-" * 80 + msg += "\n" + request[5] + "\n" + request[2] + ": " + msg += str(exception) + msg += "\n" + "-" * 80 + + if not isinstance(exception, error._ClientSideRecreateError): + # Let's log these as errors + log.error(msg) + else: + # The rest just get's logged in debug level, just to log + # what's happending + log.debug(msg) + + d.errback(exception) del self.__rpc_requests[request_id] def send_request(self, request): @@ -347,7 +332,6 @@ class DaemonSSLProxy(DaemonProxy): # Create a Deferred object to return and add a default errback to print # the error. d = defer.Deferred() - d.addErrback(self.__rpcError) # Store the Deferred until we receive a response from the daemon self.__deferred[self.__request_counter] = d @@ -405,27 +389,6 @@ class DaemonSSLProxy(DaemonProxy): if event in self.__factory.event_handlers and handler in self.__factory.event_handlers[event]: self.__factory.event_handlers[event].remove(handler) - def __rpcError(self, error_data): - """ - Prints out a RPCError message to the error log. This includes the daemon - traceback. - - :param error_data: this is passed from the deferred errback with error.value - containing a `:class:DelugeRPCError` object. - """ - try: - if isinstance(error_data.value, error.NotAuthorizedError): - # Still log these errors - log.error(error_data.value.logable()) - return error_data - if isinstance(error_data.value, error._PassthroughError): - return error_data - except: - pass - - log.error(error_data.value.logable()) - return error_data - def __on_connect(self, result): log.debug("__on_connect called") diff --git a/deluge/ui/console/commander.py b/deluge/ui/console/commander.py index 5f3503049..21680c06f 100644 --- a/deluge/ui/console/commander.py +++ b/deluge/ui/console/commander.py @@ -37,6 +37,7 @@ from twisted.internet import defer, reactor import deluge.component as component +from deluge.error import DelugeError from deluge.ui.client import client from deluge.ui.console import UI_PATH from colors import strip_colors @@ -51,7 +52,7 @@ class Commander: self.interactive = interactive def write(self,line): - print(strip_colors(line)) + print(strip_colors(line)) def do_command(self, cmd): """ @@ -128,12 +129,11 @@ class Commander: self.console.started_deferred.addCallback(on_started) component.start().addCallback(on_started) - def on_connect_fail(result): - from deluge.ui.client import DelugeRPCError - if isinstance(result.value,DelugeRPCError): - rm = result.value.exception_msg + def on_connect_fail(reason): + if reason.check(DelugeError): + rm = reason.value.message else: - rm = result.getErrorMessage() + rm = reason.getErrorMessage() print "Could not connect to: %s:%d\n %s"%(host,port,rm) self.do_command("quit") @@ -143,4 +143,4 @@ class Commander: d = client.connect() d.addCallback(on_connect) d.addErrback(on_connect_fail) - +