Fix 2247: AttributeError in deluge.error.DaemonRunningError
* Removed all the properties in error.py and added more tests * Handle failure in client.py handling RPC_ERROR (From older daemons)
This commit is contained in:
parent
a27b479f06
commit
b76cdc2227
|
@ -144,7 +144,7 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
|
||||||
only message that a client sends to the server is a RPC Request message.
|
only message that a client sends to the server is a RPC Request message.
|
||||||
If the RPC Request message is valid, then the method is called in
|
If the RPC Request message is valid, then the method is called in
|
||||||
:meth:`dispatch`.
|
:meth:`dispatch`.
|
||||||
|
|
||||||
:param request: the request from the client.
|
:param request: the request from the client.
|
||||||
:type data: tuple
|
:type data: tuple
|
||||||
|
|
||||||
|
@ -238,16 +238,17 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
|
||||||
exceptionValue._kwargs,
|
exceptionValue._kwargs,
|
||||||
formated_tb
|
formated_tb
|
||||||
))
|
))
|
||||||
except Exception, err:
|
except AttributeError, err:
|
||||||
# This most likely not a deluge exception, let's wrap it
|
# This is not a deluge exception (object has no attribute '_args), let's wrap it
|
||||||
log.error("An exception occurred while sending RPC_ERROR to "
|
log.error("An exception occurred while sending RPC_ERROR to "
|
||||||
"client. Wrapping it and resending. Error to "
|
"client. Wrapping it and resending. Error to "
|
||||||
"send(causing exception goes next):\n%s", formated_tb)
|
"send(causing exception goes next):\n%s", formated_tb)
|
||||||
log.exception(err)
|
|
||||||
try:
|
try:
|
||||||
raise WrappedException(str(exceptionValue), exceptionType.__name__, formated_tb)
|
raise WrappedException(str(exceptionValue), exceptionType.__name__, formated_tb)
|
||||||
except:
|
except:
|
||||||
sendError()
|
sendError()
|
||||||
|
except Exception, err:
|
||||||
|
log.error("An exception occurred while sending RPC_ERROR to client: %s", err)
|
||||||
|
|
||||||
if method == "daemon.info":
|
if method == "daemon.info":
|
||||||
# This is a special case and used in the initial connection process
|
# This is a special case and used in the initial connection process
|
||||||
|
|
|
@ -36,15 +36,6 @@
|
||||||
|
|
||||||
|
|
||||||
class DelugeError(Exception):
|
class DelugeError(Exception):
|
||||||
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):
|
def __new__(cls, *args, **kwargs):
|
||||||
inst = super(DelugeError, cls).__new__(cls, *args, **kwargs)
|
inst = super(DelugeError, cls).__new__(cls, *args, **kwargs)
|
||||||
|
@ -52,80 +43,71 @@ class DelugeError(Exception):
|
||||||
inst._kwargs = kwargs
|
inst._kwargs = kwargs
|
||||||
return inst
|
return inst
|
||||||
|
|
||||||
class NoCoreError(DelugeError):
|
def __init__(self, message=None):
|
||||||
pass
|
self.message = message
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.message
|
||||||
|
|
||||||
|
|
||||||
class DaemonRunningError(DelugeError):
|
class DaemonRunningError(DelugeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class InvalidTorrentError(DelugeError):
|
class InvalidTorrentError(DelugeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class InvalidPathError(DelugeError):
|
class InvalidPathError(DelugeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class WrappedException(DelugeError):
|
|
||||||
def _get_traceback(self):
|
|
||||||
return self._traceback
|
|
||||||
def _set_traceback(self, traceback):
|
|
||||||
self._traceback = traceback
|
|
||||||
traceback = property(_get_traceback, _set_traceback)
|
|
||||||
del _get_traceback, _set_traceback
|
|
||||||
|
|
||||||
def _get_type(self):
|
class WrappedException(DelugeError):
|
||||||
return self._type
|
|
||||||
def _set_type(self, type):
|
|
||||||
self._type = type
|
|
||||||
type = property(_get_type, _set_type)
|
|
||||||
del _get_type, _set_type
|
|
||||||
|
|
||||||
def __init__(self, message, exception_type, traceback):
|
def __init__(self, message, exception_type, traceback):
|
||||||
self.message = message
|
self.message = message
|
||||||
self.type = exception_type
|
self.type = exception_type
|
||||||
self.traceback = traceback
|
self.traceback = traceback
|
||||||
|
|
||||||
|
|
||||||
class _ClientSideRecreateError(DelugeError):
|
class _ClientSideRecreateError(DelugeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class IncompatibleClient(_ClientSideRecreateError):
|
class IncompatibleClient(_ClientSideRecreateError):
|
||||||
|
|
||||||
def __init__(self, daemon_version):
|
def __init__(self, daemon_version):
|
||||||
self.daemon_version = daemon_version
|
self.daemon_version = daemon_version
|
||||||
self.message = _(
|
msg = "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"
|
dict(daemon_version=self.daemon_version)
|
||||||
) % dict(daemon_version=self.daemon_version)
|
super(IncompatibleClient, self).__init__(message=msg)
|
||||||
|
|
||||||
|
|
||||||
class NotAuthorizedError(_ClientSideRecreateError):
|
class NotAuthorizedError(_ClientSideRecreateError):
|
||||||
|
|
||||||
def __init__(self, current_level, required_level):
|
def __init__(self, current_level, required_level):
|
||||||
self.message = _(
|
msg = "Auth level too low: %(current_level)s < %(required_level)s" % \
|
||||||
"Auth level too low: %(current_level)s < %(required_level)s" %
|
|
||||||
dict(current_level=current_level, required_level=required_level)
|
dict(current_level=current_level, required_level=required_level)
|
||||||
)
|
super(NotAuthorizedError, self).__init__(message=msg)
|
||||||
self.current_level = current_level
|
self.current_level = current_level
|
||||||
self.required_level = required_level
|
self.required_level = required_level
|
||||||
|
|
||||||
|
|
||||||
class _UsernameBasedPasstroughError(_ClientSideRecreateError):
|
class _UsernameBasedPasstroughError(_ClientSideRecreateError):
|
||||||
|
|
||||||
def _get_username(self):
|
|
||||||
return self._username
|
|
||||||
def _set_username(self, username):
|
|
||||||
self._username = username
|
|
||||||
username = property(_get_username, _set_username)
|
|
||||||
del _get_username, _set_username
|
|
||||||
|
|
||||||
def __init__(self, message, username):
|
def __init__(self, message, username):
|
||||||
super(_UsernameBasedPasstroughError, self).__init__(message)
|
super(_UsernameBasedPasstroughError, self).__init__(message)
|
||||||
self.message = message
|
|
||||||
self.username = username
|
self.username = username
|
||||||
|
|
||||||
|
|
||||||
class BadLoginError(_UsernameBasedPasstroughError):
|
class BadLoginError(_UsernameBasedPasstroughError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AuthenticationRequired(_UsernameBasedPasstroughError):
|
class AuthenticationRequired(_UsernameBasedPasstroughError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AuthManagerError(_UsernameBasedPasstroughError):
|
class AuthManagerError(_UsernameBasedPasstroughError):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
from twisted.trial import unittest
|
||||||
|
import deluge.error
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorTestCase(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_DelugeError(self):
|
||||||
|
msg = "Some message"
|
||||||
|
e = deluge.error.DelugeError(msg)
|
||||||
|
self.assertEquals(str(e), msg)
|
||||||
|
self.assertEquals(e._args, (msg,))
|
||||||
|
self.assertEquals(e._kwargs, {})
|
||||||
|
|
||||||
|
def test_IncompatibleClient(self):
|
||||||
|
version = "1.3.6"
|
||||||
|
e = deluge.error.IncompatibleClient(version)
|
||||||
|
self.assertEquals(str(e), "Your deluge client is not compatible with the daemon. \
|
||||||
|
Please upgrade your client to %s" % version)
|
||||||
|
|
||||||
|
def test_NotAuthorizedError(self):
|
||||||
|
current_level = 5
|
||||||
|
required_level = 10
|
||||||
|
e = deluge.error.NotAuthorizedError(current_level, required_level)
|
||||||
|
self.assertEquals(str(e), "Auth level too low: %d < %d" % (current_level, required_level))
|
||||||
|
|
||||||
|
def test_BadLoginError(self):
|
||||||
|
message = "Login failed"
|
||||||
|
username = "deluge"
|
||||||
|
e = deluge.error.BadLoginError(message, username)
|
||||||
|
self.assertEquals(str(e), message)
|
|
@ -150,37 +150,46 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
|
||||||
d.callback(request[2])
|
d.callback(request[2])
|
||||||
elif message_type == RPC_ERROR:
|
elif message_type == RPC_ERROR:
|
||||||
# Recreate exception and errback'it
|
# Recreate exception and errback'it
|
||||||
exception_cls = getattr(error, request[2])
|
try:
|
||||||
exception = exception_cls(*request[3], **request[4])
|
# The exception class is located in deluge.error
|
||||||
|
if hasattr(error, request[2]):
|
||||||
|
exception_cls = getattr(error, request[2])
|
||||||
|
exception = exception_cls(*request[3], **request[4])
|
||||||
|
else:
|
||||||
|
# Shouldn't happen
|
||||||
|
raise Exception("Received invalid exception: %s", request[2])
|
||||||
|
|
||||||
# Ideally we would chain the deferreds instead of instance
|
# Ideally we would chain the deferreds instead of instance
|
||||||
# checking just to log them. But, that would mean that any
|
# checking just to log them. But, that would mean that any
|
||||||
# errback on the fist deferred should returns it's failure
|
# errback on the fist deferred should returns it's failure
|
||||||
# so it could pass back to the 2nd deferred on the chain. But,
|
# so it could pass back to the 2nd deferred on the chain. But,
|
||||||
# that does not always happen.
|
# that does not always happen.
|
||||||
# So, just do some instance checking and just log rpc error at
|
# So, just do some instance checking and just log rpc error at
|
||||||
# diferent levels.
|
# diferent levels.
|
||||||
r = self.__rpc_requests[request_id]
|
r = self.__rpc_requests[request_id]
|
||||||
msg = "RPCError Message Received!"
|
msg = "RPCError Message Received!"
|
||||||
msg += "\n" + "-" * 80
|
msg += "\n" + "-" * 80
|
||||||
msg += "\n" + "RPCRequest: " + r.__repr__()
|
msg += "\n" + "RPCRequest: " + r.__repr__()
|
||||||
msg += "\n" + "-" * 80
|
msg += "\n" + "-" * 80
|
||||||
if isinstance(exception, error.WrappedException):
|
if isinstance(exception, error.WrappedException):
|
||||||
msg += "\n" + exception.type + "\n" + exception.message + ": "
|
msg += "\n" + exception.type + "\n" + exception.message + ": "
|
||||||
msg += exception.traceback
|
msg += exception.traceback
|
||||||
else:
|
else:
|
||||||
msg += "\n" + request[5] + "\n" + request[2] + ": "
|
msg += "\n" + request[5] + "\n" + request[2] + ": "
|
||||||
msg += str(exception)
|
msg += str(exception)
|
||||||
msg += "\n" + "-" * 80
|
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 happening
|
|
||||||
log.debug(msg)
|
|
||||||
|
|
||||||
|
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 happening
|
||||||
|
log.debug(msg)
|
||||||
|
except:
|
||||||
|
# Failed probably because of invalid data (old daemon)
|
||||||
|
import traceback
|
||||||
|
log.error("Failed to handle RPC_ERROR (Old daemon?): %s\nLocal error: %s", request[2], traceback.format_exc())
|
||||||
d.errback(exception)
|
d.errback(exception)
|
||||||
del self.__rpc_requests[request_id]
|
del self.__rpc_requests[request_id]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue