Coverage for faststream / redis / schemas / stream_sub.py: 91%
30 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-08 01:48 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-08 01:48 +0000
1import warnings
2from copy import deepcopy
4from faststream._internal.proto import NameRequired
5from faststream.exceptions import SetupError
8class StreamSub(NameRequired):
9 """A class to represent a Redis Stream subscriber.
11 Args:
12 batch:
13 Whether to consume messages in batches or not.
14 max_records:
15 Number of messages to consume as one batch.
16 consumer:
17 The consumer unique name
19 https://redis.io/docs/latest/develop/tools/insight/tutorials/insight-stream-consumer/#run-the-consumer
20 group:
21 The name of consumer group
22 last_id:
23 An Entry ID, which uses to pick up from where it left off after it is restarted.
24 maxlen:
25 Redis Stream maxlen publish option. Remove eldest message if maxlen exceeded.
27 https://redis.io/docs/latest/develop/data-types/streams/#capped-streams
28 name:
29 The original Redis Stream name.
30 no_ack:
31 If True, to enable the XREADGROUP NOACK subcommand.
33 https://redis.io/docs/latest/commands/xreadgroup/#differences-between-xread-and-xreadgroup
34 polling_interval:
35 Polling interval in milliseconds.
36 min_idle_time:
37 Minimum idle time in milliseconds for a message to be eligible for claiming via XAUTOCLAIM.
38 Messages that have been pending (unacknowledged) for at least this duration can be
39 reclaimed by this consumer. Only applicable when using consumer groups.
41 https://redis.io/docs/latest/commands/xautoclaim/
42 """
44 __slots__ = (
45 "batch",
46 "consumer",
47 "group",
48 "last_id",
49 "max_records",
50 "maxlen",
51 "min_idle_time",
52 "name",
53 "no_ack",
54 "polling_interval",
55 )
57 def __init__(
58 self,
59 stream: str,
60 polling_interval: int | None = None,
61 group: str | None = None,
62 consumer: str | None = None,
63 batch: bool = False,
64 no_ack: bool = False,
65 last_id: str | None = None,
66 maxlen: int | None = None,
67 max_records: int | None = None,
68 min_idle_time: int | None = None,
69 ) -> None:
70 if (group and not consumer) or (not group and consumer):
71 msg = "You should specify `group` and `consumer` both"
72 raise SetupError(msg)
74 if last_id is None:
75 last_id = ">" if group and consumer else "$"
77 if group and consumer:
78 if last_id != ">":
79 if polling_interval: 79 ↛ 80line 79 didn't jump to line 80 because the condition on line 79 was never true
80 warnings.warn(
81 message="`polling_interval` is not supported by consumer group with last_id other than `>`",
82 category=RuntimeWarning,
83 stacklevel=1,
84 )
86 if no_ack:
87 warnings.warn(
88 message="`no_ack` is not supported by consumer group with last_id other than `>`",
89 category=RuntimeWarning,
90 stacklevel=1,
91 )
93 elif no_ack: 93 ↛ 94line 93 didn't jump to line 94 because the condition on line 93 was never true
94 warnings.warn(
95 message="`no_ack` has no effect with consumer group",
96 category=RuntimeWarning,
97 stacklevel=1,
98 )
100 super().__init__(stream)
102 self.group = group
103 self.consumer = consumer
104 self.polling_interval = polling_interval or 100
105 self.batch = batch
106 self.no_ack = no_ack
107 self.last_id = last_id
108 self.maxlen = maxlen
109 self.max_records = max_records
110 self.min_idle_time = min_idle_time
112 def add_prefix(self, prefix: str) -> "StreamSub":
113 new_stream = deepcopy(self)
114 new_stream.name = f"{prefix}{new_stream.name}"
115 return new_stream