Skip to content

Commit f1d1cd2

Browse files
committed
fix: consistent hashing and equality for RabbitMQ schemas
1 parent 23e87e8 commit f1d1cd2

File tree

6 files changed

+97
-15
lines changed

6 files changed

+97
-15
lines changed

faststream/rabbit/schemas/channel.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,10 @@ class Channel:
2929
when mandatory message will be returned"""
3030

3131
def __hash__(self) -> int:
32-
return id(self)
32+
return hash((
33+
self.prefetch_count,
34+
self.global_qos,
35+
self.channel_number,
36+
self.publisher_confirms,
37+
self.on_return_raises,
38+
))

faststream/rabbit/schemas/exchange.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,29 @@ def __repr__(self) -> str:
3434

3535
return f"{self.__class__.__name__}({self.name}, type={self.type}, routing_key='{self.routing()}'{body})"
3636

37+
def __eq__(self, value: object, /) -> bool:
38+
if not isinstance(value, RabbitExchange):
39+
return NotImplemented
40+
41+
return (
42+
self.name == value.name
43+
and self.type == value.type
44+
and self.routing_key == value.routing_key
45+
and self.durable == value.durable
46+
and self.auto_delete == value.auto_delete
47+
and self.arguments == value.arguments
48+
)
49+
3750
def __hash__(self) -> int:
3851
"""Supports hash to store real objects in declarer."""
39-
return sum(
52+
return hash(
4053
(
41-
hash(self.name),
42-
hash(self.type),
43-
hash(self.routing_key),
44-
int(self.durable),
45-
int(self.auto_delete),
54+
self.name,
55+
self.type,
56+
self.routing_key,
57+
self.durable,
58+
self.auto_delete,
59+
frozenset((self.arguments or {}).items()),
4660
),
4761
)
4862

faststream/rabbit/schemas/queue.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,27 @@ def __repr__(self) -> str:
5454

5555
return f"{self.__class__.__name__}({self.name}{body})"
5656

57+
def __eq__(self, value: object, /) -> bool:
58+
if not isinstance(value, RabbitQueue):
59+
return NotImplemented
60+
61+
return (
62+
self.name == value.name
63+
and self.durable == value.durable
64+
and self.exclusive == value.exclusive
65+
and self.auto_delete == value.auto_delete
66+
and self.arguments == value.arguments
67+
)
68+
5769
def __hash__(self) -> int:
5870
"""Supports hash to store real objects in declarer."""
59-
return sum(
71+
return hash(
6072
(
61-
hash(self.name),
62-
int(self.durable),
63-
int(self.exclusive),
64-
int(self.auto_delete),
73+
self.name,
74+
self.durable,
75+
self.exclusive,
76+
self.auto_delete,
77+
frozenset((self.arguments or {}).items()),
6578
),
6679
)
6780

faststream/rabbit/testing.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,9 @@ def _is_handler_matches(
293293
headers = headers or {}
294294
exchange = RabbitExchange.validate(exchange)
295295

296-
if handler.exchange != exchange:
296+
if (handler.exchange.name if handler.exchange else None) != (
297+
exchange.name if exchange else None
298+
):
297299
return False
298300

299301
if handler.exchange is None or handler.exchange.type == ExchangeType.DIRECT:

tests/brokers/rabbit/specific/test_channels.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ async def test_subscriber_use_shared_channel() -> None:
1212
broker = RabbitBroker(logger=None)
1313

1414
sub1 = broker.subscriber(uuid4().hex)
15-
sub2 = broker.subscriber(uuid4().hex, channel=Channel())
15+
sub2 = broker.subscriber(uuid4().hex, channel=Channel(prefetch_count=1))
1616

1717
shared_channel = Channel()
1818
sub3 = broker.subscriber(uuid4().hex, channel=shared_channel)

tests/brokers/rabbit/test_schemas.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
from faststream.rabbit import RabbitQueue
3+
from faststream.rabbit import Channel, RabbitExchange, RabbitQueue
44

55

66
@pytest.mark.rabbit()
@@ -23,3 +23,50 @@ def test_different_queue_routing_key() -> None:
2323
})
2424
== 1
2525
)
26+
27+
28+
@pytest.mark.rabbit()
29+
def test_different_queue_params() -> None:
30+
assert (
31+
len({
32+
RabbitQueue("test", durable=True): 0,
33+
RabbitQueue("test", durable=False): 1,
34+
})
35+
== 2
36+
)
37+
38+
39+
@pytest.mark.rabbit()
40+
def test_exchange_equality() -> None:
41+
assert (
42+
len({
43+
RabbitExchange("test", durable=True): 0,
44+
RabbitExchange("test", durable=True): 1,
45+
})
46+
== 1
47+
)
48+
assert (
49+
len({
50+
RabbitExchange("test", durable=True): 0,
51+
RabbitExchange("test", durable=False): 1,
52+
})
53+
== 2
54+
)
55+
56+
57+
@pytest.mark.rabbit()
58+
def test_channel_equality() -> None:
59+
assert (
60+
len({
61+
Channel(prefetch_count=10): 0,
62+
Channel(prefetch_count=10): 1,
63+
})
64+
== 1
65+
)
66+
assert (
67+
len({
68+
Channel(prefetch_count=10): 0,
69+
Channel(prefetch_count=20): 1,
70+
})
71+
== 2
72+
)

0 commit comments

Comments
 (0)