Added unittets for kombu.entities (Exchange/Binding)

This commit is contained in:
Ask Solem 2010-06-28 23:29:40 +02:00
parent db24c9d1f7
commit 9a4074b568
4 changed files with 261 additions and 90 deletions

View File

@ -1,21 +1,47 @@
from copy import copy from copy import copy
TRANSIENT_DELIVERY_MODE = 1
PERSISTENT_DELIVERY_MODE = 2
DELIVERY_MODES = {"transient": TRANSIENT_DELIVERY_MODE,
"persistent": PERSISTENT_DELIVERY_MODE}
class NotBoundError(Exception):
"""Trying to call channel dependent method on unbound entity."""
class Object(object):
attrs = ()
def __init__(self, *args, **kwargs):
any = lambda v: v
for name, type_ in self.attrs:
value = kwargs.get(name)
if value is not None:
setattr(self, name, (type_ or any)(value))
def __copy__(self):
return self.__class__(**dict((name, getattr(self, name))
for name, _ in self.attrs))
def assert_is_bound(fun): def assert_is_bound(fun):
def only_if_bound(self, *args, **kwargs): def only_if_bound(self, *args, **kwargs):
if self.is_bound: if self.is_bound:
return fun(*args, **kwargs) return fun(self, *args, **kwargs)
raise NotBoundError("Can't call %s on unbound %s" % ( raise NotBoundError(
fun.__name__, self.__class__.__name__)) "Can't call %s on %s not bound to a channel" % (
fun.__name__,
self.__class__.__name__))
only_if_bound.__name__ = fun.__name__ only_if_bound.__name__ = fun.__name__
return only_if_bound return only_if_bound
class MaybeChannelBound(object): class MaybeChannelBound(Object):
"""Mixin for classes that can be bound to an AMQP channel.""" """Mixin for classes that can be bound to an AMQP channel."""
channel = None channel = None
_is_bound = False
def bind(self, channel): def bind(self, channel):
"""Create copy of the instance that is bound to a channel.""" """Create copy of the instance that is bound to a channel."""
@ -23,9 +49,10 @@ class MaybeChannelBound(object):
def maybe_bind(self, channel): def maybe_bind(self, channel):
"""Bind instance to channel if not already bound.""" """Bind instance to channel if not already bound."""
if not self.is_bound: if not self.is_bound and channel:
self.channel = channel self.channel = channel
self.when_bound() self.when_bound()
self._is_bound = True
return self return self
def when_bound(self): def when_bound(self):
@ -35,7 +62,7 @@ class MaybeChannelBound(object):
@property @property
def is_bound(self): def is_bound(self):
"""Returns ``True`` if the entity is bound.""" """Returns ``True`` if the entity is bound."""
return self.channel is not None return self._is_bound and self.channel is not None
def __repr__(self, item=""): def __repr__(self, item=""):
if self.is_bound: if self.is_bound:
@ -45,35 +72,29 @@ class MaybeChannelBound(object):
class Exchange(MaybeChannelBound): class Exchange(MaybeChannelBound):
TRANSIENT_DELIVERY_MODE = 1 TRANSIENT_DELIVERY_MODE = TRANSIENT_DELIVERY_MODE
PERSISTENT_DELIVERY_MODE = 2 PERSISTENT_DELIVERY_MODE = PERSISTENT_DELIVERY_MODE
DELIVERY_MODES = {
"transient": TRANSIENT_DELIVERY_MODE,
"persistent": PERSISTENT_DELIVERY_MODE,
}
name = "" name = ""
type = "direct" type = "direct"
routing_key = "" routing_key = ""
delivery_mode = PERSISTENT_DELIVERY_MODE
durable = True durable = True
auto_delete = False auto_delete = False
_init_opts = ("durable", "auto_delete", delivery_mode = PERSISTENT_DELIVERY_MODE
"delivery_mode", "auto_declare")
def __init__(self, name="", type="", routing_key=None, channel=None, attrs = (("name", None),
**kwargs): ("type", None),
("routing_key", None),
("channel", None),
("durable", bool),
("auto_delete", bool),
("delivery_mode", lambda m: DELIVERY_MODES.get(m) or m))
def __init__(self, name="", type="", routing_key="", **kwargs):
super(Exchange, self).__init__(**kwargs)
self.name = name or self.name self.name = name or self.name
self.type = type or self.type self.type = type or self.type
self.routing_key = routing_key or self.routing_key self.routing_key = routing_key or self.routing_key
self.maybe_bind(channel) self.maybe_bind(self.channel)
for opt_name in self._init_opts:
opt_value = kwargs.get(opt_name)
if opt_value is not None:
setattr(self, opt_name, opt_value)
self.delivery_mode = self.DELIVERY_MODES.get(self.delivery_mode,
self.delivery_mode)
@assert_is_bound @assert_is_bound
def declare(self): def declare(self):
@ -82,7 +103,7 @@ class Exchange(MaybeChannelBound):
Creates the exchange on the broker. Creates the exchange on the broker.
""" """
self.channel.exchange_declare(exchange=self.name, return self.channel.exchange_declare(exchange=self.name,
type=self.type, type=self.type,
durable=self.durable, durable=self.durable,
auto_delete=self.auto_delete) auto_delete=self.auto_delete)
@ -91,6 +112,7 @@ class Exchange(MaybeChannelBound):
def create_message(self, message_data, delivery_mode=None, def create_message(self, message_data, delivery_mode=None,
priority=None, content_type=None, content_encoding=None, priority=None, content_type=None, content_encoding=None,
properties=None): properties=None):
properties = properties or {}
properties["delivery_mode"] = delivery_mode or self.delivery_mode properties["delivery_mode"] = delivery_mode or self.delivery_mode
return self.channel.prepare_message(message_data, return self.channel.prepare_message(message_data,
properties=properties, properties=properties,
@ -99,31 +121,27 @@ class Exchange(MaybeChannelBound):
content_encoding=content_encoding) content_encoding=content_encoding)
@assert_is_bound @assert_is_bound
def publish(self, message, routing_key=None, def publish(self, message, routing_key=None, mandatory=False,
mandatory=False, immediate=False): immediate=False, headers=None):
if routing_key is None: if routing_key is None:
routing_key = self.routing_key routing_key = self.routing_key
self.channel.basic_publish(message, return self.channel.basic_publish(message,
exchange=self.name, exchange=self.name,
routing_key=routing_key, routing_key=routing_key,
mandatory=mandatory, mandatory=mandatory,
immediate=immediate, immediate=immediate,
headers=headers) headers=headers)
def __copy__(self): @assert_is_bound
return self.__class__(name=self.name, def delete(self, if_unused=False):
type=self.type, return self.channel.exchange_delete(self.name, if_unused=if_unused)
routing_key=self.routing_key,
channel=self.channel,
**dict((name, getattr(self, name))
for name in self._init_opts))
def __repr__(self): def __repr__(self):
super(Exchange, self).__repr__("Exchange %s(%s)" % (self.name, return super(Exchange, self).__repr__("Exchange %s(%s)" % (self.name,
self.type)) self.type))
class Binding(object): class Binding(MaybeChannelBound):
name = "" name = ""
exchange = None exchange = None
routing_key = "" routing_key = ""
@ -131,27 +149,24 @@ class Binding(object):
durable = True durable = True
exclusive = False exclusive = False
auto_delete = False auto_delete = False
warn_if_exists = False
_init_opts = ("durable", "exclusive", "auto_delete",
"warn_if_exists")
def __init__(self, name=None, exchange=None, routing_key=None, attrs = (("name", None),
channel=None, **kwargs): ("exchange", None),
# Binding. ("routing_key", None),
("channel", None),
("durable", bool),
("exclusive", bool),
("auto_delete", bool))
def __init__(self, name="", exchange=None, routing_key="", **kwargs):
super(Binding, self).__init__(**kwargs)
self.name = name or self.name self.name = name or self.name
self.exchange = exchange or self.exchange self.exchange = exchange or self.exchange
self.routing_key = routing_key or self.routing_key self.routing_key = routing_key or self.routing_key
self.maybe_bind(channel)
# Options
for opt_name in self._init_opts:
opt_value = kwargs.get(opt_name)
if opt_value is not None:
setattr(self, opt_name, opt_value)
# exclusive implies auto-delete. # exclusive implies auto-delete.
if self.exclusive: if self.exclusive:
self.auto_delete = True self.auto_delete = True
self.maybe_bind(self.channel)
def when_bound(self): def when_bound(self):
self.exchange = self.exchange.bind(self.channel) self.exchange = self.exchange.bind(self.channel)
@ -160,16 +175,16 @@ class Binding(object):
def declare(self): def declare(self):
"""Declares the queue, the exchange and binds the queue to """Declares the queue, the exchange and binds the queue to
the exchange.""" the exchange."""
if self.exchange: chan = self.channel
self.exchange.declare() return (self.exchange and self.exchange.declare(),
if self.name: self.name and chan.queue_declare(queue=self.name,
self.channel.queue_declare(queue=self.name,
durable=self.durable, durable=self.durable,
exclusive=self.exclusive, exclusive=self.exclusive,
auto_delete=self.auto_delete) auto_delete=self.auto_delete),
self.channel.queue_bind(queue=self.name, self.name and chan.queue_bind(queue=self.name,
exchange=self.exchange.name, exchange=self.exchange.name,
routing_key=self.routing_key) routing_key=self.routing_key))
@assert_is_bound @assert_is_bound
def get(self, no_ack=None): def get(self, no_ack=None):
@ -183,7 +198,7 @@ class Binding(object):
@assert_is_bound @assert_is_bound
def consume(self, consumer_tag, callback, no_ack=None, nowait=True): def consume(self, consumer_tag, callback, no_ack=None, nowait=True):
return self.channel.consume(queue=self.name, return self.channel.basic_consume(queue=self.name,
no_ack=no_ack, no_ack=no_ack,
consumer_tag=consumer_tag, consumer_tag=consumer_tag,
callback=callback, callback=callback,
@ -191,18 +206,10 @@ class Binding(object):
@assert_is_bound @assert_is_bound
def cancel(self, consumer_tag): def cancel(self, consumer_tag):
self.channel.basic_cancel(consumer_tag) return self.channel.basic_cancel(consumer_tag)
def __copy__(self):
return self.__class__(name=self.name,
exchange=self.exchange,
routing_key=self.routing_key,
channel=self.channel,
**dict((name, getattr(self, name)
for name in self._init_opts)))
def __repr__(self): def __repr__(self):
super(Binding, self).__repr__( return super(Binding, self).__repr__(
"Binding %s -> %s -> %s" % (self.name, "Binding %s -> %s -> %s" % (self.name,
self.exchange, self.exchange,
self.routing_key)) self.routing_key))

0
kombu/tests/__init__.py Normal file
View File

View File

@ -0,0 +1,159 @@
import unittest2 as unittest
from kombu.entity import Exchange, Binding, NotBoundError
class Channel(object):
def __init__(self):
self.called = []
def _called(self, name):
self.called.append(name)
def __contains__(self, key):
return key in self.called
def exchange_declare(self, *args, **kwargs):
self._called("exchange_declare")
def prepare_message(self, *args, **kwargs):
self._called("prepare_message")
def basic_publish(self, *args, **kwargs):
self._called("basic_publish")
def exchange_delete(self, *args, **kwargs):
self._called("exchange_delete")
def queue_declare(self, *args, **kwargs):
self._called("queue_declare")
def queue_bind(self, *args, **kwargs):
self._called("queue_bind")
def basic_get(self, *args, **kwargs):
self._called("basic_get")
def queue_purge(self, *args, **kwargs):
self._called("queue_purge")
def basic_consume(self, *args, **kwargs):
self._called("basic_consume")
def basic_cancel(self, *args, **kwargs):
self._called("basic_cancel")
class test_Exchange(unittest.TestCase):
def test_bound(self):
exchange = Exchange("foo", "direct")
self.assertFalse(exchange.is_bound)
self.assertIn("<unbound", repr(exchange))
chan = Channel()
bound = exchange.bind(chan)
self.assertTrue(bound.is_bound)
self.assertIs(bound.channel, chan)
self.assertIn("<bound", repr(bound))
def test_assert_is_bound(self):
exchange = Exchange("foo", "direct")
self.assertRaises(NotBoundError, exchange.declare)
chan = Channel()
exchange.bind(chan).declare()
self.assertIn("exchange_declare", chan)
def test_set_transient_delivery_mode(self):
exc = Exchange("foo", "direct", delivery_mode="transient")
self.assertEqual(exc.delivery_mode, Exchange.TRANSIENT_DELIVERY_MODE)
def test_set_persistent_delivery_mode(self):
exc = Exchange("foo", "direct", delivery_mode="persistent")
self.assertEqual(exc.delivery_mode, Exchange.PERSISTENT_DELIVERY_MODE)
def test_bind_at_instantiation(self):
self.assertTrue(Exchange("foo", channel=Channel()).is_bound)
def test_create_message(self):
chan = Channel()
Exchange("foo", channel=chan).create_message({"foo": "bar"})
self.assertIn("prepare_message", chan)
def test_publish(self):
chan = Channel()
Exchange("foo", channel=chan).publish("the quick brown fox")
self.assertIn("basic_publish", chan)
def test_delete(self):
chan = Channel()
Exchange("foo", channel=chan).delete()
self.assertIn("exchange_delete", chan)
def test__repr__(self):
b = Exchange("foo", "topic")
self.assertIn("foo(topic)", repr(b))
self.assertIn("Exchange", repr(b))
class test_Binding(unittest.TestCase):
def setUp(self):
self.exchange = Exchange("foo", "direct")
def test_exclusive_implies_auto_delete(self):
self.assertTrue(
Binding("foo", self.exchange, exclusive=True).auto_delete)
def test_binds_at_instantiation(self):
self.assertTrue(
Binding("foo", self.exchange, channel=Channel()).is_bound)
def test_also_binds_exchange(self):
chan = Channel()
b = Binding("foo", self.exchange)
self.assertFalse(b.is_bound)
self.assertFalse(b.exchange.is_bound)
b = b.bind(chan)
self.assertTrue(b.is_bound)
self.assertTrue(b.exchange.is_bound)
self.assertIs(b.channel, b.exchange.channel)
self.assertIsNot(b.exchange, self.exchange)
def test_declare(self):
chan = Channel()
b = Binding("foo", self.exchange, "foo", channel=chan)
self.assertTrue(b.is_bound)
b.declare()
self.assertIn("exchange_declare", chan)
self.assertIn("queue_declare", chan)
self.assertIn("queue_bind", chan)
def test_get(self):
b = Binding("foo", self.exchange, "foo", channel=Channel())
b.get()
self.assertIn("basic_get", b.channel)
def test_purge(self):
b = Binding("foo", self.exchange, "foo", channel=Channel())
b.purge()
self.assertIn("queue_purge", b.channel)
def test_consume(self):
b = Binding("foo", self.exchange, "foo", channel=Channel())
b.consume("fifafo", None)
self.assertIn("basic_consume", b.channel)
def test_cancel(self):
b = Binding("foo", self.exchange, "foo", channel=Channel())
b.cancel("fifafo")
self.assertIn("basic_cancel", b.channel)
def test__repr__(self):
b = Binding("foo", self.exchange, "foo")
self.assertIn("foo", repr(b))
self.assertIn("Binding", repr(b))

View File

@ -1,6 +1,11 @@
[nosetests] [nosetests]
verbosity = 1 verbosity = 1
detailed-errors = 1 detailed-errors = 1
where = kombu/tests
cover3-branch = 1
cover3-html = 1
cover3-package = kombu
[build_sphinx] [build_sphinx]
source-dir = docs/ source-dir = docs/