This commit is contained in:
Ask Solem 2013-12-16 16:53:54 +00:00
parent 48853b14bb
commit 620e2b62f5
5 changed files with 54 additions and 33 deletions

View File

@ -8,9 +8,15 @@
3.0.8 3.0.8
===== =====
:release-date: 2013-12-16 16:00 P.M UTC :release-date: 2013-12-16 17:00 P.M UTC
:release-by: Ask Solem :release-by: Ask Solem
- Serializer: loads and dumps now wraps exceptions raised into
:exc:`~kombu.exceptions.DecodeError` and
:exc:`kombu.exceptions.EncodeError` respectively.
Contributed by Ionel Cristian Maries
- Redis: Would attempt to read from the wrong connection if a select/epoll/kqueue - Redis: Would attempt to read from the wrong connection if a select/epoll/kqueue
exception event happened. exception event happened.

View File

@ -24,13 +24,19 @@ class KombuError(Exception):
"""Common subclass for all Kombu exceptions.""" """Common subclass for all Kombu exceptions."""
pass pass
class SerializationError(KombuError): class SerializationError(KombuError):
"""Failed to encode a message.""" """Failed to serialize/deserialize content."""
class EncodeError(SerializationError):
"""Cannot encode object."""
pass pass
class DeserializationError(KombuError):
"""Failed to decode a message.""" class DecodeError(SerializationError):
pass """Cannot decode object."""
class NotBoundError(KombuError): class NotBoundError(KombuError):
"""Trying to call channel dependent method on unbound entity.""" """Trying to call channel dependent method on unbound entity."""

View File

@ -18,10 +18,13 @@ except ImportError: # pragma: no cover
cpickle = None # noqa cpickle = None # noqa
from collections import namedtuple from collections import namedtuple
from contextlib import contextmanager
from .exceptions import SerializerNotInstalled, ContentDisallowed, SerializationError, DeserializationError from .exceptions import (
from .five import BytesIO, text_t ContentDisallowed, DecodeError, EncodeError, SerializerNotInstalled
from .utils import entrypoints, wrap_exceptions )
from .five import BytesIO, reraise, text_t
from .utils import entrypoints
from .utils.encoding import str_to_bytes, bytes_t from .utils.encoding import str_to_bytes, bytes_t
__all__ = ['pickle', 'loads', 'dumps', 'register', 'unregister'] __all__ = ['pickle', 'loads', 'dumps', 'register', 'unregister']
@ -44,6 +47,17 @@ pickle_protocol = int(os.environ.get('PICKLE_PROTOCOL', 2))
codec = namedtuple('codec', ('content_type', 'content_encoding', 'encoder')) codec = namedtuple('codec', ('content_type', 'content_encoding', 'encoder'))
@contextmanager
def _reraise_errors(wrapper,
include=(Exception, ), exclude=(SerializerNotInstalled, )):
try:
yield
except exclude:
raise
except include as exc:
reraise(wrapper, wrapper(exc), sys.exc_info()[2])
def pickle_loads(s, load=pickle_load): def pickle_loads(s, load=pickle_load):
# used to support buffer objects # used to support buffer objects
return load(BytesIO(s)) return load(BytesIO(s))
@ -116,7 +130,6 @@ class SerializerRegistry(object):
raise SerializerNotInstalled( raise SerializerNotInstalled(
'No encoder installed for {0}'.format(name)) 'No encoder installed for {0}'.format(name))
@wrap_exceptions(SerializationError)
def dumps(self, data, serializer=None): def dumps(self, data, serializer=None):
if serializer == 'raw': if serializer == 'raw':
return raw_encode(data) return raw_encode(data)
@ -134,7 +147,8 @@ class SerializerRegistry(object):
# For Unicode objects, force it into a string # For Unicode objects, force it into a string
if not serializer and isinstance(data, text_t): if not serializer and isinstance(data, text_t):
payload = data.encode('utf-8') with _reraise_errors(EncodeError, exclude=()):
payload = data.encode('utf-8')
return 'text/plain', 'utf-8', payload return 'text/plain', 'utf-8', payload
if serializer: if serializer:
@ -145,11 +159,11 @@ class SerializerRegistry(object):
content_type = self._default_content_type content_type = self._default_content_type
content_encoding = self._default_content_encoding content_encoding = self._default_content_encoding
payload = encoder(data) with _reraise_errors(EncodeError):
payload = encoder(data)
return content_type, content_encoding, payload return content_type, content_encoding, payload
encode = dumps # XXX compat encode = dumps # XXX compat
@wrap_exceptions(DeserializationError)
def loads(self, data, content_type, content_encoding, def loads(self, data, content_type, content_encoding,
accept=None, force=False): accept=None, force=False):
if accept is not None: if accept is not None:
@ -164,10 +178,12 @@ class SerializerRegistry(object):
if data: if data:
decode = self._decoders.get(content_type) decode = self._decoders.get(content_type)
if decode: if decode:
return decode(data) with _reraise_errors(DecodeError):
return decode(data)
if content_encoding not in SKIP_DECODE and \ if content_encoding not in SKIP_DECODE and \
not isinstance(data, text_t): not isinstance(data, text_t):
return _decode(data, content_encoding) with _reraise_errors(DecodeError):
return _decode(data, content_encoding)
return data return data
decode = loads # XXX compat decode = loads # XXX compat
@ -280,7 +296,8 @@ def raw_encode(data):
payload = data payload = data
if isinstance(payload, text_t): if isinstance(payload, text_t):
content_encoding = 'utf-8' content_encoding = 'utf-8'
payload = payload.encode(content_encoding) with _reraise_errors(EncodeError, exclude=()):
payload = payload.encode(content_encoding)
else: else:
content_encoding = 'binary' content_encoding = 'binary'
return content_type, content_encoding, payload return content_type, content_encoding, payload

View File

@ -7,7 +7,7 @@ import sys
from base64 import b64decode from base64 import b64decode
from kombu.exceptions import ContentDisallowed from kombu.exceptions import ContentDisallowed, EncodeError, DecodeError
from kombu.five import text_t, bytes_t from kombu.five import text_t, bytes_t
from kombu.serialization import ( from kombu.serialization import (
registry, register, SerializerNotInstalled, registry, register, SerializerNotInstalled,
@ -186,6 +186,15 @@ class test_Serialization(Case):
call('pickle'), call('yaml'), call('doomsday') call('pickle'), call('yaml'), call('doomsday')
]) ])
def test_reraises_EncodeError(self):
with self.assertRaises(EncodeError):
dumps([object()], serializer='json')
def test_reraises_DecodeError(self):
with self.assertRaises(DecodeError):
loads(object(), content_type='application/json',
content_encoding='utf-8')
def test_json_loads(self): def test_json_loads(self):
self.assertEqual( self.assertEqual(
py_data, py_data,

View File

@ -429,20 +429,3 @@ def maybe_fileno(f):
return fileno(f) return fileno(f)
except FILENO_ERRORS: except FILENO_ERRORS:
pass pass
def wrap_exceptions(exception, catch=Exception):
"""
Catch the exception specified by ``catch`` and raise ``exception`` instead with
the old exception as the value.
"""
def decorator(func):
@wraps(func)
def wrap_exceptions_wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except catch as exc:
raise exception(exc)
return wrap_exceptions_wrapper
return decorator