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.
Files changed (44) hide show
  1. encore/__init__.py +43 -0
  2. encore/cron/__init__.py +50 -0
  3. encore/internal/__init__.py +1 -0
  4. encore/internal/_loader.py +79 -0
  5. encore/internal/_native.pyi +734 -0
  6. encore/internal/lazy.py +35 -0
  7. encore/internal/runtime.py +133 -0
  8. encore/internal/startup.py +24 -0
  9. encore/logs/__init__.py +19 -0
  10. encore/logs/_console.py +155 -0
  11. encore/logs/_runtime.py +56 -0
  12. encore/meta/__init__.py +228 -0
  13. encore/metrics/__init__.py +390 -0
  14. encore/pubsub/__init__.py +425 -0
  15. encore/py.typed +0 -0
  16. encore/secrets/__init__.py +139 -0
  17. encore/shutdown/__init__.py +164 -0
  18. encore/storage/__init__.py +5 -0
  19. encore/storage/objects/__init__.py +396 -0
  20. encore/storage/postgres/__init__.py +737 -0
  21. encore/storage/redis/__init__.py +200 -0
  22. encore/storage/redis/_base.py +42 -0
  23. encore/storage/redis/_responses.py +150 -0
  24. encore/storage/redis/_values.py +189 -0
  25. encore/storage/redis/bitmap.py +142 -0
  26. encore/storage/redis/generic.py +367 -0
  27. encore/storage/redis/geo.py +508 -0
  28. encore/storage/redis/hash.py +504 -0
  29. encore/storage/redis/json.py +614 -0
  30. encore/storage/redis/list.py +511 -0
  31. encore/storage/redis/pubsub.py +276 -0
  32. encore/storage/redis/set.py +570 -0
  33. encore/storage/redis/sorted_set.py +753 -0
  34. encore/storage/redis/stream.py +773 -0
  35. encore/storage/redis/string.py +654 -0
  36. encore/tracing/__init__.py +19 -0
  37. encore/tracing/_context.py +27 -0
  38. encore/tracing/_encode.py +351 -0
  39. encore/tracing/_provider.py +62 -0
  40. encore/tracing/_span.py +226 -0
  41. encore/tracing/_tracer.py +93 -0
  42. encore_dev-2.0.0a7.dist-info/METADATA +791 -0
  43. encore_dev-2.0.0a7.dist-info/RECORD +44 -0
  44. 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
+ )