jararaca 0.3.10__py3-none-any.whl → 0.3.11a0__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.
Potentially problematic release.
This version of jararaca might be problematic. Click here for more details.
- jararaca/messagebus/interceptors/aiopika_publisher_interceptor.py +31 -2
- jararaca/messagebus/interceptors/publisher_interceptor.py +2 -0
- jararaca/messagebus/publisher.py +14 -6
- jararaca/persistence/interceptors/aiosqa_interceptor.py +1 -0
- jararaca/presentation/websocket/context.py +30 -4
- jararaca/presentation/websocket/types.py +2 -2
- jararaca/presentation/websocket/websocket_interceptor.py +24 -2
- {jararaca-0.3.10.dist-info → jararaca-0.3.11a0.dist-info}/METADATA +1 -1
- {jararaca-0.3.10.dist-info → jararaca-0.3.11a0.dist-info}/RECORD +12 -12
- {jararaca-0.3.10.dist-info → jararaca-0.3.11a0.dist-info}/WHEEL +1 -1
- {jararaca-0.3.10.dist-info → jararaca-0.3.11a0.dist-info}/LICENSE +0 -0
- {jararaca-0.3.10.dist-info → jararaca-0.3.11a0.dist-info}/entry_points.txt +0 -0
|
@@ -15,6 +15,8 @@ from jararaca.messagebus.interceptors.publisher_interceptor import (
|
|
|
15
15
|
from jararaca.messagebus.publisher import IMessage, MessagePublisher
|
|
16
16
|
from jararaca.scheduler.types import DelayedMessageData
|
|
17
17
|
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
18
20
|
|
|
19
21
|
class AIOPikaMessagePublisher(MessagePublisher):
|
|
20
22
|
|
|
@@ -28,8 +30,13 @@ class AIOPikaMessagePublisher(MessagePublisher):
|
|
|
28
30
|
self.channel = channel
|
|
29
31
|
self.exchange_name = exchange_name
|
|
30
32
|
self.message_broker_backend = message_broker_backend
|
|
33
|
+
self.staged_delayed_messages: list[DelayedMessageData] = []
|
|
34
|
+
self.staged_messages: list[IMessage] = []
|
|
31
35
|
|
|
32
36
|
async def publish(self, message: IMessage, topic: str) -> None:
|
|
37
|
+
self.staged_messages.append(message)
|
|
38
|
+
|
|
39
|
+
async def _publish(self, message: IMessage, topic: str) -> None:
|
|
33
40
|
exchange = await self.channel.get_exchange(self.exchange_name, ensure=False)
|
|
34
41
|
if not exchange:
|
|
35
42
|
logging.warning(f"Exchange {self.exchange_name} not found")
|
|
@@ -45,7 +52,7 @@ class AIOPikaMessagePublisher(MessagePublisher):
|
|
|
45
52
|
raise NotImplementedError(
|
|
46
53
|
"Delay is not implemented for AIOPikaMessagePublisher"
|
|
47
54
|
)
|
|
48
|
-
|
|
55
|
+
self.staged_delayed_messages.append(
|
|
49
56
|
DelayedMessageData(
|
|
50
57
|
message_topic=message.MESSAGE_TOPIC,
|
|
51
58
|
payload=message.model_dump_json().encode(),
|
|
@@ -60,7 +67,7 @@ class AIOPikaMessagePublisher(MessagePublisher):
|
|
|
60
67
|
raise NotImplementedError(
|
|
61
68
|
"Schedule is not implemented for AIOPikaMessagePublisher"
|
|
62
69
|
)
|
|
63
|
-
|
|
70
|
+
self.staged_delayed_messages.append(
|
|
64
71
|
DelayedMessageData(
|
|
65
72
|
message_topic=message.MESSAGE_TOPIC,
|
|
66
73
|
payload=message.model_dump_json().encode(),
|
|
@@ -68,6 +75,28 @@ class AIOPikaMessagePublisher(MessagePublisher):
|
|
|
68
75
|
)
|
|
69
76
|
)
|
|
70
77
|
|
|
78
|
+
async def flush(self) -> None:
|
|
79
|
+
print("Flushing messages to AIOPika channel")
|
|
80
|
+
for message in self.staged_messages:
|
|
81
|
+
logger.debug(
|
|
82
|
+
f"Publishing message {message.MESSAGE_TOPIC} with payload: {message.model_dump_json()}"
|
|
83
|
+
)
|
|
84
|
+
await self._publish(message, message.MESSAGE_TOPIC)
|
|
85
|
+
|
|
86
|
+
if len(self.staged_delayed_messages) > 0:
|
|
87
|
+
if not self.message_broker_backend:
|
|
88
|
+
raise NotImplementedError(
|
|
89
|
+
"MessageBrokerBackend is required to publish delayed messages"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
for delayed_message in self.staged_delayed_messages:
|
|
93
|
+
logger.debug(
|
|
94
|
+
f"Scheduling delayed message {delayed_message.message_topic} with payload: {delayed_message.payload.decode()}"
|
|
95
|
+
)
|
|
96
|
+
await self.message_broker_backend.enqueue_delayed_message(
|
|
97
|
+
delayed_message
|
|
98
|
+
)
|
|
99
|
+
|
|
71
100
|
|
|
72
101
|
class GenericPoolConfig(BaseModel):
|
|
73
102
|
max_size: int
|
jararaca/messagebus/publisher.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
1
2
|
from contextlib import contextmanager, suppress
|
|
2
3
|
from contextvars import ContextVar
|
|
3
4
|
from datetime import datetime, tzinfo
|
|
4
|
-
from typing import Any, ClassVar, Generator, Literal
|
|
5
|
+
from typing import Any, ClassVar, Generator, Literal
|
|
5
6
|
|
|
6
7
|
from pydantic import BaseModel
|
|
7
8
|
|
|
@@ -19,24 +20,31 @@ class IMessage(BaseModel):
|
|
|
19
20
|
MESSAGE_TYPE: ClassVar[Literal["task", "event"]] = "task"
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
class MessagePublisher(
|
|
23
|
+
class MessagePublisher(ABC):
|
|
24
|
+
@abstractmethod
|
|
23
25
|
async def publish(self, message: IMessage, topic: str) -> None:
|
|
24
|
-
|
|
26
|
+
pass
|
|
25
27
|
|
|
28
|
+
@abstractmethod
|
|
26
29
|
async def delay(self, message: IMessage, seconds: int) -> None:
|
|
27
30
|
"""
|
|
28
31
|
Delay the message for a given number of seconds.
|
|
29
32
|
"""
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
@abstractmethod
|
|
33
35
|
async def schedule(
|
|
34
36
|
self, message: IMessage, when: datetime, timezone: tzinfo
|
|
35
37
|
) -> None:
|
|
36
38
|
"""
|
|
37
39
|
Schedule the message for a given datetime.
|
|
38
40
|
"""
|
|
39
|
-
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
async def flush(self) -> None:
|
|
44
|
+
"""
|
|
45
|
+
Publish all messages that have been delayed or scheduled.
|
|
46
|
+
This is typically called at the end of a request or task processing.
|
|
47
|
+
"""
|
|
40
48
|
|
|
41
49
|
|
|
42
50
|
message_publishers_ctx = ContextVar[dict[str, MessagePublisher]](
|
|
@@ -16,12 +16,12 @@ class WebSocketConnectionManager(Protocol):
|
|
|
16
16
|
async def remove_websocket(self, websocket: WebSocket) -> None: ...
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
_ws_conn_manager_ctx = ContextVar[WebSocketConnectionManager]("ws_manage_ctx")
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def use_ws_manager() -> WebSocketConnectionManager:
|
|
23
23
|
try:
|
|
24
|
-
return
|
|
24
|
+
return _ws_conn_manager_ctx.get()
|
|
25
25
|
except LookupError:
|
|
26
26
|
raise RuntimeError("No WebSocketConnectionManager found")
|
|
27
27
|
|
|
@@ -30,9 +30,35 @@ def use_ws_manager() -> WebSocketConnectionManager:
|
|
|
30
30
|
def provide_ws_manager(
|
|
31
31
|
ws_manager: WebSocketConnectionManager,
|
|
32
32
|
) -> Generator[None, None, None]:
|
|
33
|
-
token =
|
|
33
|
+
token = _ws_conn_manager_ctx.set(ws_manager)
|
|
34
34
|
try:
|
|
35
35
|
yield
|
|
36
36
|
finally:
|
|
37
37
|
with suppress(ValueError):
|
|
38
|
-
|
|
38
|
+
_ws_conn_manager_ctx.reset(token)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class WebSocketMessageSender(Protocol):
|
|
42
|
+
async def send(self, rooms: list[str], message: WebSocketMessageBase) -> None: ...
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
_ws_msg_sender_ctx = ContextVar[WebSocketMessageSender]("ws_msg_sender_ctx")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def use_ws_message_sender() -> WebSocketMessageSender:
|
|
49
|
+
try:
|
|
50
|
+
return _ws_msg_sender_ctx.get()
|
|
51
|
+
except LookupError:
|
|
52
|
+
raise RuntimeError("No WebSocketMessageSender found")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@contextmanager
|
|
56
|
+
def provide_ws_message_sender(
|
|
57
|
+
ws_message_sender: WebSocketMessageSender,
|
|
58
|
+
) -> Generator[None, None, None]:
|
|
59
|
+
token = _ws_msg_sender_ctx.set(ws_message_sender)
|
|
60
|
+
try:
|
|
61
|
+
yield
|
|
62
|
+
finally:
|
|
63
|
+
with suppress(ValueError):
|
|
64
|
+
_ws_msg_sender_ctx.reset(token)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from jararaca.presentation.websocket.base_types import WebSocketMessageBase
|
|
2
|
-
from jararaca.presentation.websocket.context import
|
|
2
|
+
from jararaca.presentation.websocket.context import use_ws_message_sender
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class WebSocketMessage(WebSocketMessageBase):
|
|
6
6
|
|
|
7
7
|
async def send(self, *rooms: str) -> None:
|
|
8
|
-
await
|
|
8
|
+
await use_ws_message_sender().send(list(rooms), self)
|
|
@@ -25,7 +25,9 @@ from jararaca.presentation.decorators import (
|
|
|
25
25
|
)
|
|
26
26
|
from jararaca.presentation.websocket.context import (
|
|
27
27
|
WebSocketConnectionManager,
|
|
28
|
+
WebSocketMessageSender,
|
|
28
29
|
provide_ws_manager,
|
|
30
|
+
provide_ws_message_sender,
|
|
29
31
|
)
|
|
30
32
|
from jararaca.presentation.websocket.decorators import (
|
|
31
33
|
INHERITS_WS_MESSAGE,
|
|
@@ -127,9 +129,23 @@ class WebSocketConnectionManagerImpl(WebSocketConnectionManager):
|
|
|
127
129
|
# async def setup_consumer(self, websocket: WebSocket) -> None: ...
|
|
128
130
|
|
|
129
131
|
|
|
132
|
+
class StagingWebSocketMessageSender(WebSocketMessageSender):
|
|
133
|
+
|
|
134
|
+
def __init__(self, connection: WebSocketConnectionManager) -> None:
|
|
135
|
+
self.connection = connection
|
|
136
|
+
self.staged_messages: list[tuple[list[str], WebSocketMessageBase]] = []
|
|
137
|
+
|
|
138
|
+
async def send(self, rooms: list[str], message: WebSocketMessageBase) -> None:
|
|
139
|
+
self.staged_messages.append((rooms, message))
|
|
140
|
+
|
|
141
|
+
async def flush(self) -> None:
|
|
142
|
+
for rooms, message in self.staged_messages:
|
|
143
|
+
await self.connection.send(rooms, message)
|
|
144
|
+
self.staged_messages.clear()
|
|
145
|
+
|
|
146
|
+
|
|
130
147
|
class WebSocketInterceptor(AppInterceptor, AppInterceptorWithLifecycle):
|
|
131
148
|
"""
|
|
132
|
-
@Deprecated
|
|
133
149
|
WebSocketInterceptor is responsible for managing WebSocket connections and
|
|
134
150
|
intercepting WebSocket requests within the application. It integrates with
|
|
135
151
|
the application's lifecycle and provides a router for WebSocket endpoints.
|
|
@@ -171,8 +187,14 @@ class WebSocketInterceptor(AppInterceptor, AppInterceptorWithLifecycle):
|
|
|
171
187
|
@asynccontextmanager
|
|
172
188
|
async def intercept(self, app_context: AppContext) -> AsyncGenerator[None, None]:
|
|
173
189
|
|
|
174
|
-
|
|
190
|
+
staging_ws_messages_sender = StagingWebSocketMessageSender(
|
|
191
|
+
self.connection_manager
|
|
192
|
+
)
|
|
193
|
+
with provide_ws_manager(self.connection_manager), provide_ws_message_sender(
|
|
194
|
+
staging_ws_messages_sender
|
|
195
|
+
):
|
|
175
196
|
yield
|
|
197
|
+
await staging_ws_messages_sender.flush()
|
|
176
198
|
|
|
177
199
|
# def __wrap_with_uow_context_provider(
|
|
178
200
|
# self, uow: UnitOfWorkContextProvider, func: Callable[..., Any]
|
|
@@ -16,10 +16,10 @@ jararaca/messagebus/bus_message_controller.py,sha256=Xd_qwnX5jUvgBTCarHR36fvtol9
|
|
|
16
16
|
jararaca/messagebus/consumers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
jararaca/messagebus/decorators.py,sha256=y-w4dWbP9ZW3ZJ4mE9iIaxw01ZC5snEbOuBY5NC-Bn0,5626
|
|
18
18
|
jararaca/messagebus/interceptors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
-
jararaca/messagebus/interceptors/aiopika_publisher_interceptor.py,sha256=
|
|
20
|
-
jararaca/messagebus/interceptors/publisher_interceptor.py,sha256=
|
|
19
|
+
jararaca/messagebus/interceptors/aiopika_publisher_interceptor.py,sha256=WBHf8DKcuCeH4kBZq9fx_qsryhU7yWplJNLF16LpqMk,6574
|
|
20
|
+
jararaca/messagebus/interceptors/publisher_interceptor.py,sha256=wkIDoa__Q7o74cv89PXdBJFbe-ijxH8Xc_-hBEgxYjM,1272
|
|
21
21
|
jararaca/messagebus/message.py,sha256=U6cyd2XknX8mtm0333slz5fanky2PFLWCmokAO56vvU,819
|
|
22
|
-
jararaca/messagebus/publisher.py,sha256=
|
|
22
|
+
jararaca/messagebus/publisher.py,sha256=JTkxdKbvxvDWT8nK8PVEyyX061vYYbKQMxRHXrZtcEY,2173
|
|
23
23
|
jararaca/messagebus/worker.py,sha256=2jReCepgMQktdLh4A3OqmOmx6KNgqUGpXIOXoBUtXSg,13778
|
|
24
24
|
jararaca/messagebus/worker_v2.py,sha256=Ey9HPgVSuIYJUggJGPKmgymZHz7-2TUSYgDB52u9T10,20683
|
|
25
25
|
jararaca/microservice.py,sha256=C_Txqm3xSmdHIghJigKk6TOycL5eTJv9PF6XP7TA8j4,6638
|
|
@@ -30,7 +30,7 @@ jararaca/observability/providers/otel.py,sha256=LgfoITdoQTCxKebfLcEfwMiG992wlWY_
|
|
|
30
30
|
jararaca/persistence/base.py,sha256=Xfnpvj3yeLdpVBifH5W6AwPCLwL2ot0dpLzbPg1zwkQ,966
|
|
31
31
|
jararaca/persistence/exports.py,sha256=Ghx4yoFaB4QVTb9WxrFYgmcSATXMNvrOvT8ybPNKXCA,62
|
|
32
32
|
jararaca/persistence/interceptors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
-
jararaca/persistence/interceptors/aiosqa_interceptor.py,sha256=
|
|
33
|
+
jararaca/persistence/interceptors/aiosqa_interceptor.py,sha256=EZz80PZVb80DrM1rOLRkKMDO-93by6xp97G7o_LzziE,2034
|
|
34
34
|
jararaca/persistence/session.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
35
|
jararaca/persistence/sort_filter.py,sha256=agggpN0YvNjUr6wJjy69NkaqxoDDW13ys9B3r85OujA,9226
|
|
36
36
|
jararaca/persistence/utilities.py,sha256=imcV4Oi5kXNk6m9QF2-OsnFpcTRY4w5mBYLdEx5XTSQ,14296
|
|
@@ -41,11 +41,11 @@ jararaca/presentation/http_microservice.py,sha256=g771JosV6jTY3hQtG-HkLOo-T0e-r3
|
|
|
41
41
|
jararaca/presentation/server.py,sha256=JuEatLFhD_NFnszwXDSgtcCXNOwvQYyxoxxQ33hnAEc,3731
|
|
42
42
|
jararaca/presentation/websocket/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
43
|
jararaca/presentation/websocket/base_types.py,sha256=AvUeeZ1TFhSiRMcYqZU1HaQNqSrcgTkC5R0ArP5dGmA,146
|
|
44
|
-
jararaca/presentation/websocket/context.py,sha256=
|
|
44
|
+
jararaca/presentation/websocket/context.py,sha256=A6K5W3kqo9Hgeh1m6JiI7Cdz5SfbXcaICSVX7u1ARZo,1903
|
|
45
45
|
jararaca/presentation/websocket/decorators.py,sha256=ZNd5aoA9UkyfHOt1C8D2Ffy2gQUNDEsusVnQuTgExgs,2157
|
|
46
46
|
jararaca/presentation/websocket/redis.py,sha256=6wD4zGHftJXNDW3VfS65WJt2cnOgTI0zmQOfjZ_CEXE,4726
|
|
47
|
-
jararaca/presentation/websocket/types.py,sha256=
|
|
48
|
-
jararaca/presentation/websocket/websocket_interceptor.py,sha256=
|
|
47
|
+
jararaca/presentation/websocket/types.py,sha256=M8snAMSdaQlKrwEM2qOgF2qrefo5Meio_oOw620Joc8,308
|
|
48
|
+
jararaca/presentation/websocket/websocket_interceptor.py,sha256=OET9Dv8CVJn6bkjnmMXqT_yRWr9HTyd7E-dtN7zfeQ4,9155
|
|
49
49
|
jararaca/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
50
|
jararaca/rpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
51
|
jararaca/rpc/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -66,8 +66,8 @@ jararaca/tools/metadata.py,sha256=7nlCDYgItNybentPSSCc2MLqN7IpBd0VyQzfjfQycVI,14
|
|
|
66
66
|
jararaca/tools/typescript/interface_parser.py,sha256=35xbOrZDQDyTXdMrVZQ8nnFw79f28lJuLYNHAspIqi8,30492
|
|
67
67
|
jararaca/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
68
68
|
jararaca/utils/rabbitmq_utils.py,sha256=FPDP8ZVgvitZXV-oK73D7EIANsqUzXTW7HdpEKsIsyI,2811
|
|
69
|
-
jararaca-0.3.
|
|
70
|
-
jararaca-0.3.
|
|
71
|
-
jararaca-0.3.
|
|
72
|
-
jararaca-0.3.
|
|
73
|
-
jararaca-0.3.
|
|
69
|
+
jararaca-0.3.11a0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
70
|
+
jararaca-0.3.11a0.dist-info/METADATA,sha256=XbMVjGqVYheva2VT-YGJoMx_DalQbkjx7t3PfkFACrI,4954
|
|
71
|
+
jararaca-0.3.11a0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
72
|
+
jararaca-0.3.11a0.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
|
|
73
|
+
jararaca-0.3.11a0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|