edda-framework 0.13.0__py3-none-any.whl → 0.14.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.
- edda/channels.py +27 -0
- edda/storage/protocol.py +12 -0
- edda/storage/sqlalchemy_storage.py +20 -0
- {edda_framework-0.13.0.dist-info → edda_framework-0.14.0.dist-info}/METADATA +1 -1
- {edda_framework-0.13.0.dist-info → edda_framework-0.14.0.dist-info}/RECORD +8 -8
- {edda_framework-0.13.0.dist-info → edda_framework-0.14.0.dist-info}/WHEEL +0 -0
- {edda_framework-0.13.0.dist-info → edda_framework-0.14.0.dist-info}/entry_points.txt +0 -0
- {edda_framework-0.13.0.dist-info → edda_framework-0.14.0.dist-info}/licenses/LICENSE +0 -0
edda/channels.py
CHANGED
|
@@ -129,6 +129,24 @@ class WaitForTimerException(Exception):
|
|
|
129
129
|
super().__init__(f"Waiting for timer: {timer_id}")
|
|
130
130
|
|
|
131
131
|
|
|
132
|
+
class ChannelModeConflictError(Exception):
|
|
133
|
+
"""
|
|
134
|
+
Raised when subscribing with a different mode than the channel's established mode.
|
|
135
|
+
|
|
136
|
+
A channel's mode is locked when the first subscription is created. Subsequent
|
|
137
|
+
subscriptions must use the same mode.
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
def __init__(self, channel: str, existing_mode: str, requested_mode: str) -> None:
|
|
141
|
+
self.channel = channel
|
|
142
|
+
self.existing_mode = existing_mode
|
|
143
|
+
self.requested_mode = requested_mode
|
|
144
|
+
super().__init__(
|
|
145
|
+
f"Channel '{channel}' is already configured as '{existing_mode}' mode. "
|
|
146
|
+
f"Cannot subscribe with '{requested_mode}' mode."
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
132
150
|
# =============================================================================
|
|
133
151
|
# Subscription Functions
|
|
134
152
|
# =============================================================================
|
|
@@ -150,6 +168,10 @@ async def subscribe(
|
|
|
150
168
|
- "competing": Each message goes to only one subscriber (work queue pattern)
|
|
151
169
|
- "direct": Receive messages sent via send_to() to this instance
|
|
152
170
|
|
|
171
|
+
Raises:
|
|
172
|
+
ChannelModeConflictError: If the channel is already configured with a different mode
|
|
173
|
+
ValueError: If mode is not 'broadcast', 'competing', or 'direct'
|
|
174
|
+
|
|
153
175
|
The "direct" mode is syntactic sugar that subscribes to "channel:instance_id" internally,
|
|
154
176
|
allowing simpler code when receiving direct messages:
|
|
155
177
|
|
|
@@ -204,6 +226,11 @@ async def subscribe(
|
|
|
204
226
|
f"Invalid subscription mode: {mode}. Must be 'broadcast', 'competing', or 'direct'"
|
|
205
227
|
)
|
|
206
228
|
|
|
229
|
+
# Check for mode conflict
|
|
230
|
+
existing_mode = await ctx.storage.get_channel_mode(actual_channel)
|
|
231
|
+
if existing_mode is not None and existing_mode != actual_mode:
|
|
232
|
+
raise ChannelModeConflictError(channel, existing_mode, mode)
|
|
233
|
+
|
|
207
234
|
await ctx.storage.subscribe_to_channel(ctx.instance_id, actual_channel, actual_mode)
|
|
208
235
|
|
|
209
236
|
|
edda/storage/protocol.py
CHANGED
|
@@ -990,6 +990,18 @@ class StorageProtocol(Protocol):
|
|
|
990
990
|
"""
|
|
991
991
|
...
|
|
992
992
|
|
|
993
|
+
async def get_channel_mode(self, channel: str) -> str | None:
|
|
994
|
+
"""
|
|
995
|
+
Get the mode for a channel (from any existing subscription).
|
|
996
|
+
|
|
997
|
+
Args:
|
|
998
|
+
channel: Channel name
|
|
999
|
+
|
|
1000
|
+
Returns:
|
|
1001
|
+
The mode ('broadcast' or 'competing') or None if no subscriptions exist
|
|
1002
|
+
"""
|
|
1003
|
+
...
|
|
1004
|
+
|
|
993
1005
|
async def register_channel_receive_and_release_lock(
|
|
994
1006
|
self,
|
|
995
1007
|
instance_id: str,
|
|
@@ -3170,6 +3170,26 @@ class SQLAlchemyStorage:
|
|
|
3170
3170
|
"cursor_message_id": subscription.cursor_message_id,
|
|
3171
3171
|
}
|
|
3172
3172
|
|
|
3173
|
+
async def get_channel_mode(self, channel: str) -> str | None:
|
|
3174
|
+
"""
|
|
3175
|
+
Get the mode for a channel (from any existing subscription).
|
|
3176
|
+
|
|
3177
|
+
Args:
|
|
3178
|
+
channel: Channel name
|
|
3179
|
+
|
|
3180
|
+
Returns:
|
|
3181
|
+
The mode ('broadcast' or 'competing') or None if no subscriptions exist
|
|
3182
|
+
"""
|
|
3183
|
+
session = self._get_session_for_operation()
|
|
3184
|
+
async with self._session_scope(session) as session:
|
|
3185
|
+
result = await session.execute(
|
|
3186
|
+
select(ChannelSubscription.mode)
|
|
3187
|
+
.where(ChannelSubscription.channel == channel)
|
|
3188
|
+
.limit(1)
|
|
3189
|
+
)
|
|
3190
|
+
row = result.scalar_one_or_none()
|
|
3191
|
+
return row
|
|
3192
|
+
|
|
3173
3193
|
async def register_channel_receive_and_release_lock(
|
|
3174
3194
|
self,
|
|
3175
3195
|
instance_id: str,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
edda/__init__.py,sha256=hGC6WR2R36M8LWC97F-0Rw4Ln0QUUT_1xC-7acOy_Fk,2237
|
|
2
2
|
edda/activity.py,sha256=nRm9eBrr0lFe4ZRQ2whyZ6mo5xd171ITIVhqytUhOpw,21025
|
|
3
3
|
edda/app.py,sha256=ITTc7x5S4ykCP3KPZXKxuNczXkPtbn04ZQaxcem46Hw,68406
|
|
4
|
-
edda/channels.py,sha256=
|
|
4
|
+
edda/channels.py,sha256=6JFZkeOs0xDumexr0_bLI_Mb4S245hLJM_Sqp3xPCCA,37676
|
|
5
5
|
edda/compensation.py,sha256=iKLlnTxiF1YSatmYQW84EkPB1yMKUEZBtgjuGnghLtY,11824
|
|
6
6
|
edda/context.py,sha256=Qqm_nUC5NNnOfHAb7taqKqZVIc0GoRWUrjZ4L9_-q70,22128
|
|
7
7
|
edda/exceptions.py,sha256=-ntBLGpVQgPFG5N1o8m_7weejAYkNrUdxTkOP38vsHk,1766
|
|
@@ -34,8 +34,8 @@ edda/storage/migrations.py,sha256=KrceouVODct9WWDBhmjAW0IYptDWd2mqJmhrHnee59M,13
|
|
|
34
34
|
edda/storage/models.py,sha256=axXGJ-Orwcd_AsEUwIyFfDyg3NQxMcOQ2mrTzXkNv3g,12284
|
|
35
35
|
edda/storage/notify_base.py,sha256=gUb-ypG1Bo0c-KrleYmC7eKtdwQNUeqGS5k7UILlSsQ,5055
|
|
36
36
|
edda/storage/pg_notify.py,sha256=myzJ9xX86uiro9aaiA1SW1sN3E-zYafn7_lpeAy1jOg,11830
|
|
37
|
-
edda/storage/protocol.py,sha256=
|
|
38
|
-
edda/storage/sqlalchemy_storage.py,sha256=
|
|
37
|
+
edda/storage/protocol.py,sha256=tLUbD7SQ71oJVaTKfeh5HG1hvuLfaxoqC-a8m-iF0LY,39786
|
|
38
|
+
edda/storage/sqlalchemy_storage.py,sha256=UQmq3C_iC2j3N7q2V0kPcbtdwnFHAtyYWd9NBHVWsWQ,146934
|
|
39
39
|
edda/viewer_ui/__init__.py,sha256=N1-T33SXadOXcBsDSgJJ9Iqz4y4verJngWryQu70c5c,517
|
|
40
40
|
edda/viewer_ui/app.py,sha256=xZdIIGX5D2efNWQSVpPdldxLukHHpJD7JiAa_YKG5Uw,97084
|
|
41
41
|
edda/viewer_ui/components.py,sha256=A0IxLwgj_Lu51O57OfzOwME8jzoJtKegEVvSnWc7uPo,45174
|
|
@@ -47,8 +47,8 @@ edda/visualizer/mermaid_generator.py,sha256=XWa2egoOTNDfJEjPcwoxwQmblUqXf7YInWFj
|
|
|
47
47
|
edda/migrations/mysql/20251217000000_initial_schema.sql,sha256=LpINasESRhadOeqABwDk4JZ0OZ4_zQw_opnhIR4Xe9U,12367
|
|
48
48
|
edda/migrations/postgresql/20251217000000_initial_schema.sql,sha256=hCaGMWeptpzpnsjfNKVsMYuwPRe__fK9E0VZpClAumQ,11732
|
|
49
49
|
edda/migrations/sqlite/20251217000000_initial_schema.sql,sha256=Wq9gCnQ0K9SOt0PY_8f1MG4va8rLVWIIcf2lnRzSK5g,11906
|
|
50
|
-
edda_framework-0.
|
|
51
|
-
edda_framework-0.
|
|
52
|
-
edda_framework-0.
|
|
53
|
-
edda_framework-0.
|
|
54
|
-
edda_framework-0.
|
|
50
|
+
edda_framework-0.14.0.dist-info/METADATA,sha256=FbHEPrrr0THCfzFccY0_NJyCPuGArsxK6AKVrmTUKjQ,37567
|
|
51
|
+
edda_framework-0.14.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
52
|
+
edda_framework-0.14.0.dist-info/entry_points.txt,sha256=dPH47s6UoJgUZxHoeSMqZsQkLaSE-SGLi-gh88k2WrU,48
|
|
53
|
+
edda_framework-0.14.0.dist-info/licenses/LICENSE,sha256=udxb-V7_cYKTHqW7lNm48rxJ-Zpf0WAY_PyGDK9BPCo,1069
|
|
54
|
+
edda_framework-0.14.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|