jararaca 0.3.11a1__py3-none-any.whl → 0.3.11a3__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/cli.py CHANGED
@@ -1,3 +1,4 @@
1
+ import asyncio
1
2
  import importlib
2
3
  import importlib.resources
3
4
  import multiprocessing
@@ -9,6 +10,7 @@ from pathlib import Path
9
10
  from typing import Any
10
11
  from urllib.parse import urlparse, urlunsplit
11
12
 
13
+ import aio_pika
12
14
  import click
13
15
  import uvicorn
14
16
  from mako.template import Template
@@ -23,6 +25,7 @@ from jararaca.scheduler.scheduler_v2 import SchedulerV2
23
25
  from jararaca.tools.typescript.interface_parser import (
24
26
  write_microservice_to_typescript_interface,
25
27
  )
28
+ from jararaca.utils.rabbitmq_utils import RabbitmqUtils
26
29
 
27
30
  LIBRARY_FILES_PATH = importlib.resources.files("jararaca.files")
28
31
  ENTITY_TEMPLATE_PATH = LIBRARY_FILES_PATH / "entity.py.mako"
@@ -67,6 +70,230 @@ def find_microservice_by_module_path(module_path: str) -> Microservice:
67
70
  return app
68
71
 
69
72
 
73
+ async def declare_worker_infrastructure(
74
+ url: str,
75
+ exchange: str,
76
+ app: Microservice,
77
+ passive_declare: bool = False,
78
+ ) -> None:
79
+ """
80
+ Declare the infrastructure (exchanges and queues) for worker v1.
81
+ """
82
+ connection = await aio_pika.connect(url)
83
+ channel = await connection.channel()
84
+
85
+ await channel.set_qos(prefetch_count=1)
86
+
87
+ # Declare main exchange
88
+ main_ex = await RabbitmqUtils.declare_main_exchange(
89
+ channel=channel,
90
+ exchange_name=exchange,
91
+ passive=passive_declare,
92
+ )
93
+
94
+ # Declare dead letter infrastructure
95
+ dlx, dlq = await RabbitmqUtils.declare_dl_kit(
96
+ channel=channel, passive=passive_declare
97
+ )
98
+
99
+ # Find all message handlers to declare their queues
100
+ from jararaca.di import Container
101
+ from jararaca.messagebus.decorators import MessageBusController
102
+
103
+ container = Container(app)
104
+
105
+ for instance_type in app.controllers:
106
+ controller = MessageBusController.get_messagebus(instance_type)
107
+ if controller is None:
108
+ continue
109
+
110
+ instance: Any = container.get_by_type(instance_type)
111
+ factory = controller.get_messagebus_factory()
112
+ handlers, _ = factory(instance)
113
+
114
+ for handler in handlers:
115
+ queue_name = f"{handler.message_type.MESSAGE_TOPIC}.{handler.callable.__module__}.{handler.callable.__qualname__}"
116
+ routing_key = f"{handler.message_type.MESSAGE_TOPIC}.#"
117
+
118
+ queue = await channel.declare_queue(
119
+ passive=passive_declare,
120
+ name=queue_name,
121
+ arguments={
122
+ "x-dead-letter-exchange": dlx.name,
123
+ "x-dead-letter-routing-key": dlq.name,
124
+ },
125
+ durable=True,
126
+ )
127
+
128
+ await queue.bind(exchange=main_ex, routing_key=routing_key)
129
+ click.echo(f"✓ Declared queue: {queue_name} (routing key: {routing_key})")
130
+
131
+ await channel.close()
132
+ await connection.close()
133
+
134
+
135
+ async def declare_worker_v2_infrastructure(
136
+ broker_url: str,
137
+ app: Microservice,
138
+ passive_declare: bool = False,
139
+ ) -> None:
140
+ """
141
+ Declare the infrastructure (exchanges and queues) for worker v2.
142
+ """
143
+ from urllib.parse import parse_qs, urlparse
144
+
145
+ parsed_url = urlparse(broker_url)
146
+ if parsed_url.scheme not in ["amqp", "amqps"]:
147
+ raise ValueError(f"Unsupported broker URL scheme: {parsed_url.scheme}")
148
+
149
+ if not parsed_url.query:
150
+ raise ValueError("Query string must be set for AMQP URLs")
151
+
152
+ query_params = parse_qs(parsed_url.query)
153
+
154
+ if "exchange" not in query_params or not query_params["exchange"]:
155
+ raise ValueError("Exchange must be set in the query string")
156
+
157
+ exchange = query_params["exchange"][0]
158
+
159
+ connection = await aio_pika.connect(broker_url)
160
+ channel = await connection.channel()
161
+
162
+ await channel.set_qos(prefetch_count=1)
163
+
164
+ # Declare main exchange
165
+ await RabbitmqUtils.declare_main_exchange(
166
+ channel=channel,
167
+ exchange_name=exchange,
168
+ passive=passive_declare,
169
+ )
170
+
171
+ # Declare dead letter infrastructure
172
+ dlx = await RabbitmqUtils.declare_dl_exchange(
173
+ channel=channel, passive=passive_declare
174
+ )
175
+ dlq = await RabbitmqUtils.declare_dl_queue(channel=channel, passive=passive_declare)
176
+ await dlq.bind(dlx, routing_key=RabbitmqUtils.DEAD_LETTER_EXCHANGE)
177
+
178
+ # Find all message handlers and scheduled actions
179
+ from jararaca.di import Container
180
+ from jararaca.messagebus.decorators import MessageBusController
181
+
182
+ container = Container(app)
183
+
184
+ for instance_type in app.controllers:
185
+ controller = MessageBusController.get_messagebus(instance_type)
186
+ if controller is None:
187
+ continue
188
+
189
+ instance: Any = container.get_by_type(instance_type)
190
+ factory = controller.get_messagebus_factory()
191
+ handlers, scheduled_actions = factory(instance)
192
+
193
+ # Declare queues for message handlers
194
+ for handler in handlers:
195
+ queue_name = f"{handler.message_type.MESSAGE_TOPIC}.{handler.callable.__module__}.{handler.callable.__qualname__}"
196
+ routing_key = f"{handler.message_type.MESSAGE_TOPIC}.#"
197
+
198
+ queue = await RabbitmqUtils.declare_queue(
199
+ channel=channel, queue_name=queue_name, passive=passive_declare
200
+ )
201
+ await queue.bind(exchange=exchange, routing_key=routing_key)
202
+ click.echo(
203
+ f"✓ Declared message handler queue: {queue_name} (routing key: {routing_key})"
204
+ )
205
+
206
+ # Declare queues for scheduled actions
207
+ for scheduled_action in scheduled_actions:
208
+ queue_name = f"{scheduled_action.callable.__module__}.{scheduled_action.callable.__qualname__}"
209
+ routing_key = queue_name
210
+
211
+ queue = await RabbitmqUtils.declare_queue(
212
+ channel=channel, queue_name=queue_name, passive=passive_declare
213
+ )
214
+ await queue.bind(exchange=exchange, routing_key=routing_key)
215
+ click.echo(
216
+ f"✓ Declared scheduled action queue: {queue_name} (routing key: {routing_key})"
217
+ )
218
+
219
+ await channel.close()
220
+ await connection.close()
221
+
222
+
223
+ async def declare_scheduler_v2_infrastructure(
224
+ broker_url: str,
225
+ app: Microservice,
226
+ passive_declare: bool = False,
227
+ ) -> None:
228
+ """
229
+ Declare the infrastructure (exchanges and queues) for scheduler v2.
230
+ """
231
+ from urllib.parse import parse_qs, urlparse
232
+
233
+ from jararaca.scheduler.decorators import ScheduledAction
234
+
235
+ parsed_url = urlparse(broker_url)
236
+ if parsed_url.scheme not in ["amqp", "amqps"]:
237
+ raise ValueError(f"Unsupported broker URL scheme: {parsed_url.scheme}")
238
+
239
+ if not parsed_url.query:
240
+ raise ValueError("Query string must be set for AMQP URLs")
241
+
242
+ query_params = parse_qs(parsed_url.query)
243
+
244
+ if "exchange" not in query_params or not query_params["exchange"]:
245
+ raise ValueError("Exchange must be set in the query string")
246
+
247
+ exchange = query_params["exchange"][0]
248
+
249
+ connection = await aio_pika.connect(broker_url)
250
+ channel = await connection.channel()
251
+
252
+ await channel.set_qos(prefetch_count=1)
253
+
254
+ # Declare exchange for scheduler
255
+ await channel.declare_exchange(
256
+ name=exchange,
257
+ type="topic",
258
+ durable=True,
259
+ auto_delete=False,
260
+ passive=passive_declare,
261
+ )
262
+
263
+ # Find all scheduled actions and declare their queues
264
+ from jararaca.di import Container
265
+ from jararaca.messagebus.decorators import MessageBusController
266
+
267
+ container = Container(app)
268
+ scheduled_actions: list[Any] = []
269
+
270
+ for instance_type in app.controllers:
271
+ controller = MessageBusController.get_messagebus(instance_type)
272
+ if controller is None:
273
+ continue
274
+
275
+ instance: Any = container.get_by_type(instance_type)
276
+ factory = controller.get_messagebus_factory()
277
+ _, actions = factory(instance)
278
+ scheduled_actions.extend(actions)
279
+
280
+ for scheduled_action in scheduled_actions:
281
+ queue_name = ScheduledAction.get_function_id(scheduled_action.callable)
282
+ queue = await channel.declare_queue(
283
+ name=queue_name,
284
+ durable=True,
285
+ passive=passive_declare,
286
+ )
287
+ await queue.bind(
288
+ exchange=exchange,
289
+ routing_key=queue_name,
290
+ )
291
+ click.echo(f"✓ Declared scheduler queue: {queue_name}")
292
+
293
+ await channel.close()
294
+ await connection.close()
295
+
296
+
70
297
  @click.group()
