Permalink
Browse files

Allow dashi to translate exceptions to dashi exceptions

For instance, we can tell dashi that any CustomModuleException must be
translated as NotFoundError, which will be shipped to the client.
On the client it can be handled without parsing a DashiError object to
find out what was the real exception on the server.
  • Loading branch information...
1 parent 1292a6e commit 7a0d25a48b514ec4107a6995960e4935d4053c54 @priteau priteau committed Oct 12, 2012
Showing with 51 additions and 1 deletion.
  1. +18 −0 dashi/__init__.py
  2. +33 −1 dashi/tests/test_dashi.py
View
@@ -51,6 +51,8 @@ def __init__(self, name, uri, exchange, durable=False, auto_delete=True,
self._consumer_conn = None
self._consumer = None
+ self._linked_exceptions = {}
+
self._serializer = serializer
@property
@@ -190,6 +192,16 @@ def disconnect(self):
if self._consumer:
self._consumer.disconnect()
+ def link_exceptions(self, custom_exception=None, dashi_exception=None):
+ """Link a custom exception thrown on the receiver to a dashi exception
+ """
+ if custom_exception is None:
+ raise ValueError("custom_exception must be set")
+ if dashi_exception is None:
+ raise ValueError("dashi_exception must be set")
+
+ self._linked_exceptions[custom_exception] = dashi_exception
+
_OpSpec = namedtuple('_OpSpec', ['function', 'sender_kwarg'])
class DashiConsumer(object):
@@ -325,6 +337,12 @@ def _callback(self, body, message):
# name on the exc_type.
exc_type = err[0]
+
+ # Check if there is a dashi exception linked to this custom exception
+ linked_exception = self._dashi._linked_exceptions.get(exc_type)
+ if linked_exception:
+ exc_type = linked_exception
+
known_type = ERROR_TYPE_MAP.get(exc_type.__name__)
if known_type and exc_type is known_type:
exc_type_name = ERROR_PREFIX + exc_type.__name__
@@ -15,6 +15,7 @@
log = logging.getLogger(__name__)
+_NO_EXCEPTION = object()
_NO_REPLY = object()
class TestReceiver(object):
@@ -33,11 +34,15 @@ def __init__(self, **kwargs):
self.conn.consumer_timeout = 0.01
self.received = []
self.reply_with = {}
+ self.raise_exception = {}
self.consumer_thread = None
self.condition = threading.Condition()
- def handle(self, opname, reply_with=_NO_REPLY, **kwargs):
+ def handle(self, opname, reply_with=_NO_REPLY, raise_exception=_NO_EXCEPTION, **kwargs):
+ if raise_exception is not _NO_EXCEPTION:
+ self.raise_exception[opname] = raise_exception
+
if reply_with is not _NO_REPLY:
self.reply_with[opname] = reply_with
self.conn.handle(partial(self._handler, opname), opname, **kwargs)
@@ -47,6 +52,10 @@ def _handler(self, opname, **kwargs):
self.received.append((opname, kwargs))
self.condition.notifyAll()
+ if opname in self.raise_exception:
+ raise_exception = self.raise_exception[opname]
+ raise raise_exception(opname)
+
if opname in self.reply_with:
reply_with = self.reply_with[opname]
if callable(reply_with):
@@ -300,6 +309,29 @@ def test_handle_sender_kwarg(self):
receiver.cancel()
receiver.join_consumer_thread()
+ def test_exceptions(self):
+ class CustomNotFoundError(Exception):
+ pass
+
+ receiver = TestReceiver(uri=self.uri, exchange="x1",
+ transport_options=self.transport_options)
+ receiver.conn.link_exceptions(custom_exception=CustomNotFoundError, dashi_exception=dashi.exceptions.NotFoundError)
+ receiver.handle("test_exception", raise_exception=CustomNotFoundError, sender_kwarg="sender")
+ receiver.consume_in_thread(1)
+
+ conn = dashi.DashiConnection("s1", self.uri, "x1",
+ transport_options=self.transport_options)
+ args1 = dict(a=1, b="sandwich")
+
+ try:
+ ret = conn.call(receiver.name, "test_exception", **args1)
+ except dashi.exceptions.NotFoundError:
+ pass
+ else:
+ self.fail("Expected NotFoundError")
+ finally:
+ receiver.join_consumer_thread()
+
class RabbitDashiConnectionTests(DashiConnectionTests):
"""The base dashi tests run on rabbit, plus some extras which are

0 comments on commit 7a0d25a

Please sign in to comment.