Skip to content

Commit fab7ae7

Browse files
committed
Merge branch 'release/0.3.0'
2 parents e17201d + 5db783e commit fab7ae7

9 files changed

+199
-243
lines changed

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ Let's see the example with the redis broker and redis async result:
2020
```python
2121
import asyncio
2222

23-
from taskiq_redis.redis_broker import ListQueueBroker
24-
from taskiq_redis.redis_backend import RedisAsyncResultBackend
23+
from taskiq_redis import ListQueueBroker, RedisAsyncResultBackend
2524

2625
redis_async_result = RedisAsyncResultBackend(
2726
redis_url="redis://localhost:6379",
@@ -71,9 +70,10 @@ Brokers parameters:
7170
RedisAsyncResultBackend parameters:
7271
* `redis_url` - url to redis.
7372
* `keep_results` - flag to not remove results from Redis after reading.
74-
* `result_ex_time` - expire time in seconds (by default - 1 minute)
73+
* `result_ex_time` - expire time in seconds (by default - not specified)
7574
* `result_px_time` - expire time in milliseconds (by default - not specified)
76-
> IMPORTANT: You must specify either `result_ex_time` or `result_px_time`.
75+
> IMPORTANT: **It is highly recommended to use expire time ​​in RedisAsyncResultBackend**
76+
> If you want to add expiration, either `result_ex_time` or `result_px_time` must be set.
7777
>```python
7878
># First variant
7979
>redis_async_result = RedisAsyncResultBackend(

poetry.lock

+130-183
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "taskiq-redis"
3-
version = "0.2.1"
3+
version = "0.3.0"
44
description = "Redis integration for taskiq"
55
authors = ["taskiq-team <[email protected]>"]
66
readme = "README.md"
@@ -19,7 +19,7 @@ keywords = ["taskiq", "tasks", "distributed", "async", "redis", "result_backend"
1919

2020
[tool.poetry.dependencies]
2121
python = "^3.7"
22-
taskiq = "^0.2.0"
22+
taskiq = "^0"
2323
redis = "^4.2.0"
2424

2525
[tool.poetry.dev-dependencies]

taskiq_redis/__init__.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
"""Package for redis integration."""
22
from taskiq_redis.redis_backend import RedisAsyncResultBackend
3-
from taskiq_redis.redis_broker import ListQueueBroker
3+
from taskiq_redis.redis_broker import ListQueueBroker, PubSubBroker
44

5-
__all__ = ["RedisAsyncResultBackend", "ListQueueBroker"]
5+
__all__ = [
6+
"RedisAsyncResultBackend",
7+
"ListQueueBroker",
8+
"PubSubBroker",
9+
]

taskiq_redis/redis_backend.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,13 @@ def __init__(
4141
self.result_ex_time = result_ex_time
4242
self.result_px_time = result_px_time
4343

44-
if self.result_ex_time == 0 or self.result_px_time == 0:
44+
unavailable_conditions = any(
45+
(
46+
self.result_ex_time is not None and self.result_ex_time <= 0,
47+
self.result_px_time is not None and self.result_px_time <= 0,
48+
),
49+
)
50+
if unavailable_conditions:
4551
raise ExpireTimeMustBeMoreThanZeroError(
4652
"You must select one expire time param and it must be more than zero.",
4753
)
@@ -51,9 +57,6 @@ def __init__(
5157
"Choose either result_ex_time or result_px_time.",
5258
)
5359

54-
if not self.result_ex_time and not self.result_px_time:
55-
self.result_ex_time = 60
56-
5760
async def shutdown(self) -> None:
5861
"""Closes redis connection."""
5962
await self.redis_pool.disconnect()

taskiq_redis/redis_broker.py

+36-40
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import pickle
2-
from abc import abstractmethod
31
from logging import getLogger
42
from typing import Any, AsyncGenerator, Callable, Optional, TypeVar
53

@@ -49,68 +47,66 @@ def __init__(
4947

5048
async def shutdown(self) -> None:
5149
"""Closes redis connection pool."""
50+
await super().shutdown()
5251
await self.connection_pool.disconnect()
5352

54-
async def listen(self) -> AsyncGenerator[BrokerMessage, None]:
55-
"""
56-
Listen redis queue for new messages.
5753

58-
This function listens to the queue
59-
and yields new messages if they have BrokerMessage type.
54+
class PubSubBroker(BaseRedisBroker):
55+
"""Broker that works with Redis and broadcasts tasks to all workers."""
6056

61-
:yields: broker messages.
62-
"""
63-
async for message in self._listen_to_raw_messages():
64-
try:
65-
redis_message = pickle.loads(message)
66-
if isinstance(redis_message, BrokerMessage):
67-
yield redis_message
68-
except (
69-
TypeError,
70-
AttributeError,
71-
pickle.UnpicklingError,
72-
) as exc:
73-
logger.debug(
74-
"Cannot read broker message %s",
75-
exc,
76-
exc_info=True,
77-
)
78-
79-
@abstractmethod
80-
async def _listen_to_raw_messages(self) -> AsyncGenerator[bytes, None]:
57+
async def kick(self, message: BrokerMessage) -> None:
8158
"""
82-
Generator for reading raw data from Redis.
59+
Publish message over PUBSUB channel.
8360
84-
:yields: raw data.
61+
:param message: message to send.
8562
"""
86-
yield # type: ignore
87-
63+
async with Redis(connection_pool=self.connection_pool) as redis_conn:
64+
await redis_conn.publish(self.queue_name, message.message)
8865

89-
class PubSubBroker(BaseRedisBroker):
90-
"""Broker that works with Redis and broadcasts tasks to all workers."""
66+
async def listen(self) -> AsyncGenerator[bytes, None]:
67+
"""
68+
Listen redis queue for new messages.
9169
92-
async def kick(self, message: BrokerMessage) -> None: # noqa: D102
93-
async with Redis(connection_pool=self.connection_pool) as redis_conn:
94-
await redis_conn.publish(self.queue_name, pickle.dumps(message))
70+
This function listens to the pubsub channel
71+
and yields all messages with proper types.
9572
96-
async def _listen_to_raw_messages(self) -> AsyncGenerator[bytes, None]:
73+
:yields: broker messages.
74+
"""
9775
async with Redis(connection_pool=self.connection_pool) as redis_conn:
9876
redis_pubsub_channel = redis_conn.pubsub()
9977
await redis_pubsub_channel.subscribe(self.queue_name)
10078
async for message in redis_pubsub_channel.listen():
10179
if not message:
10280
continue
81+
if message["type"] != "message":
82+
logger.debug("Received non-message from redis: %s", message)
83+
continue
10384
yield message["data"]
10485

10586

10687
class ListQueueBroker(BaseRedisBroker):
10788
"""Broker that works with Redis and distributes tasks between workers."""
10889

109-
async def kick(self, message: BrokerMessage) -> None: # noqa: D102
90+
async def kick(self, message: BrokerMessage) -> None:
91+
"""
92+
Put a message in a list.
93+
94+
This method appends a message to the list of all messages.
95+
96+
:param message: message to append.
97+
"""
11098
async with Redis(connection_pool=self.connection_pool) as redis_conn:
111-
await redis_conn.lpush(self.queue_name, pickle.dumps(message))
99+
await redis_conn.lpush(self.queue_name, message.message)
112100

113-
async def _listen_to_raw_messages(self) -> AsyncGenerator[bytes, None]:
101+
async def listen(self) -> AsyncGenerator[bytes, None]:
102+
"""
103+
Listen redis queue for new messages.
104+
105+
This function listens to the queue
106+
and yields new messages if they have BrokerMessage type.
107+
108+
:yields: broker messages.
109+
"""
114110
redis_brpop_data_position = 1
115111
async with Redis(connection_pool=self.connection_pool) as redis_conn:
116112
while True: # noqa: WPS457

tests/test_backend.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
import pytest
66
from taskiq import TaskiqResult
77

8+
from taskiq_redis import RedisAsyncResultBackend
89
from taskiq_redis.exceptions import (
910
DuplicateExpireTimeSelectedError,
1011
ExpireTimeMustBeMoreThanZeroError,
1112
)
12-
from taskiq_redis.redis_backend import RedisAsyncResultBackend
1313

1414
_ReturnType = TypeVar("_ReturnType")
1515

@@ -132,7 +132,13 @@ async def test_cant_specify_ex_and_px_params(
132132

133133

134134
@pytest.mark.anyio
135+
@pytest.mark.parametrize(
136+
"ex_time, px_time",
137+
[(0, 0), (-500, 0), (0, -500), (-500, -500)],
138+
)
135139
async def test_ex_or_px_must_be_more_than_zero(
140+
ex_time: int,
141+
px_time: int,
136142
redis_url: str,
137143
) -> None:
138144
"""
@@ -143,8 +149,8 @@ async def test_ex_or_px_must_be_more_than_zero(
143149
with pytest.raises(ExpireTimeMustBeMoreThanZeroError):
144150
RedisAsyncResultBackend(
145151
redis_url,
146-
result_ex_time=0,
147-
result_px_time=0,
152+
result_ex_time=ex_time,
153+
result_px_time=px_time,
148154
)
149155

150156

tests/test_broker.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
import pytest
55
from taskiq import AsyncBroker, BrokerMessage
66

7-
from taskiq_redis.redis_broker import ListQueueBroker, PubSubBroker
7+
from taskiq_redis import ListQueueBroker, PubSubBroker
88

99

10-
async def get_message(broker: AsyncBroker) -> BrokerMessage: # type: ignore
10+
async def get_message(broker: AsyncBroker) -> bytes: # type: ignore
1111
"""
1212
Get a message from the broker.
1313
@@ -56,7 +56,7 @@ async def test_pub_sub_broker(
5656

5757
message1 = worker1_task.result()
5858
message2 = worker2_task.result()
59-
assert message1 == valid_broker_message
59+
assert message1 == valid_broker_message.message
6060
assert message1 == message2
6161

6262

@@ -81,4 +81,4 @@ async def test_list_queue_broker(
8181

8282
assert worker1_task.done() != worker2_task.done()
8383
message = worker1_task.result() if worker1_task.done() else worker2_task.result()
84-
assert message == valid_broker_message
84+
assert message == valid_broker_message.message

tests/test_result_backend.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import pytest
44
from taskiq import TaskiqResult
55

6-
from taskiq_redis.redis_backend import RedisAsyncResultBackend
6+
from taskiq_redis import RedisAsyncResultBackend
77

88

99
@pytest.mark.anyio

0 commit comments

Comments
 (0)