71
298
  def cli() -> None:
72
299
  pass
@@ -493,3 +720,170 @@ def gen_entity(entity_name: str, file_path: StreamWriter) -> None:
493
720
  entityNameKebabCase=entity_kebab_case,
494
721
  )
495
722
  )
723
+
724
+
725
+ @cli.command("declare-queues-v1")
726
+ @click.argument(
727
+ "app_path",
728
+ type=str,
729
+ )
730
+ @click.option(
731
+ "--broker-url",
732
+ type=str,
733
+ envvar="BROKER_URL",
734
+ help="Broker URL (e.g., amqp://guest:guest@localhost/) [env: BROKER_URL]",
735
+ )
736
+ @click.option(
737
+ "--exchange",
738
+ type=str,
739
+ default="jararaca_ex",
740
+ envvar="EXCHANGE",
741
+ help="Exchange name [env: EXCHANGE]",
742
+ )
743
+ @click.option(
744
+ "--passive-declare",
745
+ is_flag=True,
746
+ default=False,
747
+ help="Use passive declarations (check if infrastructure exists without creating it)",
748
+ )
749
+ def declare_queues_v1(
750
+ app_path: str,
751
+ broker_url: str | None,
752
+ exchange: str,
753
+ passive_declare: bool,
754
+ ) -> None:
755
+ """
756
+ Declare RabbitMQ infrastructure (exchanges and queues) for worker v1.
757
+
758
+ This command pre-declares the necessary exchanges and queues that worker v1
759
+ needs, without starting the actual consumption processes.
760
+
761
+ Environment variables:
762
+ - BROKER_URL: Broker URL (e.g., amqp://guest:guest@localhost/)
763
+ - EXCHANGE: Exchange name (defaults to 'jararaca_ex')
764
+
765
+ Examples:
766
+
767
+ \b
768
+ # Declare worker v1 infrastructure
769
+ jararaca declare-queues-v1 myapp:app --broker-url amqp://guest:guest@localhost/
770
+
771
+ \b
772
+ # Use environment variables
773
+ export BROKER_URL="amqp://guest:guest@localhost/"
774
+ export EXCHANGE="my_exchange"
775
+ jararaca declare-queues-v1 myapp:app
776
+ """
777
+
778
+ app = find_microservice_by_module_path(app_path)
779
+
780
+ async def run_declarations() -> None:
781
+ if not broker_url:
782
+ click.echo(
783
+ "ERROR: --broker-url is required or set BROKER_URL environment variable",
784
+ err=True,
785
+ )
786
+ return
787
+
788
+ click.echo(
789
+ f"→ Declaring worker v1 infrastructure (URL: {broker_url}, Exchange: {exchange})"
790
+ )
791
+
792
+ try:
793
+ await declare_worker_infrastructure(
794
+ broker_url, exchange, app, passive_declare
795
+ )
796
+ click.echo("✓ Worker v1 infrastructure declared successfully!")
797
+ except Exception as e:
798
+ click.echo(
799
+ f"ERROR: Failed to declare worker v1 infrastructure: {e}", err=True
800
+ )
801
+ raise
802
+
803
+ asyncio.run(run_declarations())
804
+
805
+
806
+ @cli.command("declare-queues-v2")
807
+ @click.argument(
808
+ "app_path",
809
+ type=str,
810
+ )
811
+ @click.option(
812
+ "--broker-url",
813
+ type=str,
814
+ envvar="BROKER_URL",
815
+ help="Broker URL (e.g., amqp://guest:guest@localhost/) [env: BROKER_URL]",
816
+ )
817
+ @click.option(
818
+ "--exchange",
819
+ type=str,
820
+ default="jararaca_ex",
821
+ envvar="EXCHANGE",
822
+ help="Exchange name [env: EXCHANGE]",
823
+ )
824
+ @click.option(
825
+ "--passive-declare",
826
+ is_flag=True,
827
+ default=False,
828
+ help="Use passive declarations (check if infrastructure exists without creating it)",
829
+ )
830
+ def declare_queues_v2(
831
+ app_path: str,
832
+ broker_url: str | None,
833
+ exchange: str,
834
+ passive_declare: bool,
835
+ ) -> None:
836
+ """
837
+ Declare RabbitMQ infrastructure (exchanges and queues) for worker v2 and scheduler v2.
838
+
839
+ This command pre-declares the necessary exchanges and queues that worker v2
840
+ and scheduler v2 need, without starting the actual consumption processes.
841
+
842
+ Environment variables:
843
+ - BROKER_URL: Broker URL (e.g., amqp://guest:guest@localhost/)
844
+ - EXCHANGE: Exchange name (defaults to 'jararaca_ex')
845
+
846
+ Examples:
847
+
848
+ \b
849
+ # Declare worker v2 and scheduler v2 infrastructure
850
+ jararaca declare-queues-v2 myapp:app --broker-url amqp://guest:guest@localhost/
851
+
852
+ \b
853
+ # Use environment variables
854
+ export BROKER_URL="amqp://guest:guest@localhost/"
855
+ export EXCHANGE="my_exchange"
856
+ jararaca declare-queues-v2 myapp:app
857
+ """
858
+
859
+ app = find_microservice_by_module_path(app_path)
860
+
861
+ async def run_declarations() -> None:
862
+ if not broker_url:
863
+ click.echo(
864
+ "ERROR: --broker-url is required or set BROKER_URL environment variable",
865
+ err=True,
866
+ )
867
+ return
868
+
869
+ # For v2, create the broker URL with exchange parameter
870
+ v2_broker_url = f"{broker_url}?exchange={exchange}"
871
+
872
+ click.echo(f"→ Declaring worker v2 infrastructure (URL: {v2_broker_url})")
873
+ click.echo(f"→ Declaring scheduler v2 infrastructure (URL: {v2_broker_url})")
874
+
875
+ try:
876
+ await asyncio.gather(
877
+ declare_worker_v2_infrastructure(v2_broker_url, app, passive_declare),
878
+ declare_scheduler_v2_infrastructure(
879
+ v2_broker_url, app, passive_declare
880
+ ),
881
+ )
882
+ click.echo(
883
+ "✓ Worker v2 and scheduler v2 infrastructure declared successfully!"
884
+ )
885
+ except Exception as e:
886
+ click.echo(f"ERROR: Failed to declare v2 infrastructure: {e}", err=True)
887
+ raise
888
+
889
+ asyncio.run(run_declarations())
@@ -58,7 +58,6 @@ class AIOSqlAlchemySessionInterceptor(AppInterceptor):
58
58
  with provide_session(self.config.connection_name, session):
