diff --git a/.github/ci/provider-groups.json b/.github/ci/provider-groups.json
index 63330e1a..64c635c0 100644
--- a/.github/ci/provider-groups.json
+++ b/.github/ci/provider-groups.json
@@ -27,6 +27,7 @@
"tests/test_elasticsearch.py::test_plugin_imports_without_elasticsearch_clients",
"tests/test_mongodb.py::test_plugin_imports_without_pymongo",
"tests/test_mssql.py::test_plugin_imports_without_pymssql",
+ "tests/test_redis.py::test_plugin_imports_without_redis",
"tests/test_spanner.py::test_plugin_imports_without_google_cloud_spanner",
"tests/test_valkey.py::test_plugin_imports_without_valkey"
],
diff --git a/docs/supported-databases/redis.rst b/docs/supported-databases/redis.rst
index 60940041..01289e06 100644
--- a/docs/supported-databases/redis.rst
+++ b/docs/supported-databases/redis.rst
@@ -1,14 +1,16 @@
Redis
=====
-Integration with `Redis `_ using the `Redis Docker Image `_, Snap's `Key DB` or `Dragonfly `_.
+Integration with `Redis `_ using the `Redis Docker Image `_, `KeyDB `_, or `Dragonfly `_. KeyDB and Dragonfly are wire-compatible with Redis, so a ``redis.Redis`` client works against all three services.
Installation
------------
.. code-block:: bash
- pip install pytest-databases[redis]
+ pip install pytest-databases[redis] redis
+
+The ``redis`` package is no longer pulled by the ``pytest-databases[redis]`` extra — the fixtures validate the container via ``redis-cli`` invoked from a short-lived sidecar — so install your own client (``redis``, ``redis[hiredis]``, etc.) alongside ``pytest-databases``.
Usage Example
-------------
@@ -21,18 +23,24 @@ Usage Example
pytest_plugins = ["pytest_databases.docker.redis"]
- def test(redis_service: RedisService) -> None:
+ def test_redis(redis_service: RedisService) -> None:
client = redis.Redis(
host=redis_service.host,
port=redis_service.port,
- db=redis_service.db
+ db=redis_service.db,
)
client.set("test_key", "test_value")
assert client.get("test_key") == b"test_value"
- def test(redis_connection: redis.Redis) -> None:
- redis_connection.set("test_key", "test_value")
- assert redis_connection.get("test_key") == b"test_value"
+ def test_keydb(keydb_service: RedisService) -> None:
+ client = redis.Redis(host=keydb_service.host, port=keydb_service.port, db=keydb_service.db)
+ client.set("test_key", "test_value")
+ assert client.get("test_key") == b"test_value"
+
+ def test_dragonfly(dragonfly_service: RedisService) -> None:
+ client = redis.Redis(host=dragonfly_service.host, port=dragonfly_service.port, db=dragonfly_service.db)
+ client.set("test_key", "test_value")
+ assert client.get("test_key") == b"test_value"
Available Fixtures
------------------
@@ -40,13 +48,12 @@ Available Fixtures
* ``redis_port``: The port number for the Redis service.
* ``redis_host``: The host name for the Redis service.
* ``redis_image``: The Docker image to use for Redis.
-* ``redis_service``: A fixture that provides a Redis service.
-* ``redis_connection``: A fixture that provides a Redis connection.
+* ``redis_service``: A fixture that provides a ``RedisService`` (``host``, ``port``, ``container``, ``db``).
-The following version-specific fixtures are also available:
+The following compatible-service fixtures are also available:
-* ``dragonflydb_port``, ``dragonflydb_host``, ``dragonflydb_image``, ``dragonflydb_service``, ``dragonflydb_connection``: Latest Available DragonflyDB Docker image.
-* ``keydb_port``, ``keydb_host``, ``keydb_image``, ``keydb_service``, ``keydb_connection``: Latest Available KeyDB Docker image.
+* ``dragonfly_port``, ``dragonfly_host``, ``dragonfly_image``, ``dragonfly_service``: Latest available DragonflyDB Docker image.
+* ``keydb_port``, ``keydb_host``, ``keydb_image``, ``keydb_service``: Latest available KeyDB Docker image.
Service API
-----------
diff --git a/pyproject.toml b/pyproject.toml
index 830b7d5c..a68fecd8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -62,18 +62,18 @@ Source = "https://github.com/litestar-org/pytest-databases"
azure-storage = ["azure-storage-blob"]
bigquery = ["google-cloud-bigquery"]
cockroachdb = []
-dragonfly = ["redis"]
+dragonfly = []
elasticsearch7 = []
elasticsearch8 = []
gizmosql = ["adbc-driver-flightsql", "pyarrow"]
-keydb = ["redis"]
+keydb = []
mariadb = []
mongodb = []
mssql = []
mysql = []
oracle = []
postgres = ["psycopg>=3"]
-redis = ["redis"]
+redis = []
spanner = []
valkey = []
yugabyte = []
@@ -115,7 +115,6 @@ lint = [
"types-decorator",
"types-pyyaml",
"types-docutils",
- "types-redis",
"types-pymysql",
"slotscheck",
]
diff --git a/src/pytest_databases/docker/redis.py b/src/pytest_databases/docker/redis.py
index 0aee10ca..9c91eb62 100644
--- a/src/pytest_databases/docker/redis.py
+++ b/src/pytest_databases/docker/redis.py
@@ -4,18 +4,72 @@
from typing import TYPE_CHECKING
import pytest
-from redis import Redis
-from redis.exceptions import ConnectionError as RedisConnectionError
+from docker.errors import ContainerError
from pytest_databases.helpers import get_xdist_worker_num
from pytest_databases.types import ServiceContainer, XdistIsolationLevel
if TYPE_CHECKING:
- from collections.abc import Generator
+ from collections.abc import Generator, Iterator
+
+ from docker.models.containers import Container
from pytest_databases._service import DockerService
+REDIS_PROBE_IMAGE = "redis:latest"
+
+
+def _output_to_bytes(output: bytes | str | Iterator[bytes]) -> bytes:
+ if isinstance(output, bytes):
+ return output
+ if isinstance(output, str):
+ return output.encode()
+ return b"".join(output)
+
+
+def _exec_redis_cli(container: Container, *args: str, db: int = 0) -> tuple[int, bytes]:
+ result = container.exec_run([
+ "redis-cli",
+ "-h",
+ "localhost",
+ "-p",
+ "6379",
+ "-n",
+ str(db),
+ *args,
+ ])
+ return result.exit_code if result.exit_code is not None else -1, _output_to_bytes(result.output)
+
+
+def _probe_redis_endpoint(
+ docker_service: DockerService,
+ service: ServiceContainer,
+ *args: str,
+ db: int = 0,
+ probe_image: str = REDIS_PROBE_IMAGE,
+) -> tuple[int, bytes]:
+ try:
+ output = docker_service._client.containers.run(
+ image=probe_image,
+ command=[
+ "redis-cli",
+ "-h",
+ service.host,
+ "-p",
+ str(service.port),
+ "-n",
+ str(db),
+ *args,
+ ],
+ network_mode="host",
+ remove=True,
+ )
+ except ContainerError as exc:
+ return exc.exit_status, _output_to_bytes(exc.stderr) if exc.stderr else str(exc).encode()
+ return 0, _output_to_bytes(output)
+
+
@dataclasses.dataclass
class RedisService(ServiceContainer):
db: int
@@ -26,16 +80,6 @@ def xdist_redis_isolation_level() -> XdistIsolationLevel:
return "database"
-def redis_responsive(service_container: ServiceContainer) -> bool:
- client = Redis(host=service_container.host, port=service_container.port)
- try:
- return client.ping()
- except (ConnectionError, RedisConnectionError):
- return False
- finally:
- client.close()
-
-
@pytest.fixture(autouse=False, scope="session")
def redis_port(redis_service: RedisService) -> int:
return redis_service.port
@@ -66,9 +110,13 @@ def redis_service(
else:
name += f"_{worker_num + 1}"
+ def _responsive(_service: ServiceContainer) -> bool:
+ exit_code, output = _probe_redis_endpoint(docker_service, _service, "PING")
+ return exit_code == 0 and output.strip().endswith(b"PONG")
+
with docker_service.run(
redis_image,
- check=redis_responsive,
+ check=_responsive,
container_port=6379,
name=name,
transient=xdist_redis_isolation_level == "server",
@@ -101,9 +149,13 @@ def dragonfly_service(
else:
name += f"_{worker_num + 1}"
+ def _responsive(_service: ServiceContainer) -> bool:
+ exit_code, output = _probe_redis_endpoint(docker_service, _service, "PING")
+ return exit_code == 0 and output.strip().endswith(b"PONG")
+
with docker_service.run(
dragonfly_image,
- check=redis_responsive,
+ check=_responsive,
container_port=6379,
name=name,
transient=xdist_redis_isolation_level == "server",
@@ -146,9 +198,13 @@ def keydb_service(
else:
name += f"_{worker_num + 1}"
+ def _responsive(_service: ServiceContainer) -> bool:
+ exit_code, output = _probe_redis_endpoint(docker_service, _service, "PING")
+ return exit_code == 0 and output.strip().endswith(b"PONG")
+
with docker_service.run(
keydb_image,
- check=redis_responsive,
+ check=_responsive,
container_port=6379,
name=name,
transient=xdist_redis_isolation_level == "server",
diff --git a/tests/test_redis.py b/tests/test_redis.py
index 49cc5396..dff60b98 100644
--- a/tests/test_redis.py
+++ b/tests/test_redis.py
@@ -3,6 +3,47 @@
import pytest
+def test_plugin_imports_without_redis(pytester: pytest.Pytester) -> None:
+ pytester.makepyfile("""
+ import builtins
+
+ def test_import() -> None:
+ original_import = builtins.__import__
+
+ def blocked_import(name, globals=None, locals=None, fromlist=(), level=0):
+ if name == "redis" or name.startswith("redis."):
+ raise ModuleNotFoundError(name)
+ return original_import(name, globals, locals, fromlist, level)
+
+ builtins.__import__ = blocked_import
+ try:
+ import pytest_databases.docker.redis
+ finally:
+ builtins.__import__ = original_import
+ """)
+
+ result = pytester.runpytest_subprocess("-p", "pytest_databases", "-vv")
+ result.assert_outcomes(passed=1)
+
+
+REDIS_TEST_HELPERS = """
+def run_redis_cli(docker_service, service, *args, db=None):
+ if db is None:
+ db = service.db
+ output = docker_service._client.containers.run(
+ image="redis:latest",
+ command=["redis-cli", "-h", service.host, "-p", str(service.port), "-n", str(db), *args],
+ network_mode="host",
+ remove=True,
+ )
+ if isinstance(output, bytes):
+ return output.decode().strip()
+ if isinstance(output, str):
+ return output.strip()
+ return b"".join(output).decode().strip()
+"""
+
+
@pytest.fixture(
params=[
pytest.param("redis:latest", id="redis"),
@@ -29,20 +70,19 @@ def redis_compatible_service(request: pytest.FixtureRequest) -> str:
def test_redis_image(pytester: pytest.Pytester, redis_image_name: str) -> None:
pytester.makepyfile(f"""
import pytest
-import redis
from pytest_databases.docker.redis import RedisService
-from pytest_databases.helpers import get_xdist_worker_num
-pytest_plugins = [
- "pytest_databases.docker.redis",
-]
+pytest_plugins = ["pytest_databases.docker.redis"]
+
@pytest.fixture(scope="session")
def redis_image():
return "{redis_image_name}"
-def test_redis_service(redis_service: RedisService) -> None:
- assert redis.Redis(host=redis_service.host, port=redis_service.port).ping()
+{REDIS_TEST_HELPERS}
+
+def test_redis_service(docker_service, redis_service: RedisService) -> None:
+ assert run_redis_cli(docker_service, redis_service, "PING") == "PONG"
""")
result = pytester.runpytest_subprocess("-p", "pytest_databases")
result.assert_outcomes(passed=1)
@@ -50,17 +90,14 @@ def test_redis_service(redis_service: RedisService) -> None:
def test_default_no_xdist(pytester: pytest.Pytester, redis_compatible_service: str) -> None:
pytester.makepyfile(f"""
-import pytest
-import redis
from pytest_databases.docker.redis import RedisService
-from pytest_databases.helpers import get_xdist_worker_num
-pytest_plugins = [
- "pytest_databases.docker.redis",
-]
+pytest_plugins = ["pytest_databases.docker.redis"]
+
+{REDIS_TEST_HELPERS}
-def test_redis_service({redis_compatible_service}: RedisService) -> None:
- assert redis.Redis(host={redis_compatible_service}.host, port={redis_compatible_service}.port).ping()
+def test_redis_service(docker_service, {redis_compatible_service}: RedisService) -> None:
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "PING") == "PONG"
""")
result = pytester.runpytest_subprocess("-p", "pytest_databases")
result.assert_outcomes(passed=1)
@@ -68,38 +105,32 @@ def test_redis_service({redis_compatible_service}: RedisService) -> None:
def test_xdist_isolate_database(pytester: pytest.Pytester, redis_compatible_service: str) -> None:
pytester.makepyfile(f"""
-import pytest
-import redis
from pytest_databases.docker.redis import RedisService
from pytest_databases.helpers import get_xdist_worker_num
-pytest_plugins = [
- "pytest_databases.docker.redis",
-]
+pytest_plugins = ["pytest_databases.docker.redis"]
+
+{REDIS_TEST_HELPERS}
-def test_one({redis_compatible_service}: RedisService) -> None:
- client = redis.Redis(host={redis_compatible_service}.host, port={redis_compatible_service}.port, db={redis_compatible_service}.db)
+def test_one(docker_service, {redis_compatible_service}: RedisService) -> None:
assert {redis_compatible_service}.db == get_xdist_worker_num()
- assert not client.get("one")
- client.set("one", "0")
- assert client.get("one") == b"0"
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "GET", "one") in ("", "(nil)")
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "SET", "one", "0") == "OK"
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "GET", "one") == "0"
-def test_two({redis_compatible_service}: RedisService) -> None:
- client = redis.Redis(host={redis_compatible_service}.host, port={redis_compatible_service}.port, db={redis_compatible_service}.db)
+def test_two(docker_service, {redis_compatible_service}: RedisService) -> None:
assert {redis_compatible_service}.db == get_xdist_worker_num()
- assert not client.get("one")
- client.set("one", "1")
- assert client.get("one") == b"1"
-
-
-def test_use_same_db({redis_compatible_service}: RedisService) -> None:
- client_0 = redis.Redis(host={redis_compatible_service}.host, port={redis_compatible_service}.port, db=0)
- client_1 = redis.Redis(host={redis_compatible_service}.host, port={redis_compatible_service}.port, db=1)
- client_0.set("foo", "0")
- client_1.set("foo", "1")
- assert client_0.get("foo") == b"0"
- assert client_1.get("foo") == b"1"
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "GET", "one") in ("", "(nil)")
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "SET", "one", "1") == "OK"
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "GET", "one") == "1"
+
+
+def test_use_same_db(docker_service, {redis_compatible_service}: RedisService) -> None:
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "SET", "foo", "0", db=0) == "OK"
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "SET", "foo", "1", db=1) == "OK"
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "GET", "foo", db=0) == "0"
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "GET", "foo", db=1) == "1"
""")
result = pytester.runpytest_subprocess("-p", "pytest_databases", "-n", "2")
result.assert_outcomes(passed=3)
@@ -108,31 +139,27 @@ def test_use_same_db({redis_compatible_service}: RedisService) -> None:
def test_xdist_isolate_server(pytester: pytest.Pytester, redis_compatible_service: str) -> None:
pytester.makepyfile(f"""
import pytest
-import redis
from pytest_databases.docker.redis import RedisService
-from pytest_databases.helpers import get_xdist_worker_num
-pytest_plugins = [
- "pytest_databases.docker.redis",
-]
+pytest_plugins = ["pytest_databases.docker.redis"]
+
@pytest.fixture(scope="session")
def xdist_redis_isolation_level():
return "server"
+{REDIS_TEST_HELPERS}
-def test_one({redis_compatible_service}: RedisService) -> None:
- client = redis.Redis(host={redis_compatible_service}.host, port={redis_compatible_service}.port, db={redis_compatible_service}.db)
- assert not client.get("one")
- client.set("one", "1")
+def test_one(docker_service, {redis_compatible_service}: RedisService) -> None:
assert {redis_compatible_service}.db == 0
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "GET", "one") in ("", "(nil)")
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "SET", "one", "1") == "OK"
-def test_two({redis_compatible_service}: RedisService) -> None:
- client = redis.Redis(host={redis_compatible_service}.host, port={redis_compatible_service}.port, db={redis_compatible_service}.db)
- assert not client.get("one")
- client.set("one", "1")
+def test_two(docker_service, {redis_compatible_service}: RedisService) -> None:
assert {redis_compatible_service}.db == 0
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "GET", "one") in ("", "(nil)")
+ assert run_redis_cli(docker_service, {redis_compatible_service}, "SET", "one", "1") == "OK"
""")
result = pytester.runpytest_subprocess("-p", "pytest_databases", "-n", "2")
result.assert_outcomes(passed=2)
diff --git a/uv.lock b/uv.lock
index 3ee06df2..f78475c7 100644
--- a/uv.lock
+++ b/uv.lock
@@ -308,15 +308,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/45/19/cc8bd127d28a43da249aa955cfd164cf8fd534e79e42cea96c4854d72fd0/ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642", size = 1081181, upload-time = "2026-05-17T17:48:28.122Z" },
]
-[[package]]
-name = "async-timeout"
-version = "5.0.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" },
-]
-
[[package]]
name = "auto-pytabs"
version = "0.5.0"
@@ -3772,29 +3763,16 @@ azure-storage = [
bigquery = [
{ name = "google-cloud-bigquery" },
]
-dragonfly = [
- { name = "redis", version = "7.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "redis", version = "7.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
-]
gizmosql = [
{ name = "adbc-driver-flightsql", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "adbc-driver-flightsql", version = "1.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
{ name = "pyarrow", version = "21.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "pyarrow", version = "24.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
]
-keydb = [
- { name = "redis", version = "7.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "redis", version = "7.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
-]
postgres = [
{ name = "psycopg", version = "3.2.13", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "psycopg", version = "3.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
]
-redis = [
- { name = "redis", version = "7.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "redis", version = "7.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
-]
-
[package.dev-dependencies]
build = [
{ name = "bump-my-version" },
@@ -3858,7 +3836,6 @@ dev = [
{ name = "types-pymysql", version = "1.1.0.20260518", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
{ name = "types-pyyaml", version = "6.0.12.20250915", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "types-pyyaml", version = "6.0.12.20260518", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "types-redis" },
{ name = "types-six", version = "1.17.0.20251009", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "types-six", version = "1.17.0.20260518", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
]
@@ -3912,7 +3889,6 @@ lint = [
{ name = "types-pymysql", version = "1.1.0.20260518", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
{ name = "types-pyyaml", version = "6.0.12.20250915", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "types-pyyaml", version = "6.0.12.20260518", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
- { name = "types-redis" },
{ name = "types-six", version = "1.17.0.20251009", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "types-six", version = "1.17.0.20260518", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
]
@@ -3941,9 +3917,6 @@ requires-dist = [
{ name = "psycopg", marker = "extra == 'postgres'", specifier = ">=3" },
{ name = "pyarrow", marker = "extra == 'gizmosql'" },
{ name = "pytest" },
- { name = "redis", marker = "extra == 'dragonfly'" },
- { name = "redis", marker = "extra == 'keydb'" },
- { name = "redis", marker = "extra == 'redis'" },
]
provides-extras = ["azure-storage", "bigquery", "cockroachdb", "dragonfly", "elasticsearch7", "elasticsearch8", "gizmosql", "keydb", "mariadb", "mongodb", "mssql", "mysql", "oracle", "postgres", "redis", "spanner", "valkey", "yugabyte"]
@@ -3986,7 +3959,6 @@ dev = [
{ name = "types-docutils" },
{ name = "types-pymysql" },
{ name = "types-pyyaml" },
- { name = "types-redis" },
{ name = "types-six" },
]
doc = [
@@ -4018,7 +3990,6 @@ lint = [
{ name = "types-docutils" },
{ name = "types-pymysql" },
{ name = "types-pyyaml" },
- { name = "types-redis" },
{ name = "types-six" },
]
test = [
@@ -4242,42 +4213,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl", hash = "sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59", size = 36753, upload-time = "2025-08-28T19:00:19.56Z" },
]
-[[package]]
-name = "redis"
-version = "7.0.1"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version > '3.9' and python_full_version < '3.10'",
- "python_full_version <= '3.9'",
-]
-dependencies = [
- { name = "async-timeout", marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/57/8f/f125feec0b958e8d22c8f0b492b30b1991d9499a4315dfde466cf4289edc/redis-7.0.1.tar.gz", hash = "sha256:c949df947dca995dc68fdf5a7863950bf6df24f8d6022394585acc98e81624f1", size = 4755322, upload-time = "2025-10-27T14:34:00.33Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e9/97/9f22a33c475cda519f20aba6babb340fb2f2254a02fb947816960d1e669a/redis-7.0.1-py3-none-any.whl", hash = "sha256:4977af3c7d67f8f0eb8b6fec0dafc9605db9343142f634041fb0235f67c0588a", size = 339938, upload-time = "2025-10-27T14:33:58.553Z" },
-]
-
-[[package]]
-name = "redis"
-version = "7.4.0"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.15'",
- "python_full_version == '3.14.*'",
- "python_full_version == '3.13.*'",
- "python_full_version == '3.12.*'",
- "python_full_version == '3.11.*'",
- "python_full_version == '3.10.*'",
-]
-dependencies = [
- { name = "async-timeout", marker = "python_full_version >= '3.10' and python_full_version < '3.11.3'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/7b/7f/3759b1d0d72b7c92f0d70ffd9dc962b7b7b5ee74e135f9d7d8ab06b8a318/redis-7.4.0.tar.gz", hash = "sha256:64a6ea7bf567ad43c964d2c30d82853f8df927c5c9017766c55a1d1ed95d18ad", size = 4943913, upload-time = "2026-03-24T09:14:37.53Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/74/3a/95deec7db1eb53979973ebd156f3369a72732208d1391cd2e5d127062a32/redis-7.4.0-py3-none-any.whl", hash = "sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec", size = 409772, upload-time = "2026-03-24T09:14:35.968Z" },
-]
-
[[package]]
name = "requests"
version = "2.32.5"
@@ -5440,42 +5375,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/6a/43/8bd850ee71a191bf072e31302c73a66be413fecdd98fdcd111ecbcce13ca/tomlkit-0.15.0-py3-none-any.whl", hash = "sha256:4dbc8f0fc024412b57ced8757ac7461305126a648ff8c2c807fcb8e133a78738", size = 41328, upload-time = "2026-05-10T07:38:23.517Z" },
]
-[[package]]
-name = "types-cffi"
-version = "1.17.0.20250915"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version > '3.9' and python_full_version < '3.10'",
- "python_full_version <= '3.9'",
-]
-dependencies = [
- { name = "types-setuptools", version = "81.0.0.20260209", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/2a/98/ea454cea03e5f351323af6a482c65924f3c26c515efd9090dede58f2b4b6/types_cffi-1.17.0.20250915.tar.gz", hash = "sha256:4362e20368f78dabd5c56bca8004752cc890e07a71605d9e0d9e069dbaac8c06", size = 17229, upload-time = "2025-09-15T03:01:25.31Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/aa/ec/092f2b74b49ec4855cdb53050deb9699f7105b8fda6fe034c0781b8687f3/types_cffi-1.17.0.20250915-py3-none-any.whl", hash = "sha256:cef4af1116c83359c11bb4269283c50f0688e9fc1d7f0eeb390f3661546da52c", size = 20112, upload-time = "2025-09-15T03:01:24.187Z" },
-]
-
-[[package]]
-name = "types-cffi"
-version = "2.0.0.20260518"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.15'",
- "python_full_version == '3.14.*'",
- "python_full_version == '3.13.*'",
- "python_full_version == '3.12.*'",
- "python_full_version == '3.11.*'",
- "python_full_version == '3.10.*'",
-]
-dependencies = [
- { name = "types-setuptools", version = "82.0.0.20260518", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/bd/0b/b352742758a6054d1053783887bf8cfb739deda1102fda8722294bdc01f7/types_cffi-2.0.0.20260518.tar.gz", hash = "sha256:f9707e66c13454789a58f8843d1ded4a66f1e9c8b10bd24d5eb5e0f25c0c5472", size = 17790, upload-time = "2026-05-18T06:06:50.672Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/68/44/d3b4aafa20a3f76384ba19a513d39272add13746dcfe0409d8d4974fd464/types_cffi-2.0.0.20260518-py3-none-any.whl", hash = "sha256:5b68a215a95d0eac4203b58e766ff7fe40c2e091b1fa1a9e54111f04cc560084", size = 20198, upload-time = "2026-05-18T06:06:49.83Z" },
-]
-
[[package]]
name = "types-click"
version = "7.1.8"
@@ -5575,21 +5474,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b1/5a/db02b5e6633fbe49eaf4e3194bc64ec031e6436a0cfcc610cbda4f1b6a24/types_pymysql-1.1.0.20260518-py3-none-any.whl", hash = "sha256:cf697ce4e44124fc859e8e8a7f047c1dc864745c3c628b85a51b3ee01502ef98", size = 23071, upload-time = "2026-05-18T06:02:30.36Z" },
]
-[[package]]
-name = "types-pyopenssl"
-version = "24.1.0.20240722"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "cryptography", version = "47.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version <= '3.9'" },
- { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version > '3.9'" },
- { name = "types-cffi", version = "1.17.0.20250915", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
- { name = "types-cffi", version = "2.0.0.20260518", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/93/29/47a346550fd2020dac9a7a6d033ea03fccb92fa47c726056618cc889745e/types-pyOpenSSL-24.1.0.20240722.tar.gz", hash = "sha256:47913b4678a01d879f503a12044468221ed8576263c1540dcb0484ca21b08c39", size = 8458, upload-time = "2024-07-22T02:32:22.558Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/98/05/c868a850b6fbb79c26f5f299b768ee0adc1f9816d3461dcf4287916f655b/types_pyOpenSSL-24.1.0.20240722-py3-none-any.whl", hash = "sha256:6a7a5d2ec042537934cfb4c9d4deb0e16c4c6250b09358df1f083682fe6fda54", size = 7499, upload-time = "2024-07-22T02:32:21.232Z" },
-]
-
[[package]]
name = "types-pyyaml"
version = "6.0.12.20250915"
@@ -5620,50 +5504,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/06/a2/c01db32be2ae7d6a1689972f3c492b149ee4e164b12fdfd9f64b50888215/types_pyyaml-6.0.12.20260518-py3-none-any.whl", hash = "sha256:d2150f75a231c9fe9c7463bd29487d93e60bac90400287351384bc2284eba7cd", size = 20312, upload-time = "2026-05-18T06:01:57.368Z" },
]
-[[package]]
-name = "types-redis"
-version = "4.6.0.20241004"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "cryptography", version = "47.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version <= '3.9'" },
- { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version > '3.9'" },
- { name = "types-pyopenssl" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/3a/95/c054d3ac940e8bac4ca216470c80c26688a0e79e09f520a942bb27da3386/types-redis-4.6.0.20241004.tar.gz", hash = "sha256:5f17d2b3f9091ab75384153bfa276619ffa1cf6a38da60e10d5e6749cc5b902e", size = 49679, upload-time = "2024-10-04T02:43:59.224Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/55/82/7d25dce10aad92d2226b269bce2f85cfd843b4477cd50245d7d40ecf8f89/types_redis-4.6.0.20241004-py3-none-any.whl", hash = "sha256:ef5da68cb827e5f606c8f9c0b49eeee4c2669d6d97122f301d3a55dc6a63f6ed", size = 58737, upload-time = "2024-10-04T02:43:57.968Z" },
-]
-
-[[package]]
-name = "types-setuptools"
-version = "81.0.0.20260209"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version > '3.9' and python_full_version < '3.10'",
- "python_full_version <= '3.9'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/9e/57/f1f7992d6d7bded78d1f14dc23d59e87601920852bf10ece2325e49bacae/types_setuptools-81.0.0.20260209.tar.gz", hash = "sha256:2c2eb64499b41b672c387f6f45678a28d20a143a81b45a5c77acbfd4da0df3e1", size = 43201, upload-time = "2026-02-09T04:14:15.505Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/3f/87/90c9143af95850bdaf7eb0d47c59e5c3a8b55fc5a49aca0eb7f98cb964d5/types_setuptools-81.0.0.20260209-py3-none-any.whl", hash = "sha256:4facf71e3f953f8f5ac0020cd6c1b5e493aaff0183e85830bc34870b6abf8475", size = 64194, upload-time = "2026-02-09T04:14:14.278Z" },
-]
-
-[[package]]
-name = "types-setuptools"
-version = "82.0.0.20260518"
-source = { registry = "https://pypi.org/simple" }
-resolution-markers = [
- "python_full_version >= '3.15'",
- "python_full_version == '3.14.*'",
- "python_full_version == '3.13.*'",
- "python_full_version == '3.12.*'",
- "python_full_version == '3.11.*'",
- "python_full_version == '3.10.*'",
-]
-sdist = { url = "https://files.pythonhosted.org/packages/38/bc/73c2c27e047e42f114ac50fb3bdef986c56cbdb68096f8690eeafb839a93/types_setuptools-82.0.0.20260518.tar.gz", hash = "sha256:3b743cfe63d0981ea4c15b90710fc1ed41e3464a537d51e705be514e891c1d07", size = 44999, upload-time = "2026-05-18T06:02:55.642Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/32/8f/d5e2d493f09a7a98c95619edda1cb37cee377626c0a869d53274c26f2858/types_setuptools-82.0.0.20260518-py3-none-any.whl", hash = "sha256:31c04a62b57a653a5021caf191be0f10f70df890f813b51f02bab3969d300f20", size = 68444, upload-time = "2026-05-18T06:02:54.582Z" },
-]
-
[[package]]
name = "types-six"
version = "1.17.0.20251009"