feat: allow pydantic RedisDsn in addition to str for redis_url (#62)

This commit is contained in:
d3vyce
2026-06-26 20:59:23 +02:00
committed by GitHub
parent 58c520c33a
commit e45aa8f977
5 changed files with 21 additions and 4 deletions
+1 -1
View File
@@ -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. |
+1
View File
@@ -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",
] ]
+5 -3
View File
@@ -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
+12
View File
@@ -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()
Generated
+2
View File
@@ -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" },
] ]