mirror of
https://github.com/d3vyce/taskiq-deduplication.git
synced 2026-08-04 19:14:07 +00:00
feat: allow pydantic RedisDsn in addition to str for redis_url (#62)
This commit is contained in:
+1
-1
@@ -17,7 +17,7 @@ broker = ListQueueBroker("redis://localhost:6379").with_middlewares(
|
|||||||
|
|
||||||
| Parameter | Type | Default | Description |
|
| Parameter | Type | Default | Description |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `redis_url` | `str` | — | Redis connection URL passed to `Redis.from_url`. |
|
| `redis_url` | `str \| RedisDsn` | — | Redis connection URL passed to `Redis.from_url`. Accepts a plain string or a pydantic [`RedisDsn`](https://docs.pydantic.dev/latest/api/networks/#pydantic.networks.RedisDsn). |
|
||||||
| `default_deduplication` | `bool` | `True` | Whether deduplication is enabled for all tasks by default. Set `False` to opt-in per task instead of opting out. |
|
| `default_deduplication` | `bool` | `True` | Whether deduplication is enabled for all tasks by default. Set `False` to opt-in per task instead of opting out. |
|
||||||
| `default_ttl` | `int` | `300` | Default lock TTL in seconds. Overridden per task with the `deduplication_ttl` label. |
|
| `default_ttl` | `int` | `300` | Default lock TTL in seconds. Overridden per task with the `deduplication_ttl` label. |
|
||||||
| `key_prefix` | `str` | `"taskiq:deduplication"` | Prefix for all Redis lock keys. |
|
| `key_prefix` | `str` | `"taskiq:deduplication"` | Prefix for all Redis lock keys. |
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ classifiers = [
|
|||||||
"Typing :: Typed",
|
"Typing :: Typed",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"pydantic>=2.0.0",
|
||||||
"redis>=7.0.0",
|
"redis>=7.0.0",
|
||||||
"taskiq>=0.12.0",
|
"taskiq>=0.12.0",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, Awaitable, cast
|
from typing import Any, Awaitable, cast
|
||||||
|
|
||||||
|
from pydantic import RedisDsn
|
||||||
from redis.asyncio import Redis
|
from redis.asyncio import Redis
|
||||||
from taskiq import TaskiqMessage, TaskiqResult
|
from taskiq import TaskiqMessage, TaskiqResult
|
||||||
from taskiq.abc.middleware import TaskiqMiddleware
|
from taskiq.abc.middleware import TaskiqMiddleware
|
||||||
@@ -39,7 +40,8 @@ class RedisDeduplicationMiddleware(TaskiqMiddleware):
|
|||||||
on completion or error.
|
on completion or error.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
redis_url: Redis connection URL passed to ``Redis.from_url``.
|
redis_url: Redis connection URL (``str`` or ``RedisDsn``) passed to
|
||||||
|
``Redis.from_url``.
|
||||||
default_deduplication: Whether deduplication is enabled by default.
|
default_deduplication: Whether deduplication is enabled by default.
|
||||||
default_ttl: Default lock TTL in seconds.
|
default_ttl: Default lock TTL in seconds.
|
||||||
key_prefix: Prefix for all Redis lock keys.
|
key_prefix: Prefix for all Redis lock keys.
|
||||||
@@ -47,7 +49,7 @@ class RedisDeduplicationMiddleware(TaskiqMiddleware):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
redis_url: str,
|
redis_url: str | RedisDsn,
|
||||||
default_deduplication: bool = True,
|
default_deduplication: bool = True,
|
||||||
default_ttl: int = 300,
|
default_ttl: int = 300,
|
||||||
key_prefix: str = "taskiq:deduplication",
|
key_prefix: str = "taskiq:deduplication",
|
||||||
@@ -66,7 +68,7 @@ class RedisDeduplicationMiddleware(TaskiqMiddleware):
|
|||||||
async def startup(self) -> None:
|
async def startup(self) -> None:
|
||||||
last_error: BaseException | None = None
|
last_error: BaseException | None = None
|
||||||
for attempt in range(self.startup_retries):
|
for attempt in range(self.startup_retries):
|
||||||
client = Redis.from_url(self.redis_url)
|
client = Redis.from_url(str(self.redis_url))
|
||||||
try:
|
try:
|
||||||
await cast(Awaitable[bool], client.ping())
|
await cast(Awaitable[bool], client.ping())
|
||||||
self._redis = client
|
self._redis = client
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from unittest.mock import AsyncMock, MagicMock, patch
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from pydantic import RedisDsn, TypeAdapter
|
||||||
|
|
||||||
from taskiq_deduplication import DuplicateTaskError, RedisDeduplicationMiddleware
|
from taskiq_deduplication import DuplicateTaskError, RedisDeduplicationMiddleware
|
||||||
from taskiq_deduplication.middleware import (
|
from taskiq_deduplication.middleware import (
|
||||||
@@ -341,6 +342,17 @@ class TestLifecycle:
|
|||||||
mock_from_url.assert_called_once_with("redis://localhost")
|
mock_from_url.assert_called_once_with("redis://localhost")
|
||||||
assert mw._redis is mock_client
|
assert mw._redis is mock_client
|
||||||
|
|
||||||
|
async def test_startup_accepts_redis_dsn(self):
|
||||||
|
dsn = TypeAdapter(RedisDsn).validate_python("redis://localhost:6379/0")
|
||||||
|
mw = RedisDeduplicationMiddleware(redis_url=dsn)
|
||||||
|
with patch("redis.asyncio.Redis.from_url") as mock_from_url:
|
||||||
|
mock_client = AsyncMock()
|
||||||
|
mock_client.register_script = MagicMock()
|
||||||
|
mock_from_url.return_value = mock_client
|
||||||
|
await mw.startup()
|
||||||
|
mock_from_url.assert_called_once_with("redis://localhost:6379/0")
|
||||||
|
assert mw._redis is mock_client
|
||||||
|
|
||||||
async def test_shutdown_closes_redis_client(self):
|
async def test_shutdown_closes_redis_client(self):
|
||||||
mw = RedisDeduplicationMiddleware(redis_url="redis://localhost")
|
mw = RedisDeduplicationMiddleware(redis_url="redis://localhost")
|
||||||
mock_client = AsyncMock()
|
mock_client = AsyncMock()
|
||||||
|
|||||||
@@ -1488,6 +1488,7 @@ name = "taskiq-deduplication"
|
|||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
{ name = "pydantic" },
|
||||||
{ name = "redis" },
|
{ name = "redis" },
|
||||||
{ name = "taskiq" },
|
{ name = "taskiq" },
|
||||||
]
|
]
|
||||||
@@ -1520,6 +1521,7 @@ tests = [
|
|||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
|
{ name = "pydantic", specifier = ">=2.0.0" },
|
||||||
{ name = "redis", specifier = ">=7.0.0" },
|
{ name = "redis", specifier = ">=7.0.0" },
|
||||||
{ name = "taskiq", specifier = ">=0.12.0" },
|
{ name = "taskiq", specifier = ">=0.12.0" },
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user