59
59
  try:
60
60
  yield
61
- print("Committing session")
62
61
  await session.commit()
63
62
  except Exception as e:
64
63
  await session.rollback()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: jararaca
3
- Version: 0.3.11a1
3
+ Version: 0.3.11a3
4
4
  Summary: A simple and fast API framework for Python
5
5
  Author: Lucas S
6
6
  Author-email: me@luscasleo.dev
@@ -3,7 +3,7 @@ jararaca/__main__.py,sha256=-O3vsB5lHdqNFjUtoELDF81IYFtR-DSiiFMzRaiSsv4,67
3
3
  jararaca/broker_backend/__init__.py,sha256=GzEIuHR1xzgCJD4FE3harNjoaYzxHMHoEL0_clUaC-k,3528
4
4
  jararaca/broker_backend/mapper.py,sha256=vTsi7sWpNvlga1PWPFg0rCJ5joJ0cdzykkIc2Tuvenc,696
5
5
  jararaca/broker_backend/redis_broker_backend.py,sha256=a7DHchy3NAiD71Ix8SwmQOUnniu7uup-Woa4ON_4J7I,5786
6
- jararaca/cli.py,sha256=gYdokdAWAPI9e5fQwkQofrcbE_ofJXr1kHc7wFRufnc,12283
6
+ jararaca/cli.py,sha256=9TrlHdZGaqNO3Hg4HpBsTc9x8W3oFP9fVqjPR0rMzKk,24671
7
7
  jararaca/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  jararaca/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  jararaca/core/providers.py,sha256=wktH84FK7c1s2wNq-fudf1uMfi3CQBR0neU2czJ_L0U,434
@@ -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=EZz80PZVb80DrM1rOLRkKMDO-93by6xp97G7o_LzziE,2034
33
+ jararaca/persistence/interceptors/aiosqa_interceptor.py,sha256=H6ZjOdosYGCZUzKjugiXQwJkAbnsL4HnkZLOEQhULEc,1986
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
@@ -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.11a1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
70
- jararaca-0.3.11a1.dist-info/METADATA,sha256=38ZWg5TI8tUYXHtFdyn5RlmeQ5p6skk4vd8oCj5JCsA,4954
71
- jararaca-0.3.11a1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
72
- jararaca-0.3.11a1.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
73
- jararaca-0.3.11a1.dist-info/RECORD,,
69
+ jararaca-0.3.11a3.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
70
+ jararaca-0.3.11a3.dist-info/METADATA,sha256=JIMh7XrOOOqc11tQi7hoGCfKaW4D7YSWW542qBJbBgk,4954
71
+ jararaca-0.3.11a3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
72
+ jararaca-0.3.11a3.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
73
+ jararaca-0.3.11a3.dist-info/RECORD,,