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
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()
if not isinstance(e, _ClientSideRecreateError):
log.exception(e)
else:
self.sendData((RPC_RESPONSE, request_id, (ret)))

View File

@ -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

View File

@ -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")

View File

@ -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
@ -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")