Commit 6e683133 authored by Jim Fulton's avatar Jim Fulton

Don't pickle errors.

Don't send errors as pickles. Send error names and class dict or arguments.

This is a step toward eliminating instance pickles from the ZEO protocol.

This is much messier than I'd like it to be because of the way both
python and ZEO handle exceptions.
parent c087f634
...@@ -37,3 +37,7 @@ class AuthError(StorageError): ...@@ -37,3 +37,7 @@ class AuthError(StorageError):
class ProtocolError(ClientStorageError): class ProtocolError(ClientStorageError):
"""A client contacted a server with an incomparible protocol """A client contacted a server with an incomparible protocol
""" """
class ServerException(ClientStorageError):
"""
"""
...@@ -5,7 +5,7 @@ if PY3: ...@@ -5,7 +5,7 @@ if PY3:
else: else:
import trollius as asyncio import trollius as asyncio
from ZEO.Exceptions import ClientDisconnected from ZEO.Exceptions import ClientDisconnected, ServerException
import concurrent.futures import concurrent.futures
import functools import functools
import logging import logging
...@@ -208,15 +208,22 @@ class Protocol(base.Protocol): ...@@ -208,15 +208,22 @@ class Protocol(base.Protocol):
msgid, async, name, args = decode(data) msgid, async, name, args = decode(data)
if name == '.reply': if name == '.reply':
future = self.futures.pop(msgid) future = self.futures.pop(msgid)
if (isinstance(args, tuple) and len(args) > 1 and if (async): # ZEO 5 exception
class_, args = args
factory = exc_factories.get(class_)
if factory:
exc = factory(class_, args)
if not isinstance(exc, unlogged_exceptions):
logger.error("%s from server: %s:%s",
self.name, class_, args)
else:
exc = ServerException(class_, args)
future.set_exception(exc)
elif (isinstance(args, tuple) and len(args) > 1 and
type(args[0]) == self.exception_type_type and type(args[0]) == self.exception_type_type and
issubclass(args[0], Exception) issubclass(args[0], Exception)
): ):
if not issubclass( if not issubclass(args[0], unlogged_exceptions):
args[0], (
ZODB.POSException.POSKeyError,
ZODB.POSException.ConflictError,)
):
logger.error("%s from server: %s.%s:%s", logger.error("%s from server: %s.%s:%s",
self.name, self.name,
args[0].__module__, args[0].__module__,
...@@ -270,6 +277,52 @@ class Protocol(base.Protocol): ...@@ -270,6 +277,52 @@ class Protocol(base.Protocol):
self.heartbeat_handle = self.loop.call_later( self.heartbeat_handle = self.loop.call_later(
self.heartbeat_interval, self.heartbeat) self.heartbeat_interval, self.heartbeat)
def create_Exception(class_, args):
return exc_classes[class_](*args)
def create_ConflictError(class_, args):
exc = exc_classes[class_](
message = args['message'],
oid = args['oid'],
serials = args['serials'],
)
exc.class_name = args.get('class_name')
return exc
def create_BTreesConflictError(class_, args):
return ZODB.POSException.BTreesConflictError(
p1 = args['p1'],
p2 = args['p2'],
p3 = args['p3'],
reason = args['reason'],
)
def create_MultipleUndoErrors(class_, args):
return ZODB.POSException.MultipleUndoErrors(args['_errs'])
exc_classes = {
'builtins.KeyError': KeyError,
'builtins.TypeError': TypeError,
'ZODB.POSException.ConflictError': ZODB.POSException.ConflictError,
'ZODB.POSException.POSKeyError': ZODB.POSException.POSKeyError,
'ZODB.POSException.ReadConflictError': ZODB.POSException.ReadConflictError,
'ZODB.POSException.ReadOnlyError': ZODB.POSException.ReadOnlyError,
'ZODB.POSException.StorageTransactionError':
ZODB.POSException.StorageTransactionError,
}
exc_factories = {
'builtins.KeyError': create_Exception,
'builtins.TypeError': create_Exception,
'ZODB.POSException.BTreesConflictError': create_BTreesConflictError,
'ZODB.POSException.ConflictError': create_ConflictError,
'ZODB.POSException.MultipleUndoErrors': create_MultipleUndoErrors,
'ZODB.POSException.POSKeyError': create_Exception,
'ZODB.POSException.ReadConflictError': create_ConflictError,
'ZODB.POSException.ReadOnlyError': create_Exception,
'ZODB.POSException.StorageTransactionError': create_Exception,
}
unlogged_exceptions = (ZODB.POSException.POSKeyError,
ZODB.POSException.ConflictError)
class Client(object): class Client(object):
"""asyncio low-level ZEO client interface """asyncio low-level ZEO client interface
""" """
......
...@@ -111,9 +111,9 @@ class ServerProtocol(base.Protocol): ...@@ -111,9 +111,9 @@ class ServerProtocol(base.Protocol):
if not async: if not async:
self.send_reply(message_id, result) self.send_reply(message_id, result)
def send_reply(self, message_id, result, send_error=False): def send_reply(self, message_id, result, send_error=False, flag=0):
try: try:
result = self.encode(message_id, 0, '.reply', result) result = self.encode(message_id, flag, '.reply', result)
except Exception: except Exception:
if isinstance(result, Delay): if isinstance(result, Delay):
result.set_sender(message_id, self) result.set_sender(message_id, self)
...@@ -134,7 +134,10 @@ class ServerProtocol(base.Protocol): ...@@ -134,7 +134,10 @@ class ServerProtocol(base.Protocol):
def send_error(self, message_id, exc, send_error=False): def send_error(self, message_id, exc, send_error=False):
"""Abstracting here so we can make this cleaner in the future """Abstracting here so we can make this cleaner in the future
""" """
self.send_reply(message_id, (exc.__class__, exc), send_error) class_ = exc.__class__
class_ = "%s.%s" % (class_.__module__, class_.__name__)
args = class_, exc.__dict__ or exc.args
self.send_reply(message_id, args, send_error, 2)
def async(self, method, *args): def async(self, method, *args):
self.call_async(method, args) self.call_async(method, args)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment