codex-platform 0.1.0__py3-none-any.whl
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.
- codex_platform/__init__.py +1 -0
- codex_platform/notifications/__init__.py +48 -0
- codex_platform/notifications/channels.py +24 -0
- codex_platform/notifications/clients/__init__.py +3 -0
- codex_platform/notifications/clients/smtp.py +134 -0
- codex_platform/notifications/delivery/__init__.py +18 -0
- codex_platform/notifications/delivery/arq.py +69 -0
- codex_platform/notifications/delivery/base.py +39 -0
- codex_platform/notifications/delivery/direct.py +89 -0
- codex_platform/notifications/dto.py +63 -0
- codex_platform/notifications/interfaces.py +30 -0
- codex_platform/notifications/orchestrator.py +116 -0
- codex_platform/notifications/registry.py +71 -0
- codex_platform/notifications/renderer.py +111 -0
- codex_platform/redis_service/__init__.py +59 -0
- codex_platform/redis_service/base.py +69 -0
- codex_platform/redis_service/exceptions.py +20 -0
- codex_platform/redis_service/keys.py +110 -0
- codex_platform/redis_service/managers/__init__.py +18 -0
- codex_platform/redis_service/managers/base_manager.py +31 -0
- codex_platform/redis_service/operations/__init__.py +19 -0
- codex_platform/redis_service/operations/hash.py +302 -0
- codex_platform/redis_service/operations/json_module.py +129 -0
- codex_platform/redis_service/operations/json_string.py +113 -0
- codex_platform/redis_service/operations/list_.py +219 -0
- codex_platform/redis_service/operations/pipeline.py +197 -0
- codex_platform/redis_service/operations/set_.py +212 -0
- codex_platform/redis_service/operations/string.py +318 -0
- codex_platform/redis_service/operations/zset.py +277 -0
- codex_platform/redis_service/service.py +80 -0
- codex_platform/streams/__init__.py +59 -0
- codex_platform/streams/consumer.py +106 -0
- codex_platform/streams/dispatcher.py +175 -0
- codex_platform/streams/processor.py +217 -0
- codex_platform/streams/producer.py +60 -0
- codex_platform/streams/router.py +78 -0
- codex_platform/workers/__init__.py +1 -0
- codex_platform/workers/arq/__init__.py +27 -0
- codex_platform/workers/arq/base.py +170 -0
- codex_platform/workers/arq/config.py +67 -0
- codex_platform/workers/arq/task_utils.py +72 -0
- codex_platform/workers/arq/types.py +11 -0
- codex_platform-0.1.0.dist-info/METADATA +120 -0
- codex_platform-0.1.0.dist-info/RECORD +45 -0
- codex_platform-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"""
|
|
2
|
+
codex_platform.redis_service.operations.string
|
|
3
|
+
===============================================
|
|
4
|
+
Redis String/Key operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from redis.asyncio import Redis
|
|
11
|
+
|
|
12
|
+
from codex_platform.redis_service.base import catch_redis_errors
|
|
13
|
+
from codex_platform.redis_service.keys import BaseRedisKey, resolve_key
|
|
14
|
+
|
|
15
|
+
log = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class StringOperations:
|
|
19
|
+
"""Redis String and key-level operations (SET / GET / EXPIRE / TTL and more).
|
|
20
|
+
|
|
21
|
+
Accepts an already-constructed ``redis.asyncio.Redis`` client.
|
|
22
|
+
All methods wrap Redis errors in typed exceptions.
|
|
23
|
+
|
|
24
|
+
Example::
|
|
25
|
+
|
|
26
|
+
ops = StringOperations(client)
|
|
27
|
+
await ops.set("session:abc", "token-value", ttl=3600)
|
|
28
|
+
token = await ops.get("session:abc")
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, client: Redis) -> None:
|
|
32
|
+
self.client = client
|
|
33
|
+
|
|
34
|
+
@catch_redis_errors
|
|
35
|
+
async def set(self, key: "str | BaseRedisKey", value: str, ttl: int | None = None, **kwargs: Any) -> None:
|
|
36
|
+
"""Set a string value (SET).
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
40
|
+
value: String value to store.
|
|
41
|
+
ttl: Expiry in seconds. ``None`` means no expiry.
|
|
42
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
RedisConnectionError: Redis connection failure.
|
|
46
|
+
RedisServiceError: Redis operation failure.
|
|
47
|
+
"""
|
|
48
|
+
real_key = resolve_key(key, **kwargs)
|
|
49
|
+
await self.client.set(real_key, value, ex=ttl)
|
|
50
|
+
|
|
51
|
+
@catch_redis_errors
|
|
52
|
+
async def setnx(self, key: "str | BaseRedisKey", value: str, ttl: int | None = None, **kwargs: Any) -> bool:
|
|
53
|
+
"""Set a value only if the key does not exist (SET NX).
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
57
|
+
value: String value to store.
|
|
58
|
+
ttl: Expiry in seconds. ``None`` means no expiry.
|
|
59
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
``True`` if the value was set, ``False`` if the key already existed.
|
|
63
|
+
|
|
64
|
+
Raises:
|
|
65
|
+
RedisConnectionError: Redis connection failure.
|
|
66
|
+
RedisServiceError: Redis operation failure.
|
|
67
|
+
"""
|
|
68
|
+
real_key = resolve_key(key, **kwargs)
|
|
69
|
+
return bool(await self.client.set(real_key, value, ex=ttl, nx=True))
|
|
70
|
+
|
|
71
|
+
@catch_redis_errors
|
|
72
|
+
async def get(self, key: "str | BaseRedisKey", **kwargs: Any) -> str | None:
|
|
73
|
+
"""Retrieve a string value (GET).
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
77
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
String value, or ``None`` if the key does not exist.
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
RedisConnectionError: Redis connection failure.
|
|
84
|
+
RedisServiceError: Redis operation failure.
|
|
85
|
+
"""
|
|
86
|
+
real_key = resolve_key(key, **kwargs)
|
|
87
|
+
val = await self.client.get(real_key)
|
|
88
|
+
return str(val) if val is not None else None
|
|
89
|
+
|
|
90
|
+
@catch_redis_errors
|
|
91
|
+
async def getset(self, key: "str | BaseRedisKey", value: str, **kwargs: Any) -> str | None:
|
|
92
|
+
"""Atomically set a new value and return the previous one (GETSET).
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
96
|
+
value: New value to store.
|
|
97
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Previous value, or ``None`` if the key did not exist.
|
|
101
|
+
|
|
102
|
+
Raises:
|
|
103
|
+
RedisConnectionError: Redis connection failure.
|
|
104
|
+
RedisServiceError: Redis operation failure.
|
|
105
|
+
"""
|
|
106
|
+
real_key = resolve_key(key, **kwargs)
|
|
107
|
+
val = await self.client.getset(real_key, value)
|
|
108
|
+
return str(val) if val is not None else None
|
|
109
|
+
|
|
110
|
+
@catch_redis_errors
|
|
111
|
+
async def mget(self, *keys: str) -> list[str | None]:
|
|
112
|
+
"""Retrieve values for multiple keys in a single request (MGET).
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
*keys: Redis key strings.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
List of values in the same order as ``keys``.
|
|
119
|
+
``None`` for keys that do not exist.
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
RedisConnectionError: Redis connection failure.
|
|
123
|
+
RedisServiceError: Redis operation failure.
|
|
124
|
+
"""
|
|
125
|
+
values = await self.client.mget(list(keys))
|
|
126
|
+
return [str(v) if v is not None else None for v in values]
|
|
127
|
+
|
|
128
|
+
@catch_redis_errors
|
|
129
|
+
async def mset(self, mapping: dict[str, str]) -> None:
|
|
130
|
+
"""Set values for multiple keys in a single call (MSET).
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
mapping: Dict of ``{key: value}`` pairs to write.
|
|
134
|
+
|
|
135
|
+
Raises:
|
|
136
|
+
RedisConnectionError: Redis connection failure.
|
|
137
|
+
RedisServiceError: Redis operation failure.
|
|
138
|
+
"""
|
|
139
|
+
await self.client.mset(mapping)
|
|
140
|
+
|
|
141
|
+
@catch_redis_errors
|
|
142
|
+
async def incr(self, key: "str | BaseRedisKey", **kwargs: Any) -> int:
|
|
143
|
+
"""Increment a numeric value by 1 (INCR).
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
147
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
New value after incrementing.
|
|
151
|
+
|
|
152
|
+
Raises:
|
|
153
|
+
RedisConnectionError: Redis connection failure.
|
|
154
|
+
RedisServiceError: Redis operation failure.
|
|
155
|
+
"""
|
|
156
|
+
real_key = resolve_key(key, **kwargs)
|
|
157
|
+
return int(await self.client.incr(real_key))
|
|
158
|
+
|
|
159
|
+
@catch_redis_errors
|
|
160
|
+
async def incrby(self, key: "str | BaseRedisKey", amount: int, **kwargs: Any) -> int:
|
|
161
|
+
"""Increment a numeric value by a given amount (INCRBY).
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
165
|
+
amount: Increment step.
|
|
166
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
New value after incrementing.
|
|
170
|
+
|
|
171
|
+
Raises:
|
|
172
|
+
RedisConnectionError: Redis connection failure.
|
|
173
|
+
RedisServiceError: Redis operation failure.
|
|
174
|
+
"""
|
|
175
|
+
real_key = resolve_key(key, **kwargs)
|
|
176
|
+
return int(await self.client.incrby(real_key, amount))
|
|
177
|
+
|
|
178
|
+
@catch_redis_errors
|
|
179
|
+
async def append(self, key: "str | BaseRedisKey", value: str, **kwargs: Any) -> int:
|
|
180
|
+
"""Append a string to an existing value (APPEND).
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
184
|
+
value: String to append.
|
|
185
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
New total length of the value after appending.
|
|
189
|
+
|
|
190
|
+
Raises:
|
|
191
|
+
RedisConnectionError: Redis connection failure.
|
|
192
|
+
RedisServiceError: Redis operation failure.
|
|
193
|
+
"""
|
|
194
|
+
real_key = resolve_key(key, **kwargs)
|
|
195
|
+
return int(await self.client.append(real_key, value))
|
|
196
|
+
|
|
197
|
+
@catch_redis_errors
|
|
198
|
+
async def delete(self, key: "str | BaseRedisKey", **kwargs: Any) -> None:
|
|
199
|
+
"""Delete a key (DEL).
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
203
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
204
|
+
|
|
205
|
+
Raises:
|
|
206
|
+
RedisConnectionError: Redis connection failure.
|
|
207
|
+
RedisServiceError: Redis operation failure.
|
|
208
|
+
"""
|
|
209
|
+
real_key = resolve_key(key, **kwargs)
|
|
210
|
+
await self.client.delete(real_key)
|
|
211
|
+
|
|
212
|
+
@catch_redis_errors
|
|
213
|
+
async def delete_by_pattern(self, pattern: str) -> int:
|
|
214
|
+
"""Delete all keys matching a glob pattern (SCAN + DEL).
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
pattern: Glob pattern, e.g. ``"session:*"``.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
Number of keys deleted.
|
|
221
|
+
|
|
222
|
+
Raises:
|
|
223
|
+
RedisConnectionError: Redis connection failure.
|
|
224
|
+
RedisServiceError: Redis operation failure.
|
|
225
|
+
"""
|
|
226
|
+
keys = [k async for k in self.client.scan_iter(match=pattern)]
|
|
227
|
+
if not keys:
|
|
228
|
+
return 0
|
|
229
|
+
return int(await self.client.delete(*keys))
|
|
230
|
+
|
|
231
|
+
@catch_redis_errors
|
|
232
|
+
async def expire(self, key: "str | BaseRedisKey", ttl: int, **kwargs: Any) -> bool:
|
|
233
|
+
"""Set a TTL (expiry) on a key in seconds (EXPIRE).
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
237
|
+
ttl: Time-to-live in seconds.
|
|
238
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
``True`` if the TTL was set, ``False`` if the key does not exist.
|
|
242
|
+
|
|
243
|
+
Raises:
|
|
244
|
+
RedisConnectionError: Redis connection failure.
|
|
245
|
+
RedisServiceError: Redis operation failure.
|
|
246
|
+
"""
|
|
247
|
+
real_key = resolve_key(key, **kwargs)
|
|
248
|
+
return bool(await self.client.expire(real_key, ttl))
|
|
249
|
+
|
|
250
|
+
@catch_redis_errors
|
|
251
|
+
async def ttl(self, key: "str | BaseRedisKey", **kwargs: Any) -> int:
|
|
252
|
+
"""Return the remaining TTL of a key in seconds (TTL).
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
256
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
Remaining TTL in seconds. ``-1`` if no TTL is set, ``-2`` if the key does not exist.
|
|
260
|
+
|
|
261
|
+
Raises:
|
|
262
|
+
RedisConnectionError: Redis connection failure.
|
|
263
|
+
RedisServiceError: Redis operation failure.
|
|
264
|
+
"""
|
|
265
|
+
real_key = resolve_key(key, **kwargs)
|
|
266
|
+
return int(await self.client.ttl(real_key))
|
|
267
|
+
|
|
268
|
+
@catch_redis_errors
|
|
269
|
+
async def exists(self, key: "str | BaseRedisKey", **kwargs: Any) -> bool:
|
|
270
|
+
"""Check whether a key exists (EXISTS).
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
274
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
``True`` if the key exists, ``False`` otherwise.
|
|
278
|
+
|
|
279
|
+
Raises:
|
|
280
|
+
RedisConnectionError: Redis connection failure.
|
|
281
|
+
RedisServiceError: Redis operation failure.
|
|
282
|
+
"""
|
|
283
|
+
real_key = resolve_key(key, **kwargs)
|
|
284
|
+
return bool(await self.client.exists(real_key))
|
|
285
|
+
|
|
286
|
+
@catch_redis_errors
|
|
287
|
+
async def rename(self, key: "str | BaseRedisKey", new_key: str, **kwargs: Any) -> None:
|
|
288
|
+
"""Rename a key (RENAME).
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
292
|
+
new_key: Target key name.
|
|
293
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
294
|
+
|
|
295
|
+
Raises:
|
|
296
|
+
RedisConnectionError: Redis connection failure.
|
|
297
|
+
RedisServiceError: Redis operation failure.
|
|
298
|
+
"""
|
|
299
|
+
real_key = resolve_key(key, **kwargs)
|
|
300
|
+
await self.client.rename(real_key, new_key)
|
|
301
|
+
|
|
302
|
+
@catch_redis_errors
|
|
303
|
+
async def key_type(self, key: "str | BaseRedisKey", **kwargs: Any) -> str:
|
|
304
|
+
"""Return the data type stored at a key (TYPE).
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
308
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
One of: ``"string"``, ``"list"``, ``"set"``, ``"zset"``, ``"hash"``, ``"stream"``.
|
|
312
|
+
|
|
313
|
+
Raises:
|
|
314
|
+
RedisConnectionError: Redis connection failure.
|
|
315
|
+
RedisServiceError: Redis operation failure.
|
|
316
|
+
"""
|
|
317
|
+
real_key = resolve_key(key, **kwargs)
|
|
318
|
+
return await self.client.type(real_key)
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""
|
|
2
|
+
codex_platform.redis_service.operations.zset
|
|
3
|
+
=============================================
|
|
4
|
+
Redis Sorted Set (ZSet) operations.
|
|
5
|
+
|
|
6
|
+
A Sorted Set is a set where every member has an associated float score.
|
|
7
|
+
Use it for leaderboards, priority queues, and time-based event indexes.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from redis.asyncio import Redis
|
|
14
|
+
|
|
15
|
+
from codex_platform.redis_service.base import catch_redis_errors
|
|
16
|
+
from codex_platform.redis_service.keys import BaseRedisKey, resolve_key
|
|
17
|
+
|
|
18
|
+
log = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ZSetOperations:
|
|
22
|
+
"""Redis Sorted Set operations (ZADD / ZRANGE / ZRANGEBYSCORE / ZREM and more).
|
|
23
|
+
|
|
24
|
+
Accepts an already-constructed ``redis.asyncio.Redis`` client.
|
|
25
|
+
All methods wrap Redis errors in typed exceptions.
|
|
26
|
+
|
|
27
|
+
Example::
|
|
28
|
+
|
|
29
|
+
ops = ZSetOperations(client)
|
|
30
|
+
await ops.add("leaderboard", {"alice": 1500.0, "bob": 1200.0})
|
|
31
|
+
top = await ops.range("leaderboard", 0, 9, reverse=True, with_scores=True)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, client: Redis) -> None:
|
|
35
|
+
self.client = client
|
|
36
|
+
|
|
37
|
+
@catch_redis_errors
|
|
38
|
+
async def add(self, key: "str | BaseRedisKey", mapping: dict[str, float], **kwargs: Any) -> int:
|
|
39
|
+
"""Add members with scores to a sorted set (ZADD).
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
43
|
+
mapping: Dict of ``{member: score}`` pairs.
|
|
44
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Number of new members added (existing members with updated scores are not counted).
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
RedisConnectionError: Redis connection failure.
|
|
51
|
+
RedisServiceError: Redis operation failure.
|
|
52
|
+
"""
|
|
53
|
+
real_key = resolve_key(key, **kwargs)
|
|
54
|
+
return int(await self.client.zadd(real_key, mapping))
|
|
55
|
+
|
|
56
|
+
@catch_redis_errors
|
|
57
|
+
async def score(self, key: "str | BaseRedisKey", member: str, **kwargs: Any) -> float | None:
|
|
58
|
+
"""Return the score of a member in a sorted set (ZSCORE).
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
62
|
+
member: Member name.
|
|
63
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Float score of the member, or ``None`` if the member does not exist.
|
|
67
|
+
|
|
68
|
+
Raises:
|
|
69
|
+
RedisConnectionError: Redis connection failure.
|
|
70
|
+
RedisServiceError: Redis operation failure.
|
|
71
|
+
"""
|
|
72
|
+
real_key = resolve_key(key, **kwargs)
|
|
73
|
+
val = await self.client.zscore(real_key, member)
|
|
74
|
+
return float(val) if val is not None else None
|
|
75
|
+
|
|
76
|
+
@catch_redis_errors
|
|
77
|
+
async def rank(self, key: "str | BaseRedisKey", member: str, reverse: bool = False, **kwargs: Any) -> int | None:
|
|
78
|
+
"""Return the rank (position) of a member in a sorted set (ZRANK / ZREVRANK).
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
82
|
+
member: Member name.
|
|
83
|
+
reverse: If ``True``, use ZREVRANK (rank by descending score). Defaults to ``False``.
|
|
84
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Zero-based rank of the member, or ``None`` if the member does not exist.
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
RedisConnectionError: Redis connection failure.
|
|
91
|
+
RedisServiceError: Redis operation failure.
|
|
92
|
+
"""
|
|
93
|
+
real_key = resolve_key(key, **kwargs)
|
|
94
|
+
fn = self.client.zrevrank if reverse else self.client.zrank
|
|
95
|
+
result = await fn(real_key, member)
|
|
96
|
+
return int(result) if result is not None else None
|
|
97
|
+
|
|
98
|
+
@catch_redis_errors
|
|
99
|
+
async def range(
|
|
100
|
+
self,
|
|
101
|
+
key: "str | BaseRedisKey",
|
|
102
|
+
start: int = 0,
|
|
103
|
+
end: int = -1,
|
|
104
|
+
reverse: bool = False,
|
|
105
|
+
with_scores: bool = False,
|
|
106
|
+
**kwargs: Any,
|
|
107
|
+
) -> list[str] | list[tuple[str, float]]:
|
|
108
|
+
"""Return a range of members by rank (ZRANGE / ZREVRANGE).
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
112
|
+
start: Start rank (0-based).
|
|
113
|
+
end: End rank inclusive. ``-1`` means the last member.
|
|
114
|
+
reverse: If ``True``, return members in descending score order (ZREVRANGE).
|
|
115
|
+
with_scores: If ``True``, return ``[(member, score), ...]`` tuples.
|
|
116
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
List of member strings, or list of ``(member, score)`` tuples if ``with_scores=True``.
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
RedisConnectionError: Redis connection failure.
|
|
123
|
+
RedisServiceError: Redis operation failure.
|
|
124
|
+
"""
|
|
125
|
+
real_key = resolve_key(key, **kwargs)
|
|
126
|
+
if reverse:
|
|
127
|
+
result = await self.client.zrevrange(real_key, start, end, withscores=with_scores)
|
|
128
|
+
else:
|
|
129
|
+
result = await self.client.zrange(real_key, start, end, withscores=with_scores)
|
|
130
|
+
return result
|
|
131
|
+
|
|
132
|
+
@catch_redis_errors
|
|
133
|
+
async def range_by_score(
|
|
134
|
+
self,
|
|
135
|
+
key: "str | BaseRedisKey",
|
|
136
|
+
min_score: float,
|
|
137
|
+
max_score: float,
|
|
138
|
+
with_scores: bool = False,
|
|
139
|
+
**kwargs: Any,
|
|
140
|
+
) -> list[str] | list[tuple[str, float]]:
|
|
141
|
+
"""Return members within a score range (ZRANGEBYSCORE).
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
145
|
+
min_score: Minimum score (inclusive).
|
|
146
|
+
max_score: Maximum score (inclusive).
|
|
147
|
+
with_scores: If ``True``, return ``[(member, score), ...]`` tuples.
|
|
148
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
List of member strings, or list of ``(member, score)`` tuples if ``with_scores=True``.
|
|
152
|
+
|
|
153
|
+
Raises:
|
|
154
|
+
RedisConnectionError: Redis connection failure.
|
|
155
|
+
RedisServiceError: Redis operation failure.
|
|
156
|
+
"""
|
|
157
|
+
real_key = resolve_key(key, **kwargs)
|
|
158
|
+
return await self.client.zrangebyscore(real_key, min_score, max_score, withscores=with_scores)
|
|
159
|
+
|
|
160
|
+
@catch_redis_errors
|
|
161
|
+
async def remove(self, key: "str | BaseRedisKey", *members: str, **kwargs: Any) -> int:
|
|
162
|
+
"""Remove one or more members from a sorted set (ZREM).
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
166
|
+
*members: Member names to remove.
|
|
167
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Number of members actually removed.
|
|
171
|
+
|
|
172
|
+
Raises:
|
|
173
|
+
RedisConnectionError: Redis connection failure.
|
|
174
|
+
RedisServiceError: Redis operation failure.
|
|
175
|
+
"""
|
|
176
|
+
real_key = resolve_key(key, **kwargs)
|
|
177
|
+
return int(await self.client.zrem(real_key, *members))
|
|
178
|
+
|
|
179
|
+
@catch_redis_errors
|
|
180
|
+
async def card(self, key: "str | BaseRedisKey", **kwargs: Any) -> int:
|
|
181
|
+
"""Return the number of members in a sorted set (ZCARD).
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
185
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Number of members. ``0`` if the key does not exist.
|
|
189
|
+
|
|
190
|
+
Raises:
|
|
191
|
+
RedisConnectionError: Redis connection failure.
|
|
192
|
+
RedisServiceError: Redis operation failure.
|
|
193
|
+
"""
|
|
194
|
+
real_key = resolve_key(key, **kwargs)
|
|
195
|
+
return int(await self.client.zcard(real_key))
|
|
196
|
+
|
|
197
|
+
@catch_redis_errors
|
|
198
|
+
async def increment(self, key: "str | BaseRedisKey", member: str, amount: float = 1.0, **kwargs: Any) -> float:
|
|
199
|
+
"""Increment the score of a member by the given amount (ZINCRBY).
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
203
|
+
member: Member name.
|
|
204
|
+
amount: Increment value. Defaults to ``1.0``.
|
|
205
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
New score of the member after incrementing.
|
|
209
|
+
|
|
210
|
+
Raises:
|
|
211
|
+
RedisConnectionError: Redis connection failure.
|
|
212
|
+
RedisServiceError: Redis operation failure.
|
|
213
|
+
"""
|
|
214
|
+
real_key = resolve_key(key, **kwargs)
|
|
215
|
+
return float(await self.client.zincrby(real_key, amount, member))
|
|
216
|
+
|
|
217
|
+
@catch_redis_errors
|
|
218
|
+
async def count(self, key: "str | BaseRedisKey", min_score: float, max_score: float, **kwargs: Any) -> int:
|
|
219
|
+
"""Count members within a score range (ZCOUNT).
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
223
|
+
min_score: Minimum score (inclusive).
|
|
224
|
+
max_score: Maximum score (inclusive).
|
|
225
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Number of members with score in ``[min_score, max_score]``.
|
|
229
|
+
|
|
230
|
+
Raises:
|
|
231
|
+
RedisConnectionError: Redis connection failure.
|
|
232
|
+
RedisServiceError: Redis operation failure.
|
|
233
|
+
"""
|
|
234
|
+
real_key = resolve_key(key, **kwargs)
|
|
235
|
+
return int(await self.client.zcount(real_key, min_score, max_score))
|
|
236
|
+
|
|
237
|
+
@catch_redis_errors
|
|
238
|
+
async def remove_by_rank(self, key: "str | BaseRedisKey", start: int, end: int, **kwargs: Any) -> int:
|
|
239
|
+
"""Remove members by rank range (ZREMRANGEBYRANK).
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
243
|
+
start: Start rank (inclusive, 0-based).
|
|
244
|
+
end: End rank (inclusive).
|
|
245
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Number of members removed.
|
|
249
|
+
|
|
250
|
+
Raises:
|
|
251
|
+
RedisConnectionError: Redis connection failure.
|
|
252
|
+
RedisServiceError: Redis operation failure.
|
|
253
|
+
"""
|
|
254
|
+
real_key = resolve_key(key, **kwargs)
|
|
255
|
+
return int(await self.client.zremrangebyrank(real_key, start, end))
|
|
256
|
+
|
|
257
|
+
@catch_redis_errors
|
|
258
|
+
async def remove_by_score(
|
|
259
|
+
self, key: "str | BaseRedisKey", min_score: float, max_score: float, **kwargs: Any
|
|
260
|
+
) -> int:
|
|
261
|
+
"""Remove members within a score range (ZREMRANGEBYSCORE).
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
key: Redis key or a ``BaseRedisKey`` instance.
|
|
265
|
+
min_score: Minimum score (inclusive).
|
|
266
|
+
max_score: Maximum score (inclusive).
|
|
267
|
+
**kwargs: Extra parameters forwarded to ``resolve_key``.
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
Number of members removed.
|
|
271
|
+
|
|
272
|
+
Raises:
|
|
273
|
+
RedisConnectionError: Redis connection failure.
|
|
274
|
+
RedisServiceError: Redis operation failure.
|
|
275
|
+
"""
|
|
276
|
+
real_key = resolve_key(key, **kwargs)
|
|
277
|
+
return int(await self.client.zremrangebyscore(real_key, min_score, max_score))
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
codex_platform.redis_service.service
|
|
3
|
+
=====================================
|
|
4
|
+
Full Redis service via composition.
|
|
5
|
+
|
|
6
|
+
Usage::
|
|
7
|
+
|
|
8
|
+
from redis.asyncio import Redis
|
|
9
|
+
from codex_platform.redis_service import RedisService
|
|
10
|
+
|
|
11
|
+
redis_client = Redis(host="localhost", port=6379)
|
|
12
|
+
service = RedisService(redis_client)
|
|
13
|
+
|
|
14
|
+
await service.hash.set_json("u:42", "profile", data)
|
|
15
|
+
await service.string.set("session:abc", "token", ttl=3600)
|
|
16
|
+
|
|
17
|
+
# JSON as plain string (no server modules required):
|
|
18
|
+
await service.json.set("user:42", {"name": "Alice"}, ttl=3600)
|
|
19
|
+
user = await service.json.get("user:42")
|
|
20
|
+
|
|
21
|
+
# JSON via RedisJSON module (requires server module):
|
|
22
|
+
await service.json_module.set("user:42", "$", {"name": "Alice"})
|
|
23
|
+
name = await service.json_module.get("user:42", "$.name")
|
|
24
|
+
|
|
25
|
+
For selective composition use operations directly::
|
|
26
|
+
|
|
27
|
+
from codex_platform.redis_service.operations import HashOperations, StringOperations
|
|
28
|
+
|
|
29
|
+
class MyService:
|
|
30
|
+
def __init__(self, client: Redis):
|
|
31
|
+
self.hash = HashOperations(client)
|
|
32
|
+
self.string = StringOperations(client)
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from redis.asyncio import Redis
|
|
36
|
+
|
|
37
|
+
from .operations import (
|
|
38
|
+
HashOperations,
|
|
39
|
+
JsonModuleOperations,
|
|
40
|
+
JsonStringOperations,
|
|
41
|
+
ListOperations,
|
|
42
|
+
PipelineOperations,
|
|
43
|
+
SetOperations,
|
|
44
|
+
StringOperations,
|
|
45
|
+
ZSetOperations,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class RedisService:
|
|
50
|
+
"""Unified Redis service — all data-type operations in one place.
|
|
51
|
+
|
|
52
|
+
Suitable for projects that need access to multiple Redis data types through
|
|
53
|
+
a single entry point. For selective composition, instantiate individual
|
|
54
|
+
``Operations`` classes directly.
|
|
55
|
+
|
|
56
|
+
Attributes:
|
|
57
|
+
hash: Hash operations (HSET / HGET / HGETALL …).
|
|
58
|
+
string: String and key-level operations (SET / GET / EXPIRE …).
|
|
59
|
+
list: List operations (RPUSH / LPOP / LRANGE …).
|
|
60
|
+
set: Set operations (SADD / SREM / SMEMBERS …).
|
|
61
|
+
zset: Sorted-set operations (ZADD / ZRANGE / ZRANK …).
|
|
62
|
+
json: JSON stored as Redis strings — works with any Redis instance.
|
|
63
|
+
json_module: JSON via the server-side RedisJSON module (requires RedisJSON).
|
|
64
|
+
pipeline: Pipeline / transaction helpers.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(self, client: Redis) -> None:
|
|
68
|
+
"""Initialize the service with an existing async Redis client.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
client: An already-constructed ``redis.asyncio.Redis`` instance.
|
|
72
|
+
"""
|
|
73
|
+
self.hash = HashOperations(client)
|
|
74
|
+
self.string = StringOperations(client)
|
|
75
|
+
self.list = ListOperations(client)
|
|
76
|
+
self.set = SetOperations(client)
|
|
77
|
+
self.zset = ZSetOperations(client)
|
|
78
|
+
self.json = JsonStringOperations(client)
|
|
79
|
+
self.json_module = JsonModuleOperations(client)
|
|
80
|
+
self.pipeline = PipelineOperations(client)
|