redis-client-kit 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/CHANGELOG.md +7 -0
  2. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/PKG-INFO +8 -3
  3. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/README.md +7 -2
  4. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/__init__.py +1 -1
  5. redis_client_kit-0.3.0/redis_client_kit/__version__.py +1 -0
  6. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/config.py +1 -0
  7. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/metrics/__init__.py +2 -2
  8. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/metrics/redis.py +31 -1
  9. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/providers/__init__.py +2 -1
  10. redis_client_kit-0.3.0/redis_client_kit/providers/metrics.py +44 -0
  11. redis_client_kit-0.2.0/redis_client_kit/__version__.py +0 -1
  12. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/.gitignore +0 -0
  13. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/LICENSE +0 -0
  14. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/pyproject.toml +0 -0
  15. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/aio/__init__.py +0 -0
  16. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/aio/factory.py +0 -0
  17. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/aio/instrumented.py +0 -0
  18. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/aio/lifecycle.py +0 -0
  19. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/aio/types.py +0 -0
  20. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/metrics/_deps.py +0 -0
  21. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/protocols.py +0 -0
  22. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/providers/_deps.py +0 -0
  23. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/providers/redis.py +0 -0
  24. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/providers/utils.py +0 -0
  25. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/py.typed +0 -0
  26. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/settings/__init__.py +0 -0
  27. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/settings/_deps.py +0 -0
  28. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/settings/redis.py +0 -0
  29. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/sync/__init__.py +0 -0
  30. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/sync/factory.py +0 -0
  31. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/sync/instrumented.py +0 -0
  32. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/sync/lifecycle.py +0 -0
  33. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/sync/types.py +0 -0
  34. {redis_client_kit-0.2.0 → redis_client_kit-0.3.0}/redis_client_kit/utils.py +0 -0
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0](https://github.com/bedrock-python/redis-client-kit/compare/redis-client-kit-v0.2.0...redis-client-kit-v0.3.0) (2026-09-15)
4
+
5
+
6
+ ### Features
7
+
8
+ * ship PrometheusRedisMetricsProvider and a cached get_redis_metrics ([fbe8feb](https://github.com/bedrock-python/redis-client-kit/commit/fbe8feba0b3041d6af4744482419156e7b1ca8e7))
9
+
3
10
  ## [0.2.0](https://github.com/bedrock-python/redis-client-kit/compare/redis-client-kit-v0.1.5...redis-client-kit-v0.2.0) (2026-09-07)
4
11
 
5
12
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: redis-client-kit
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Redis client with optional Pydantic, Prometheus, and Dishka support
5
5
  Project-URL: Repository, https://github.com/bedrock-python/redis-client-kit
6
6
  Project-URL: Documentation, https://bedrock-python.github.io/redis-client-kit/
@@ -425,14 +425,19 @@ client = create_async_redis_client(settings, metrics=metrics)
425
425
  # - myapp_redis_connection_errors_total{error_type}
426
426
  ```
427
427
 
428
+ Prometheus registers a metric name once per process, so a second `RedisMetrics(prefix="myapp")`
429
+ raises `ValueError`. `get_redis_metrics(prefix="myapp")` returns the one instance per prefix
430
+ instead, which is what a test suite that rebuilds its container per test wants.
431
+
428
432
  ### With Dishka DI
429
433
 
430
434
  ```python
431
435
  from dishka import make_async_container
432
- from redis_client_kit.providers import AsyncRedisProvider
436
+ from redis_client_kit.providers import AsyncRedisProvider, PrometheusRedisMetricsProvider
433
437
 
434
438
  container = make_async_container(
435
- AsyncRedisProvider(),
439
+ AsyncRedisProvider(provide_default_metrics=False),
440
+ PrometheusRedisMetricsProvider(), # RedisMetrics when settings.metrics_enabled, else None
436
441
  SettingsProvider(), # Your settings provider
437
442
  )
438
443
 
@@ -185,14 +185,19 @@ client = create_async_redis_client(settings, metrics=metrics)
185
185
  # - myapp_redis_connection_errors_total{error_type}
186
186
  ```
187
187
 
188
+ Prometheus registers a metric name once per process, so a second `RedisMetrics(prefix="myapp")`
189
+ raises `ValueError`. `get_redis_metrics(prefix="myapp")` returns the one instance per prefix
190
+ instead, which is what a test suite that rebuilds its container per test wants.
191
+
188
192
  ### With Dishka DI
189
193
 
190
194
  ```python
191
195
  from dishka import make_async_container
192
- from redis_client_kit.providers import AsyncRedisProvider
196
+ from redis_client_kit.providers import AsyncRedisProvider, PrometheusRedisMetricsProvider
193
197
 
194
198
  container = make_async_container(
195
- AsyncRedisProvider(),
199
+ AsyncRedisProvider(provide_default_metrics=False),
200
+ PrometheusRedisMetricsProvider(), # RedisMetrics when settings.metrics_enabled, else None
196
201
  SettingsProvider(), # Your settings provider
197
202
  )
198
203
 
@@ -24,7 +24,7 @@ from .utils import build_base_redis_kwargs, build_redis_retry, parse_redis_url_n
24
24
  try:
25
25
  __version__ = version("redis-client-kit")
26
26
  except PackageNotFoundError: # pragma: no cover
27
- __version__ = "0.2.0"
27
+ __version__ = "0.3.0"
28
28
 
29
29
  # ruff: noqa: RUF022
30
30
  __all__ = [
@@ -0,0 +1 @@
1
+ __version__ = "0.3.0"
@@ -89,3 +89,4 @@ class RedisSettingsProtocol(Protocol):
89
89
  ssl: RedisSSLProtocol
90
90
  response: RedisResponseProtocol
91
91
  health_check_interval: int | None
92
+ metrics_enabled: bool
@@ -5,6 +5,6 @@ from ._deps import HAS_PROMETHEUS
5
5
  if not HAS_PROMETHEUS:
6
6
  raise ImportError("prometheus-client not installed. Install redis-client-kit[metrics] to use metrics.")
7
7
 
8
- from .redis import REDIS_COMMAND_DURATION_BUCKETS, RedisMetrics
8
+ from .redis import REDIS_COMMAND_DURATION_BUCKETS, RedisMetrics, get_redis_metrics
9
9
 
10
- __all__ = ["REDIS_COMMAND_DURATION_BUCKETS", "RedisMetrics"]
10
+ __all__ = ["REDIS_COMMAND_DURATION_BUCKETS", "RedisMetrics", "get_redis_metrics"]
@@ -30,6 +30,9 @@ class RedisMetrics:
30
30
  >>> metrics = RedisMetrics(prefix="myapp")
31
31
  >>> metrics.record_command("GET", "success", 0.001)
32
32
  >>> metrics.record_pool_stats(pool_size=10, pool_checked_out=3)
33
+
34
+ Prometheus registers a metric name once per registry, so a second instance with the
35
+ same prefix raises ``ValueError``; ``get_redis_metrics`` hands back the first one instead.
33
36
  """
34
37
 
35
38
  def __init__(self, prefix: str | None = None) -> None:
@@ -37,6 +40,10 @@ class RedisMetrics:
37
40
 
38
41
  Args:
39
42
  prefix: Optional metric name prefix (e.g., "myapp" -> "myapp_redis_pool_size")
43
+
44
+ Raises:
45
+ ValueError: If a metric of the same name is already registered on the default
46
+ registry -- see ``get_redis_metrics`` for the second instance.
40
47
  """
41
48
  metric_prefix = f"{prefix}_" if prefix else ""
42
49
 
@@ -95,4 +102,27 @@ class RedisMetrics:
95
102
  self.pool_checked_out.set(float(pool_checked_out))
96
103
 
97
104
 
98
- __all__ = ["REDIS_COMMAND_DURATION_BUCKETS", "RedisMetrics"]
105
+ _REDIS_METRICS_CACHE: dict[str | None, RedisMetrics] = {}
106
+
107
+
108
+ def get_redis_metrics(prefix: str | None = None) -> RedisMetrics:
109
+ """Get (or lazily create) the cached ``RedisMetrics`` for a prefix, on the default registry.
110
+
111
+ Caching by prefix is what lets a container be rebuilt -- a test suite does it per test --
112
+ without Prometheus refusing the second registration of the same series.
113
+
114
+ Args:
115
+ prefix: The metric name prefix the instance was, or is, created with; ``""`` and
116
+ ``None`` are the same unprefixed instance.
117
+
118
+ Returns:
119
+ The one instance for that prefix.
120
+ """
121
+ key = prefix or None
122
+ metrics = _REDIS_METRICS_CACHE.get(key)
123
+ if metrics is None:
124
+ metrics = _REDIS_METRICS_CACHE[key] = RedisMetrics(prefix=key)
125
+ return metrics
126
+
127
+
128
+ __all__ = ["REDIS_COMMAND_DURATION_BUCKETS", "RedisMetrics", "get_redis_metrics"]
@@ -5,6 +5,7 @@ from ._deps import HAS_DISHKA
5
5
  if not HAS_DISHKA:
6
6
  raise ImportError("dishka not installed. Install redis-client-kit[providers] to use AsyncRedisProvider.")
7
7
 
8
+ from .metrics import PrometheusRedisMetricsProvider
8
9
  from .redis import AsyncRedisProvider
9
10
 
10
- __all__ = ["AsyncRedisProvider"]
11
+ __all__ = ["AsyncRedisProvider", "PrometheusRedisMetricsProvider"]
@@ -0,0 +1,44 @@
1
+ """Dishka provider for the Prometheus Redis metrics collector."""
2
+
3
+ from ..config import RedisSettingsProtocol
4
+ from ..protocols import RedisMetricsProtocol
5
+ from ._deps import Provider, Scope, provide
6
+
7
+
8
+ class PrometheusRedisMetricsProvider(Provider): # type: ignore[misc]
9
+ """Dishka provider for the collector ``AsyncRedisProvider`` records into.
10
+
11
+ Provides ``RedisMetricsProtocol | None`` -- the key ``AsyncRedisProvider`` reads -- as
12
+ ``get_redis_metrics(prefix)`` when ``settings.metrics_enabled`` and ``None`` otherwise,
13
+ so the client is instrumented exactly when the settings say so. Register it with
14
+ ``AsyncRedisProvider(provide_default_metrics=False)``, or after ``AsyncRedisProvider()``,
15
+ since the last provider of a type wins.
16
+
17
+ The collector comes from ``get_redis_metrics``: one instance per prefix on the default
18
+ registry, so a container rebuilt per test never asks Prometheus to register the same
19
+ series twice. Resolving it with metrics on needs the ``metrics`` extra; without it the
20
+ import raises ``ImportError`` naming the extra.
21
+ """
22
+
23
+ scope = Scope.APP # type: ignore[misc]
24
+
25
+ def __init__(self, *, prefix: str | None = None) -> None:
26
+ """Remember the prefix the collector is created with.
27
+
28
+ Args:
29
+ prefix: Metric name prefix (``"myapp"`` gives ``myapp_redis_pool_size``)
30
+ """
31
+ super().__init__()
32
+ self._prefix = prefix
33
+
34
+ @provide
35
+ def get_metrics(self, redis_settings: RedisSettingsProtocol) -> RedisMetricsProtocol | None:
36
+ """Provide the collector when ``metrics_enabled`` is on, ``None`` otherwise."""
37
+ if not redis_settings.metrics_enabled:
38
+ return None
39
+ from ..metrics import get_redis_metrics # noqa: PLC0415 - lazy: needs the [metrics] extra
40
+
41
+ return get_redis_metrics(self._prefix)
42
+
43
+
44
+ __all__ = ["PrometheusRedisMetricsProvider"]
@@ -1 +0,0 @@
1
- __version__ = "0.2.0"