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.
This commit is contained in:
Pedro Algarvio 2011-05-18 03:56:17 +01:00
parent e383187796
commit 9b812a4eec
4 changed files with 57 additions and 99 deletions

View File

@ -57,13 +57,12 @@ import deluge.component as component
import deluge.configmanager import deluge.configmanager
from deluge.core.authmanager import (AUTH_LEVEL_NONE, AUTH_LEVEL_DEFAULT, from deluge.core.authmanager import (AUTH_LEVEL_NONE, AUTH_LEVEL_DEFAULT,
AUTH_LEVEL_ADMIN) AUTH_LEVEL_ADMIN)
from deluge.error import (DelugeError, NotAuthorizedError, _PassthroughError, from deluge.error import (DelugeError, NotAuthorizedError,
IncompatibleClient) _ClientSideRecreateError, IncompatibleClient)
RPC_RESPONSE = 1 RPC_RESPONSE = 1
RPC_ERROR = 2 RPC_ERROR = 2
RPC_EVENT = 3 RPC_EVENT = 3
RPC_EVENT_EXCEPTION = 4
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -247,13 +246,13 @@ class DelugeRPCProtocol(Protocol):
Sends an error response with the contents of the exception that was raised. Sends an error response with the contents of the exception that was raised.
""" """
exceptionType, exceptionValue, exceptionTraceback = sys.exc_info() exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
self.sendData(( self.sendData((
RPC_ERROR, RPC_ERROR,
request_id, request_id,
(exceptionType.__name__, exceptionType.__name__,
exceptionValue.args[0] if len(exceptionValue.args) == 1 else "", exceptionValue._args,
"".join(traceback.format_tb(exceptionTraceback))) exceptionValue._kwargs,
"".join(traceback.format_tb(exceptionTraceback))
)) ))
if method == "daemon.info": if method == "daemon.info":
@ -273,14 +272,8 @@ class DelugeRPCProtocol(Protocol):
self.factory.authorized_sessions[self.transport.sessionno] = (ret, args[0]) self.factory.authorized_sessions[self.transport.sessionno] = (ret, args[0])
self.factory.session_protocols[self.transport.sessionno] = self self.factory.session_protocols[self.transport.sessionno] = self
except Exception, e: 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) log.exception(e)
else: else:
self.sendData((RPC_RESPONSE, request_id, (ret))) self.sendData((RPC_RESPONSE, request_id, (ret)))

View File

@ -36,7 +36,21 @@
class DelugeError(Exception): 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): class NoCoreError(DelugeError):
pass pass
@ -50,30 +64,18 @@ class InvalidTorrentError(DelugeError):
class InvalidPathError(DelugeError): class InvalidPathError(DelugeError):
pass pass
class _PassthroughError(DelugeError): class _ClientSideRecreateError(DelugeError):
pass
def _get_message(self): class IncompatibleClient(_ClientSideRecreateError):
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):
def __init__(self, daemon_version): def __init__(self, daemon_version):
self.daemon_version = daemon_version self.daemon_version = daemon_version
self.message = _( self.message = _(
"Your deluge client is not compatible with the daemon. " "Your deluge client is not compatible with the daemon. "
"Please upgrade your client to %(daemon_version)s" "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): def __init__(self, current_level, required_level):
self.message = _( self.message = _(
@ -84,7 +86,7 @@ class NotAuthorizedError(_PassthroughError):
self.required_level = required_level self.required_level = required_level
class _UsernameBasedPasstroughError(_PassthroughError): class _UsernameBasedPasstroughError(_ClientSideRecreateError):
def _get_username(self): def _get_username(self):
return self._username return self._username

View File

@ -56,39 +56,12 @@ else:
RPC_RESPONSE = 1 RPC_RESPONSE = 1
RPC_ERROR = 2 RPC_ERROR = 2
RPC_EVENT = 3 RPC_EVENT = 3
RPC_EVENT_EXCEPTION = 4
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def format_kwargs(kwargs): def format_kwargs(kwargs):
return ", ".join([key + "=" + str(value) for key, value in kwargs.items()]) 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): class DelugeRPCRequest(object):
""" """
@ -204,17 +177,29 @@ class DelugeRPCProtocol(Protocol):
if message_type == RPC_RESPONSE: if message_type == RPC_RESPONSE:
# Run the callbacks registered with this Deferred object # Run the callbacks registered with this Deferred object
d.callback(request[2]) 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: elif message_type == RPC_ERROR:
# Create the DelugeRPCError to pass to the errback # Recreate exception and errback'it
r = self.__rpc_requests[request_id] exception_cls = getattr(error, request[2])
e = DelugeRPCError(r.method, r.args, r.kwargs, request[2][0], exception = exception_cls(*request[3], **request[4])
request[2][1], request[2][2])
# Run the errbacks registered with this Deferred object
d.errback(e)
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] del self.__rpc_requests[request_id]
def send_request(self, request): 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 # Create a Deferred object to return and add a default errback to print
# the error. # the error.
d = defer.Deferred() d = defer.Deferred()
d.addErrback(self.__rpcError)
# Store the Deferred until we receive a response from the daemon # Store the Deferred until we receive a response from the daemon
self.__deferred[self.__request_counter] = d 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]: if event in self.__factory.event_handlers and handler in self.__factory.event_handlers[event]:
self.__factory.event_handlers[event].remove(handler) 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): def __on_connect(self, result):
log.debug("__on_connect called") log.debug("__on_connect called")

View File

@ -37,6 +37,7 @@
from twisted.internet import defer, reactor from twisted.internet import defer, reactor
import deluge.component as component import deluge.component as component
from deluge.error import DelugeError
from deluge.ui.client import client from deluge.ui.client import client
from deluge.ui.console import UI_PATH from deluge.ui.console import UI_PATH
from colors import strip_colors from colors import strip_colors
@ -128,12 +129,11 @@ class Commander:
self.console.started_deferred.addCallback(on_started) self.console.started_deferred.addCallback(on_started)
component.start().addCallback(on_started) component.start().addCallback(on_started)
def on_connect_fail(result): def on_connect_fail(reason):
from deluge.ui.client import DelugeRPCError if reason.check(DelugeError):
if isinstance(result.value,DelugeRPCError): rm = reason.value.message
rm = result.value.exception_msg
else: else:
rm = result.getErrorMessage() rm = reason.getErrorMessage()
print "Could not connect to: %s:%d\n %s"%(host,port,rm) print "Could not connect to: %s:%d\n %s"%(host,port,rm)
self.do_command("quit") self.do_command("quit")