encore-dev 2.0.0a7__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.
- encore/__init__.py +43 -0
- encore/cron/__init__.py +50 -0
- encore/internal/__init__.py +1 -0
- encore/internal/_loader.py +79 -0
- encore/internal/_native.pyi +734 -0
- encore/internal/lazy.py +35 -0
- encore/internal/runtime.py +133 -0
- encore/internal/startup.py +24 -0
- encore/logs/__init__.py +19 -0
- encore/logs/_console.py +155 -0
- encore/logs/_runtime.py +56 -0
- encore/meta/__init__.py +228 -0
- encore/metrics/__init__.py +390 -0
- encore/pubsub/__init__.py +425 -0
- encore/py.typed +0 -0
- encore/secrets/__init__.py +139 -0
- encore/shutdown/__init__.py +164 -0
- encore/storage/__init__.py +5 -0
- encore/storage/objects/__init__.py +396 -0
- encore/storage/postgres/__init__.py +737 -0
- encore/storage/redis/__init__.py +200 -0
- encore/storage/redis/_base.py +42 -0
- encore/storage/redis/_responses.py +150 -0
- encore/storage/redis/_values.py +189 -0
- encore/storage/redis/bitmap.py +142 -0
- encore/storage/redis/generic.py +367 -0
- encore/storage/redis/geo.py +508 -0
- encore/storage/redis/hash.py +504 -0
- encore/storage/redis/json.py +614 -0
- encore/storage/redis/list.py +511 -0
- encore/storage/redis/pubsub.py +276 -0
- encore/storage/redis/set.py +570 -0
- encore/storage/redis/sorted_set.py +753 -0
- encore/storage/redis/stream.py +773 -0
- encore/storage/redis/string.py +654 -0
- encore/tracing/__init__.py +19 -0
- encore/tracing/_context.py +27 -0
- encore/tracing/_encode.py +351 -0
- encore/tracing/_provider.py +62 -0
- encore/tracing/_span.py +226 -0
- encore/tracing/_tracer.py +93 -0
- encore_dev-2.0.0a7.dist-info/METADATA +791 -0
- encore_dev-2.0.0a7.dist-info/RECORD +44 -0
- encore_dev-2.0.0a7.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
"""Redis stream commands.
|
|
2
|
+
|
|
3
|
+
Keys are strings without Encore prefixes. Values accept bytes, str, int, float,
|
|
4
|
+
or
|
|
5
|
+
RedisValue implementations; value reads default to bytes. Use command[T](...) to
|
|
6
|
+
decode values as T. Custom encoding and decoding errors propagate to the caller.
|
|
7
|
+
Repeated arguments are variadic. Individual commands document their reply
|
|
8
|
+
shapes and any option-dependent behavior.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Any, Optional
|
|
14
|
+
|
|
15
|
+
from ...tracing._context import active_ctx_bytes
|
|
16
|
+
from ._base import CommandInterface, TypedCommand
|
|
17
|
+
from ._values import Value, encode, T, U
|
|
18
|
+
from . import _responses
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _XinfoStreamCommand(TypedCommand[T]):
|
|
22
|
+
"""A callable XINFO_STREAM command with selectable value decoding."""
|
|
23
|
+
|
|
24
|
+
def __getitem__(self, value_type: type[U]) -> _XinfoStreamCommand[U]:
|
|
25
|
+
"""Select the value type for this command.
|
|
26
|
+
|
|
27
|
+
Use xinfo_stream[T](...) to decode stored values as T. Selecting a type
|
|
28
|
+
does not open a connection or change the decoder of an existing command.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
value_type: bytes, str, int, float, or a class implementing
|
|
32
|
+
RedisValue.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
An independent command with the same arguments and the selected
|
|
36
|
+
decoder. Keys and other command metadata retain their existing
|
|
37
|
+
types.
|
|
38
|
+
"""
|
|
39
|
+
return _XinfoStreamCommand(self._get_handle, value_type)
|
|
40
|
+
|
|
41
|
+
async def __call__(
|
|
42
|
+
self, key: str, *, full: bool = False, count: int | None = None
|
|
43
|
+
) -> Any:
|
|
44
|
+
"""Return stream metadata, optionally including entries and groups.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
key: The database key, without an Encore key prefix.
|
|
48
|
+
full: Whether to include full entry, group, and consumer metadata.
|
|
49
|
+
count: The maximum entries returned with full=True; zero requests
|
|
50
|
+
all. Requires full=True.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Alternating metadata names and values in RESP2, or a dictionary in
|
|
54
|
+
RESP3. Stored values in first-entry, last-entry, and entries are
|
|
55
|
+
decoded using T. Stream IDs, field names, counts, and group metadata
|
|
56
|
+
retain their existing types and structure.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
UnicodeDecodeError: A value is not valid UTF-8 when the type
|
|
60
|
+
argument is str.
|
|
61
|
+
ValueError: A stored value cannot be parsed as int or float.
|
|
62
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
63
|
+
"""
|
|
64
|
+
result = await self._get_handle().stream_xinfo_stream(
|
|
65
|
+
key, full=full, count=count, source=active_ctx_bytes()
|
|
66
|
+
)
|
|
67
|
+
return _responses.stream_info(result, self._value_type)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class _XrangeCommand(TypedCommand[T]):
|
|
71
|
+
"""A callable XRANGE command with selectable value decoding."""
|
|
72
|
+
|
|
73
|
+
def __getitem__(self, value_type: type[U]) -> _XrangeCommand[U]:
|
|
74
|
+
"""Select the value type for this command.
|
|
75
|
+
|
|
76
|
+
Use xrange[T](...) to decode stored values as T. Selecting a type does
|
|
77
|
+
not open a connection or change the decoder of an existing command.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
value_type: bytes, str, int, float, or a class implementing
|
|
81
|
+
RedisValue.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
An independent command with the same arguments and the selected
|
|
85
|
+
decoder. Keys and other command metadata retain their existing
|
|
86
|
+
types.
|
|
87
|
+
"""
|
|
88
|
+
return _XrangeCommand(self._get_handle, value_type)
|
|
89
|
+
|
|
90
|
+
async def __call__(
|
|
91
|
+
self, key: str, start: str, end: str, *, count: int | None = None
|
|
92
|
+
) -> Any:
|
|
93
|
+
"""Return stream entries in ascending ID order.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
key: The database key, without an Encore key prefix.
|
|
97
|
+
start: An inclusive stream ID bound; use "-" or "+" for an unbounded
|
|
98
|
+
end, or prefix an ID with "(" for an exclusive bound.
|
|
99
|
+
end: An inclusive stream ID bound; use "-" or "+" for an unbounded
|
|
100
|
+
end, or prefix an ID with "(" for an exclusive bound.
|
|
101
|
+
count: The maximum number of results, or None for the server
|
|
102
|
+
default.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Rows of [ID bytes, alternating field names and decoded values], or
|
|
106
|
+
an empty list when the range contains no entries.
|
|
107
|
+
|
|
108
|
+
Raises:
|
|
109
|
+
UnicodeDecodeError: A value is not valid UTF-8 when the type
|
|
110
|
+
argument is str.
|
|
111
|
+
ValueError: A stored value cannot be parsed as int or float.
|
|
112
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
113
|
+
"""
|
|
114
|
+
result = await self._get_handle().stream_xrange(
|
|
115
|
+
key, start, end, count=count, source=active_ctx_bytes()
|
|
116
|
+
)
|
|
117
|
+
return _responses.entries(result, self._value_type)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class _XreadCommand(TypedCommand[T]):
|
|
121
|
+
"""A callable XREAD command with selectable value decoding."""
|
|
122
|
+
|
|
123
|
+
def __getitem__(self, value_type: type[U]) -> _XreadCommand[U]:
|
|
124
|
+
"""Select the value type for this command.
|
|
125
|
+
|
|
126
|
+
Use xread[T](...) to decode stored values as T. Selecting a type does
|
|
127
|
+
not open a connection or change the decoder of an existing command.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
value_type: bytes, str, int, float, or a class implementing
|
|
131
|
+
RedisValue.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
An independent command with the same arguments and the selected
|
|
135
|
+
decoder. Keys and other command metadata retain their existing
|
|
136
|
+
types.
|
|
137
|
+
"""
|
|
138
|
+
return _XreadCommand(self._get_handle, value_type)
|
|
139
|
+
|
|
140
|
+
async def __call__(
|
|
141
|
+
self,
|
|
142
|
+
stream: tuple[str, str],
|
|
143
|
+
*streams: tuple[str, str],
|
|
144
|
+
count: int | None = None,
|
|
145
|
+
block_ms: int | None = None,
|
|
146
|
+
) -> Any:
|
|
147
|
+
"""Read stream entries with IDs greater than the supplied IDs.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
stream: The first (stream key, last-seen ID) pair. Use "0-0" to
|
|
151
|
+
read from the beginning or "$" to wait for new entries. For
|
|
152
|
+
subsequent calls, use the last returned IDs.
|
|
153
|
+
*streams: Additional (stream key, last-seen ID) pairs, in order.
|
|
154
|
+
count: The maximum number of entries per stream; None uses server
|
|
155
|
+
behavior.
|
|
156
|
+
block_ms: The time to wait for entries in milliseconds; zero waits
|
|
157
|
+
indefinitely and None does not block.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
In RESP2, rows of [stream key bytes, entries], with each entry
|
|
161
|
+
represented as [ID bytes, alternating field names and decoded
|
|
162
|
+
values]. RESP3 uses a dictionary from stream keys to entries. Stream
|
|
163
|
+
keys exclude Encore prefixes. Returns None if no entries are
|
|
164
|
+
available or the blocking timeout expires.
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
UnicodeDecodeError: A value is not valid UTF-8 when the type
|
|
168
|
+
argument is str.
|
|
169
|
+
ValueError: A stored value cannot be parsed as int or float.
|
|
170
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
171
|
+
"""
|
|
172
|
+
streams = (stream, *streams)
|
|
173
|
+
result = await self._get_handle().stream_xread(
|
|
174
|
+
streams, count=count, block_ms=block_ms, source=active_ctx_bytes()
|
|
175
|
+
)
|
|
176
|
+
return _responses.streams(result, self._value_type)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class _XrevrangeCommand(TypedCommand[T]):
|
|
180
|
+
"""A callable XREVRANGE command with selectable value decoding."""
|
|
181
|
+
|
|
182
|
+
def __getitem__(self, value_type: type[U]) -> _XrevrangeCommand[U]:
|
|
183
|
+
"""Select the value type for this command.
|
|
184
|
+
|
|
185
|
+
Use xrevrange[T](...) to decode stored values as T. Selecting a type
|
|
186
|
+
does not open a connection or change the decoder of an existing command.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
value_type: bytes, str, int, float, or a class implementing
|
|
190
|
+
RedisValue.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
An independent command with the same arguments and the selected
|
|
194
|
+
decoder. Keys and other command metadata retain their existing
|
|
195
|
+
types.
|
|
196
|
+
"""
|
|
197
|
+
return _XrevrangeCommand(self._get_handle, value_type)
|
|
198
|
+
|
|
199
|
+
async def __call__(
|
|
200
|
+
self, key: str, end: str, start: str, *, count: int | None = None
|
|
201
|
+
) -> Any:
|
|
202
|
+
"""Return stream entries in descending ID order.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
key: The database key, without an Encore key prefix.
|
|
206
|
+
end: An inclusive stream ID bound; use "-" or "+" for an unbounded
|
|
207
|
+
end, or prefix an ID with "(" for an exclusive bound.
|
|
208
|
+
start: An inclusive stream ID bound; use "-" or "+" for an unbounded
|
|
209
|
+
end, or prefix an ID with "(" for an exclusive bound.
|
|
210
|
+
count: The maximum number of results, or None for the server
|
|
211
|
+
default.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Rows of [ID bytes, alternating field names and decoded values], or
|
|
215
|
+
an empty list when the range contains no entries.
|
|
216
|
+
|
|
217
|
+
Raises:
|
|
218
|
+
UnicodeDecodeError: A value is not valid UTF-8 when the type
|
|
219
|
+
argument is str.
|
|
220
|
+
ValueError: A stored value cannot be parsed as int or float.
|
|
221
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
222
|
+
"""
|
|
223
|
+
result = await self._get_handle().stream_xrevrange(
|
|
224
|
+
key, end, start, count=count, source=active_ctx_bytes()
|
|
225
|
+
)
|
|
226
|
+
return _responses.entries(result, self._value_type)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class _XautoclaimCommand(TypedCommand[T]):
|
|
230
|
+
"""A callable XAUTOCLAIM command with selectable value decoding."""
|
|
231
|
+
|
|
232
|
+
def __getitem__(self, value_type: type[U]) -> _XautoclaimCommand[U]:
|
|
233
|
+
"""Select the value type for this command.
|
|
234
|
+
|
|
235
|
+
Use xautoclaim[T](...) to decode stored values as T. Selecting a type
|
|
236
|
+
does not open a connection or change the decoder of an existing command.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
value_type: bytes, str, int, float, or a class implementing
|
|
240
|
+
RedisValue.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
An independent command with the same arguments and the selected
|
|
244
|
+
decoder. Keys and other command metadata retain their existing
|
|
245
|
+
types.
|
|
246
|
+
"""
|
|
247
|
+
return _XautoclaimCommand(self._get_handle, value_type)
|
|
248
|
+
|
|
249
|
+
async def __call__(
|
|
250
|
+
self,
|
|
251
|
+
key: str,
|
|
252
|
+
group: str,
|
|
253
|
+
consumer: str,
|
|
254
|
+
min_idle_ms: int,
|
|
255
|
+
start: str,
|
|
256
|
+
*,
|
|
257
|
+
count: int | None = None,
|
|
258
|
+
justid: bool = False,
|
|
259
|
+
) -> Any:
|
|
260
|
+
"""Claim idle pending entries by scanning from a stream ID.
|
|
261
|
+
|
|
262
|
+
Redis executes the command before its result is decoded. A decoding
|
|
263
|
+
error does not undo the command's effects.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
key: The database key, without an Encore key prefix.
|
|
267
|
+
group: The consumer-group name.
|
|
268
|
+
consumer: The consumer name within the group.
|
|
269
|
+
min_idle_ms: The minimum idle time in milliseconds before an entry
|
|
270
|
+
can be claimed.
|
|
271
|
+
start: The scan start ID; use "0-0" initially, then the next ID from
|
|
272
|
+
the previous result.
|
|
273
|
+
count: The maximum number of results, or None for the server
|
|
274
|
+
default.
|
|
275
|
+
justid: Whether to return only entry IDs and avoid incrementing
|
|
276
|
+
delivery counts.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
A list containing the next scan ID and claimed entries, plus deleted
|
|
280
|
+
pending IDs on Redis 7 and later. Entries are [ID bytes, alternating
|
|
281
|
+
field names and decoded values], or just ID bytes with justid=True.
|
|
282
|
+
A next ID of b"0-0" ends the scan.
|
|
283
|
+
|
|
284
|
+
Raises:
|
|
285
|
+
UnicodeDecodeError: A value is not valid UTF-8 when the type
|
|
286
|
+
argument is str.
|
|
287
|
+
ValueError: A stored value cannot be parsed as int or float.
|
|
288
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
289
|
+
"""
|
|
290
|
+
result = await self._get_handle().stream_xautoclaim(
|
|
291
|
+
key,
|
|
292
|
+
group,
|
|
293
|
+
consumer,
|
|
294
|
+
min_idle_ms,
|
|
295
|
+
start,
|
|
296
|
+
count=count,
|
|
297
|
+
justid=justid,
|
|
298
|
+
source=active_ctx_bytes(),
|
|
299
|
+
)
|
|
300
|
+
return _responses.autoclaim(result, self._value_type, justid)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class _XclaimCommand(TypedCommand[T]):
|
|
304
|
+
"""A callable XCLAIM command with selectable value decoding."""
|
|
305
|
+
|
|
306
|
+
def __getitem__(self, value_type: type[U]) -> _XclaimCommand[U]:
|
|
307
|
+
"""Select the value type for this command.
|
|
308
|
+
|
|
309
|
+
Use xclaim[T](...) to decode stored values as T. Selecting a type does
|
|
310
|
+
not open a connection or change the decoder of an existing command.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
value_type: bytes, str, int, float, or a class implementing
|
|
314
|
+
RedisValue.
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
An independent command with the same arguments and the selected
|
|
318
|
+
decoder. Keys and other command metadata retain their existing
|
|
319
|
+
types.
|
|
320
|
+
"""
|
|
321
|
+
return _XclaimCommand(self._get_handle, value_type)
|
|
322
|
+
|
|
323
|
+
async def __call__(
|
|
324
|
+
self,
|
|
325
|
+
key: str,
|
|
326
|
+
group: str,
|
|
327
|
+
consumer: str,
|
|
328
|
+
min_idle_ms: int,
|
|
329
|
+
id: str,
|
|
330
|
+
*ids: str,
|
|
331
|
+
idle_ms: int | None = None,
|
|
332
|
+
time_ms: int | None = None,
|
|
333
|
+
retrycount: int | None = None,
|
|
334
|
+
force: bool = False,
|
|
335
|
+
justid: bool = False,
|
|
336
|
+
) -> Any:
|
|
337
|
+
"""Transfer ownership of pending entries to a consumer.
|
|
338
|
+
|
|
339
|
+
Redis executes the command before its result is decoded. A decoding
|
|
340
|
+
error does not undo the command's effects.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
key: The database key, without an Encore key prefix.
|
|
344
|
+
group: The consumer-group name.
|
|
345
|
+
consumer: The consumer name within the group.
|
|
346
|
+
min_idle_ms: The minimum idle time in milliseconds before an entry
|
|
347
|
+
can be claimed.
|
|
348
|
+
id: The first stream entry ID.
|
|
349
|
+
*ids: Additional stream entry IDs, in order.
|
|
350
|
+
idle_ms: The idle time to assign in milliseconds; incompatible with
|
|
351
|
+
time_ms.
|
|
352
|
+
time_ms: The last-delivery Unix timestamp in milliseconds;
|
|
353
|
+
incompatible with idle_ms.
|
|
354
|
+
retrycount: The delivery count to assign, or None to use Redis
|
|
355
|
+
behavior.
|
|
356
|
+
force: Whether to create pending records for existing stream entries
|
|
357
|
+
that are not already pending.
|
|
358
|
+
justid: Whether to return only entry IDs and avoid incrementing
|
|
359
|
+
delivery counts.
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
Claimed [ID bytes, alternating field names and decoded values]
|
|
363
|
+
entries, or ID bytes with justid=True. Entries that cannot be
|
|
364
|
+
claimed are omitted.
|
|
365
|
+
|
|
366
|
+
Raises:
|
|
367
|
+
UnicodeDecodeError: A value is not valid UTF-8 when the type
|
|
368
|
+
argument is str.
|
|
369
|
+
ValueError: A stored value cannot be parsed as int or float.
|
|
370
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
371
|
+
"""
|
|
372
|
+
ids = (id, *ids)
|
|
373
|
+
result = await self._get_handle().stream_xclaim(
|
|
374
|
+
key,
|
|
375
|
+
group,
|
|
376
|
+
consumer,
|
|
377
|
+
min_idle_ms,
|
|
378
|
+
ids,
|
|
379
|
+
idle_ms=idle_ms,
|
|
380
|
+
time_ms=time_ms,
|
|
381
|
+
retrycount=retrycount,
|
|
382
|
+
force=force,
|
|
383
|
+
justid=justid,
|
|
384
|
+
source=active_ctx_bytes(),
|
|
385
|
+
)
|
|
386
|
+
return result if justid else _responses.entries(result, self._value_type)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class _XreadgroupCommand(TypedCommand[T]):
|
|
390
|
+
"""A callable XREADGROUP command with selectable value decoding."""
|
|
391
|
+
|
|
392
|
+
def __getitem__(self, value_type: type[U]) -> _XreadgroupCommand[U]:
|
|
393
|
+
"""Select the value type for this command.
|
|
394
|
+
|
|
395
|
+
Use xreadgroup[T](...) to decode stored values as T. Selecting a type
|
|
396
|
+
does not open a connection or change the decoder of an existing command.
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
value_type: bytes, str, int, float, or a class implementing
|
|
400
|
+
RedisValue.
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
An independent command with the same arguments and the selected
|
|
404
|
+
decoder. Keys and other command metadata retain their existing
|
|
405
|
+
types.
|
|
406
|
+
"""
|
|
407
|
+
return _XreadgroupCommand(self._get_handle, value_type)
|
|
408
|
+
|
|
409
|
+
async def __call__(
|
|
410
|
+
self,
|
|
411
|
+
group: str,
|
|
412
|
+
consumer: str,
|
|
413
|
+
stream: tuple[str, str],
|
|
414
|
+
*streams: tuple[str, str],
|
|
415
|
+
count: int | None = None,
|
|
416
|
+
block_ms: int | None = None,
|
|
417
|
+
noack: bool = False,
|
|
418
|
+
) -> Any:
|
|
419
|
+
"""Read stream entries as a consumer-group member.
|
|
420
|
+
|
|
421
|
+
Reading new entries advances the group and normally records pending
|
|
422
|
+
entries. Acknowledge processed entries with xack unless noack=True. When
|
|
423
|
+
reading pending entries, Redis ignores block_ms and noack.
|
|
424
|
+
|
|
425
|
+
Redis executes the command before its result is decoded. A decoding
|
|
426
|
+
error does not undo the command's effects.
|
|
427
|
+
|
|
428
|
+
Args:
|
|
429
|
+
group: The consumer-group name.
|
|
430
|
+
consumer: The consumer name within the group.
|
|
431
|
+
stream: The first (stream key, ID) pair. Use ">" for never-delivered
|
|
432
|
+
entries, or an ID such as "0-0" to read this consumer's pending
|
|
433
|
+
entries after that ID.
|
|
434
|
+
*streams: Additional (stream key, ID) pairs, in order.
|
|
435
|
+
count: The maximum number of entries per stream; None uses server
|
|
436
|
+
behavior.
|
|
437
|
+
block_ms: The time to wait for entries in milliseconds; zero waits
|
|
438
|
+
indefinitely and None does not block.
|
|
439
|
+
noack: Whether to skip pending-entry tracking for new messages; they
|
|
440
|
+
will not require xack.
|
|
441
|
+
|
|
442
|
+
Returns:
|
|
443
|
+
In RESP2, rows of [stream key bytes, entries], with entries
|
|
444
|
+
represented as [ID bytes, alternating field names and decoded
|
|
445
|
+
values]. RESP3 uses a dictionary from stream keys to entries.
|
|
446
|
+
Deleted pending entries can have None in place of their fields.
|
|
447
|
+
Stream keys exclude Encore prefixes. Returns None if no entries are
|
|
448
|
+
available or the blocking timeout expires.
|
|
449
|
+
|
|
450
|
+
Raises:
|
|
451
|
+
UnicodeDecodeError: A value is not valid UTF-8 when the type
|
|
452
|
+
argument is str.
|
|
453
|
+
ValueError: A stored value cannot be parsed as int or float.
|
|
454
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
455
|
+
"""
|
|
456
|
+
streams = (stream, *streams)
|
|
457
|
+
result = await self._get_handle().stream_xreadgroup(
|
|
458
|
+
group,
|
|
459
|
+
consumer,
|
|
460
|
+
streams,
|
|
461
|
+
count=count,
|
|
462
|
+
block_ms=block_ms,
|
|
463
|
+
noack=noack,
|
|
464
|
+
source=active_ctx_bytes(),
|
|
465
|
+
)
|
|
466
|
+
return _responses.streams(result, self._value_type)
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
class ReadOnlyInterface(CommandInterface):
|
|
470
|
+
"""Commands available through a read-only database handle."""
|
|
471
|
+
|
|
472
|
+
@property
|
|
473
|
+
def xinfo_stream(self) -> _XinfoStreamCommand[bytes]:
|
|
474
|
+
"""The callable XINFO_STREAM command.
|
|
475
|
+
|
|
476
|
+
Use xinfo_stream[T](...) to select the decoded value type. Plain calls
|
|
477
|
+
keep stored values as bytes; keys and command metadata are unchanged.
|
|
478
|
+
"""
|
|
479
|
+
return _XinfoStreamCommand(self._get_handle, bytes)
|
|
480
|
+
|
|
481
|
+
async def xlen(self, key: str) -> int:
|
|
482
|
+
"""Return the number of entries in a stream.
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
key: The database key, without an Encore key prefix.
|
|
486
|
+
|
|
487
|
+
Returns:
|
|
488
|
+
The entry count, or zero if the key is missing.
|
|
489
|
+
|
|
490
|
+
Raises:
|
|
491
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
492
|
+
"""
|
|
493
|
+
return await self._get_handle().stream_xlen(key, source=active_ctx_bytes())
|
|
494
|
+
|
|
495
|
+
async def xpending(
|
|
496
|
+
self,
|
|
497
|
+
key: str,
|
|
498
|
+
group: str,
|
|
499
|
+
*,
|
|
500
|
+
start: str | None = None,
|
|
501
|
+
end: str | None = None,
|
|
502
|
+
count: int | None = None,
|
|
503
|
+
consumer: str | None = None,
|
|
504
|
+
idle_ms: int | None = None,
|
|
505
|
+
) -> Any:
|
|
506
|
+
"""Inspect entries delivered to a group but not yet acknowledged.
|
|
507
|
+
|
|
508
|
+
Args:
|
|
509
|
+
key: The database key, without an Encore key prefix.
|
|
510
|
+
group: The consumer-group name.
|
|
511
|
+
start: An inclusive stream ID bound; use "-" or "+" for an unbounded
|
|
512
|
+
end, or prefix an ID with "(" for an exclusive bound.
|
|
513
|
+
end: An inclusive stream ID bound; use "-" or "+" for an unbounded
|
|
514
|
+
end, or prefix an ID with "(" for an exclusive bound.
|
|
515
|
+
count: The maximum entries to return; supply together with start and
|
|
516
|
+
end.
|
|
517
|
+
consumer: Only include pending entries owned by this consumer;
|
|
518
|
+
requires start, end, and count.
|
|
519
|
+
idle_ms: Only include entries idle for at least this many
|
|
520
|
+
milliseconds; requires start, end, and count.
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
Without a range, [total, smallest ID, largest ID, consumer counts].
|
|
524
|
+
With a range, rows of [ID, consumer, idle milliseconds, delivery
|
|
525
|
+
count]. IDs and consumer names are bytes; empty-summary ID fields
|
|
526
|
+
are None.
|
|
527
|
+
|
|
528
|
+
Raises:
|
|
529
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
530
|
+
"""
|
|
531
|
+
return await self._get_handle().stream_xpending(
|
|
532
|
+
key,
|
|
533
|
+
group,
|
|
534
|
+
start=start,
|
|
535
|
+
end=end,
|
|
536
|
+
count=count,
|
|
537
|
+
consumer=consumer,
|
|
538
|
+
idle_ms=idle_ms,
|
|
539
|
+
source=active_ctx_bytes(),
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
@property
|
|
543
|
+
def xrange(self) -> _XrangeCommand[bytes]:
|
|
544
|
+
"""The callable XRANGE command.
|
|
545
|
+
|
|
546
|
+
Use xrange[T](...) to select the decoded value type. Plain calls keep
|
|
547
|
+
stored values as bytes; keys and command metadata are unchanged.
|
|
548
|
+
"""
|
|
549
|
+
return _XrangeCommand(self._get_handle, bytes)
|
|
550
|
+
|
|
551
|
+
@property
|
|
552
|
+
def xread(self) -> _XreadCommand[bytes]:
|
|
553
|
+
"""The callable XREAD command.
|
|
554
|
+
|
|
555
|
+
Use xread[T](...) to select the decoded value type. Plain calls keep
|
|
556
|
+
stored values as bytes; keys and command metadata are unchanged.
|
|
557
|
+
"""
|
|
558
|
+
return _XreadCommand(self._get_handle, bytes)
|
|
559
|
+
|
|
560
|
+
@property
|
|
561
|
+
def xrevrange(self) -> _XrevrangeCommand[bytes]:
|
|
562
|
+
"""The callable XREVRANGE command.
|
|
563
|
+
|
|
564
|
+
Use xrevrange[T](...) to select the decoded value type. Plain calls keep
|
|
565
|
+
stored values as bytes; keys and command metadata are unchanged.
|
|
566
|
+
"""
|
|
567
|
+
return _XrevrangeCommand(self._get_handle, bytes)
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
class Interface(ReadOnlyInterface):
|
|
571
|
+
"""Read and write commands."""
|
|
572
|
+
|
|
573
|
+
async def xack(self, key: str, group: str, id: str, *ids: str) -> int:
|
|
574
|
+
"""Acknowledge pending stream entries for a consumer group.
|
|
575
|
+
|
|
576
|
+
Args:
|
|
577
|
+
key: The database key, without an Encore key prefix.
|
|
578
|
+
group: The consumer-group name.
|
|
579
|
+
id: The first stream entry ID.
|
|
580
|
+
*ids: Additional stream entry IDs, in order.
|
|
581
|
+
|
|
582
|
+
Returns:
|
|
583
|
+
The number of entries removed from the group's pending list. Entries
|
|
584
|
+
remain in the stream.
|
|
585
|
+
|
|
586
|
+
Raises:
|
|
587
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
588
|
+
"""
|
|
589
|
+
ids = (id, *ids)
|
|
590
|
+
return await self._get_handle().stream_xack(
|
|
591
|
+
key, group, ids, source=active_ctx_bytes()
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
async def xadd(
|
|
595
|
+
self,
|
|
596
|
+
key: str,
|
|
597
|
+
item: tuple[str, Value],
|
|
598
|
+
*items: tuple[str, Value],
|
|
599
|
+
id: str = "*",
|
|
600
|
+
nomkstream: bool = False,
|
|
601
|
+
maxlen: int | None = None,
|
|
602
|
+
minid: str | None = None,
|
|
603
|
+
approximate: bool = False,
|
|
604
|
+
limit: int | None = None,
|
|
605
|
+
) -> Optional[bytes]:
|
|
606
|
+
"""Append an entry to a stream and optionally trim older entries.
|
|
607
|
+
|
|
608
|
+
Args:
|
|
609
|
+
key: The database key, without an Encore key prefix.
|
|
610
|
+
item: The first (field name, value) pair.
|
|
611
|
+
*items: Additional tuples of the same shape, in order.
|
|
612
|
+
id: The new entry ID; "*" lets Redis generate one.
|
|
613
|
+
nomkstream: Whether to skip insertion when the stream does not
|
|
614
|
+
exist.
|
|
615
|
+
maxlen: The target maximum stream length; incompatible with minid.
|
|
616
|
+
minid: Trim entries with IDs below this threshold; incompatible with
|
|
617
|
+
maxlen.
|
|
618
|
+
approximate: Whether to trim in efficient chunks, potentially
|
|
619
|
+
retaining extra entries. Requires maxlen or minid.
|
|
620
|
+
limit: The maximum entries to examine while trimming; zero means
|
|
621
|
+
unlimited. Requires approximate=True and a trimming threshold.
|
|
622
|
+
|
|
623
|
+
Returns:
|
|
624
|
+
The new entry ID as bytes, or None if nomkstream=True and the stream
|
|
625
|
+
does not exist.
|
|
626
|
+
|
|
627
|
+
Raises:
|
|
628
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
629
|
+
"""
|
|
630
|
+
items = (item, *items)
|
|
631
|
+
return await self._get_handle().stream_xadd(
|
|
632
|
+
key,
|
|
633
|
+
[(field, encode(value)) for field, value in items],
|
|
634
|
+
id=id,
|
|
635
|
+
nomkstream=nomkstream,
|
|
636
|
+
maxlen=maxlen,
|
|
637
|
+
minid=minid,
|
|
638
|
+
approximate=approximate,
|
|
639
|
+
limit=limit,
|
|
640
|
+
source=active_ctx_bytes(),
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
@property
|
|
644
|
+
def xautoclaim(self) -> _XautoclaimCommand[bytes]:
|
|
645
|
+
"""The callable XAUTOCLAIM command.
|
|
646
|
+
|
|
647
|
+
Use xautoclaim[T](...) to select the decoded value type. Plain calls
|
|
648
|
+
keep stored values as bytes; keys and command metadata are unchanged.
|
|
649
|
+
"""
|
|
650
|
+
return _XautoclaimCommand(self._get_handle, bytes)
|
|
651
|
+
|
|
652
|
+
@property
|
|
653
|
+
def xclaim(self) -> _XclaimCommand[bytes]:
|
|
654
|
+
"""The callable XCLAIM command.
|
|
655
|
+
|
|
656
|
+
Use xclaim[T](...) to select the decoded value type. Plain calls keep
|
|
657
|
+
stored values as bytes; keys and command metadata are unchanged.
|
|
658
|
+
"""
|
|
659
|
+
return _XclaimCommand(self._get_handle, bytes)
|
|
660
|
+
|
|
661
|
+
async def xdel(self, key: str, id: str, *ids: str) -> int:
|
|
662
|
+
"""Delete entries from a stream by ID.
|
|
663
|
+
|
|
664
|
+
Args:
|
|
665
|
+
key: The database key, without an Encore key prefix.
|
|
666
|
+
id: The first stream entry ID.
|
|
667
|
+
*ids: Additional stream entry IDs, in order.
|
|
668
|
+
|
|
669
|
+
Returns:
|
|
670
|
+
The number of entries deleted; missing IDs are ignored.
|
|
671
|
+
|
|
672
|
+
Raises:
|
|
673
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
674
|
+
"""
|
|
675
|
+
ids = (id, *ids)
|
|
676
|
+
return await self._get_handle().stream_xdel(key, ids, source=active_ctx_bytes())
|
|
677
|
+
|
|
678
|
+
async def xgroup_create(
|
|
679
|
+
self,
|
|
680
|
+
key: str,
|
|
681
|
+
group: str,
|
|
682
|
+
*,
|
|
683
|
+
id: str = "$",
|
|
684
|
+
mkstream: bool = False,
|
|
685
|
+
entries_read: int | None = None,
|
|
686
|
+
) -> None:
|
|
687
|
+
"""Create a consumer group on a stream.
|
|
688
|
+
|
|
689
|
+
Args:
|
|
690
|
+
key: The database key, without an Encore key prefix.
|
|
691
|
+
group: The consumer-group name.
|
|
692
|
+
id: The last-delivered ID for the group. "$" starts after existing
|
|
693
|
+
entries; "0-0" includes existing entries.
|
|
694
|
+
mkstream: Whether to create an empty stream if it does not exist.
|
|
695
|
+
entries_read: The group's logical read counter for lag tracking, or
|
|
696
|
+
None to let Redis choose it.
|
|
697
|
+
|
|
698
|
+
Raises:
|
|
699
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
700
|
+
"""
|
|
701
|
+
return await self._get_handle().stream_xgroup_create(
|
|
702
|
+
key,
|
|
703
|
+
group,
|
|
704
|
+
id=id,
|
|
705
|
+
mkstream=mkstream,
|
|
706
|
+
entries_read=entries_read,
|
|
707
|
+
source=active_ctx_bytes(),
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
async def xgroup_destroy(self, key: str, group: str) -> bool:
|
|
711
|
+
"""Delete a consumer group and its pending-entry tracking.
|
|
712
|
+
|
|
713
|
+
Args:
|
|
714
|
+
key: The database key, without an Encore key prefix.
|
|
715
|
+
group: The consumer-group name.
|
|
716
|
+
|
|
717
|
+
Returns:
|
|
718
|
+
True if the group existed and was removed, otherwise False. The
|
|
719
|
+
stream's entries are retained.
|
|
720
|
+
|
|
721
|
+
Raises:
|
|
722
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
723
|
+
"""
|
|
724
|
+
return await self._get_handle().stream_xgroup_destroy(
|
|
725
|
+
key, group, source=active_ctx_bytes()
|
|
726
|
+
)
|
|
727
|
+
|
|
728
|
+
@property
|
|
729
|
+
def xreadgroup(self) -> _XreadgroupCommand[bytes]:
|
|
730
|
+
"""The callable XREADGROUP command.
|
|
731
|
+
|
|
732
|
+
Use xreadgroup[T](...) to select the decoded value type. Plain calls
|
|
733
|
+
keep stored values as bytes; keys and command metadata are unchanged.
|
|
734
|
+
"""
|
|
735
|
+
return _XreadgroupCommand(self._get_handle, bytes)
|
|
736
|
+
|
|
737
|
+
async def xtrim(
|
|
738
|
+
self,
|
|
739
|
+
key: str,
|
|
740
|
+
*,
|
|
741
|
+
maxlen: int | None = None,
|
|
742
|
+
minid: str | None = None,
|
|
743
|
+
approximate: bool = False,
|
|
744
|
+
limit: int | None = None,
|
|
745
|
+
) -> int:
|
|
746
|
+
"""Remove stream entries older than a trimming threshold.
|
|
747
|
+
|
|
748
|
+
Supply exactly one of maxlen and minid.
|
|
749
|
+
|
|
750
|
+
Args:
|
|
751
|
+
key: The database key, without an Encore key prefix.
|
|
752
|
+
maxlen: The target maximum stream length; incompatible with minid.
|
|
753
|
+
minid: Trim entries with IDs below this threshold; incompatible with
|
|
754
|
+
maxlen.
|
|
755
|
+
approximate: Whether to trim in efficient chunks, potentially
|
|
756
|
+
retaining extra entries. Requires maxlen or minid.
|
|
757
|
+
limit: The maximum entries to examine while trimming; zero means
|
|
758
|
+
unlimited. Requires approximate=True and a trimming threshold.
|
|
759
|
+
|
|
760
|
+
Returns:
|
|
761
|
+
The number of entries removed.
|
|
762
|
+
|
|
763
|
+
Raises:
|
|
764
|
+
EncoreError: Redis rejects the command or the request fails.
|
|
765
|
+
"""
|
|
766
|
+
return await self._get_handle().stream_xtrim(
|
|
767
|
+
key,
|
|
768
|
+
maxlen=maxlen,
|
|
769
|
+
minid=minid,
|
|
770
|
+
approximate=approximate,
|
|
771
|
+
limit=limit,
|
|
772
|
+
source=active_ctx_bytes(),
|
|
773
|
+
)
|