jararaca 0.3.11a4__py3-none-any.whl → 0.3.11a5__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/__init__.py CHANGED
@@ -11,7 +11,7 @@ if TYPE_CHECKING:
11
11
  retry_later,
12
12
  use_bus_message_controller,
13
13
  )
14
- from jararaca.microservice import AppContext, AppInterceptor
14
+ from jararaca.microservice import AppInterceptor, AppTransactionContext
15
15
  from jararaca.observability.interceptor import ObservabilityInterceptor
16
16
  from jararaca.observability.providers.otel import OtelObservabilityProvider
17
17
  from jararaca.persistence.sort_filter import (
@@ -32,6 +32,17 @@ if TYPE_CHECKING:
32
32
  raises_500_on,
33
33
  raises_http_exception_on,
34
34
  )
35
+ from jararaca.reflect.controller_inspect import (
36
+ ControllerMemberReflect,
37
+ ControllerReflect,
38
+ )
39
+ from jararaca.reflect.metadata import (
40
+ SetMetadata,
41
+ get_all_metadata,
42
+ get_metadata,
43
+ get_metadata_value,
44
+ provide_metadata,
45
+ )
35
46
  from jararaca.rpc.http.backends.httpx import HTTPXHttpRPCAsyncBackend
36
47
  from jararaca.rpc.http.backends.otel import TracedRequestMiddleware
37
48
  from jararaca.rpc.http.decorators import Body
@@ -68,7 +79,13 @@ if TYPE_CHECKING:
68
79
  from .messagebus.message import Message, MessageOf
69
80
  from .messagebus.publisher import use_publisher
70
81
  from .messagebus.worker import MessageBusWorker
71
- from .microservice import Microservice, use_app_context, use_current_container
82
+ from .microservice import (
83
+ Microservice,
84
+ use_app_context,
85
+ use_app_transaction_context,
86
+ use_app_tx_ctx_data,
87
+ use_current_container,
88
+ )
72
89
  from .persistence.base import T_BASEMODEL, BaseEntity
73
90
  from .persistence.interceptors.aiosqa_interceptor import (
74
91
  AIOSQAConfig,
@@ -122,6 +139,11 @@ if TYPE_CHECKING:
122
139
  from .tools.app_config.interceptor import AppConfigurationInterceptor
123
140
 
124
141
  __all__ = [
142
+ "SetMetadata",
143
+ "provide_metadata",
144
+ "get_metadata",
145
+ "get_all_metadata",
146
+ "get_metadata_value",
125
147
  "RedisMessageBrokerBackend",
126
148
  "FilterRuleApplier",
127
149
  "SortRuleApplier",
@@ -197,6 +219,7 @@ if TYPE_CHECKING:
197
219
  "use_session",
198
220
  "use_transaction",
199
221
  "providing_session",
222
+ "provide_session",
200
223
  "providing_transaction",
201
224
  "providing_new_session",
202
225
  "Post",
@@ -220,7 +243,12 @@ if TYPE_CHECKING:
220
243
  "HttpRpcClientBuilder",
221
244
  "HTTPXHttpRPCAsyncBackend",
222
245
  "use_app_context",
246
+ "use_app_transaction_context",
247
+ "use_app_tx_ctx_data",
248
+ "AppTransactionContext",
223
249
  "AppContext",
250
+ "ControllerMemberReflect",
251
+ "ControllerReflect",
224
252
  "AppInterceptor",
225
253
  "OtelObservabilityProvider",
226
254
  ]
@@ -228,6 +256,11 @@ if TYPE_CHECKING:
228
256
  __SPEC_PARENT__: str = __spec__.parent # type: ignore
229
257
  # A mapping of {<member name>: (package, <module name>)} defining dynamic imports
230
258
  _dynamic_imports: "dict[str, tuple[str, str, str | None]]" = {
259
+ "SetMetadata": (__SPEC_PARENT__, "reflect.metadata", None),
260
+ "provide_metadata": (__SPEC_PARENT__, "reflect.metadata", None),
261
+ "get_metadata": (__SPEC_PARENT__, "reflect.metadata", None),
262
+ "get_all_metadata": (__SPEC_PARENT__, "reflect.metadata", None),
263
+ "get_metadata_value": (__SPEC_PARENT__, "reflect.metadata", None),
231
264
  "RedisMessageBrokerBackend": (
232
265
  __SPEC_PARENT__,
233
266
  "broker_backend.redis_broker_backend",
@@ -353,6 +386,11 @@ _dynamic_imports: "dict[str, tuple[str, str, str | None]]" = {
353
386
  "persistence.interceptors.aiosqa_interceptor",
354
387
  None,
355
388
  ),
389
+ "provide_session": (
390
+ __SPEC_PARENT__,
391
+ "persistence.interceptors.aiosqa_interceptor",
392
+ None,
393
+ ),
356
394
  "providing_new_session": (
357
395
  __SPEC_PARENT__,
358
396
  "persistence.interceptors.aiosqa_interceptor",
@@ -403,8 +441,13 @@ _dynamic_imports: "dict[str, tuple[str, str, str | None]]" = {
403
441
  "HttpRpcClientBuilder": (__SPEC_PARENT__, "rpc.http.decorators", None),
404
442
  "HTTPXHttpRPCAsyncBackend": (__SPEC_PARENT__, "rpc.http.backends.httpx", None),
405
443
  "use_app_context": (__SPEC_PARENT__, "microservice", None),
444
+ "use_app_transaction_context": (__SPEC_PARENT__, "microservice", None),
445
+ "use_app_tx_ctx_data": (__SPEC_PARENT__, "microservice", None),
406
446
  "AppContext": (__SPEC_PARENT__, "microservice", None),
407
447
  "AppInterceptor": (__SPEC_PARENT__, "microservice", None),
448
+ "AppTransactionContext": (__SPEC_PARENT__, "microservice", None),
449
+ "ControllerMemberReflect": (__SPEC_PARENT__, "reflect.controller_inspect", None),
450
+ "ControllerReflect": (__SPEC_PARENT__, "reflect.controller_inspect", None),
408
451
  }
409
452
 
410
453
 
jararaca/cli.py CHANGED
@@ -112,7 +112,7 @@ async def declare_worker_infrastructure(
112
112
  handlers, _ = factory(instance)
113
113
 
114
114
  for handler in handlers:
115
- queue_name = f"{handler.message_type.MESSAGE_TOPIC}.{handler.callable.__module__}.{handler.callable.__qualname__}"
115
+ queue_name = f"{handler.message_type.MESSAGE_TOPIC}.{handler.instance_callable.__module__}.{handler.instance_callable.__qualname__}"
116
116
  routing_key = f"{handler.message_type.MESSAGE_TOPIC}.#"
117
117
 
118
118
  queue = await channel.declare_queue(
@@ -192,7 +192,7 @@ async def declare_worker_v2_infrastructure(
192
192
 
193
193
  # Declare queues for message handlers
194
194
  for handler in handlers:
195
- queue_name = f"{handler.message_type.MESSAGE_TOPIC}.{handler.callable.__module__}.{handler.callable.__qualname__}"
195
+ queue_name = f"{handler.message_type.MESSAGE_TOPIC}.{handler.instance_callable.__module__}.{handler.instance_callable.__qualname__}"
196
196
  routing_key = f"{handler.message_type.MESSAGE_TOPIC}.#"
197
197
 
198
198
  queue = await RabbitmqUtils.declare_queue(
jararaca/core/uow.py CHANGED
@@ -2,13 +2,14 @@ from contextlib import asynccontextmanager
2
2
  from typing import AsyncGenerator, Sequence
3
3
 
4
4
  from jararaca.microservice import (
5
- AppContext,
6
5
  AppInterceptor,
6
+ AppTransactionContext,
7
7
  Container,
8
8
  Microservice,
9
9
  provide_app_context,
10
10
  provide_container,
11
11
  )
12
+ from jararaca.reflect.metadata import provide_metadata
12
13
 
13
14
 
14
15
  class ContainerInterceptor(AppInterceptor):
@@ -17,7 +18,9 @@ class ContainerInterceptor(AppInterceptor):
17
18
  self.container = container
18
19
 
19
20
  @asynccontextmanager
20
- async def intercept(self, app_context: AppContext) -> AsyncGenerator[None, None]:
21
+ async def intercept(
22
+ self, app_context: AppTransactionContext
23
+ ) -> AsyncGenerator[None, None]:
21
24
 
22
25
  with provide_app_context(app_context), provide_container(self.container):
23
26
  yield None
@@ -49,18 +52,20 @@ class UnitOfWorkContextProvider:
49
52
  return interceptors
50
53
 
51
54
  @asynccontextmanager
52
- async def __call__(self, app_context: AppContext) -> AsyncGenerator[None, None]:
55
+ async def __call__(
56
+ self, app_context: AppTransactionContext
57
+ ) -> AsyncGenerator[None, None]:
53
58
 
54
59
  app_interceptors = self.factory_app_interceptors()
60
+ with provide_metadata(app_context.controller_member_reflect.metadata):
61
+ ctxs = [self.container_interceptor.intercept(app_context)] + [
62
+ interceptor.intercept(app_context) for interceptor in app_interceptors
63
+ ]
55
64
 
56
- ctxs = [self.container_interceptor.intercept(app_context)] + [
57
- interceptor.intercept(app_context) for interceptor in app_interceptors
58
- ]
65
+ for ctx in ctxs:
66
+ await ctx.__aenter__()
59
67
 
60
- for ctx in ctxs:
61
- await ctx.__aenter__()
62
-
63
- yield None
68
+ yield None
64
69
 
65
- for ctx in reversed(ctxs):
66
- await ctx.__aexit__(None, None, None)
70
+ for ctx in reversed(ctxs):
71
+ await ctx.__aexit__(None, None, None)
@@ -3,10 +3,14 @@ from dataclasses import dataclass
3
3
  from typing import Any, Awaitable, Callable, Generic, TypeVar, cast
4
4
 
5
5
  from jararaca.messagebus.message import INHERITS_MESSAGE_CO, Message, MessageOf
6
- from jararaca.scheduler.decorators import ScheduledAction
6
+ from jararaca.reflect.controller_inspect import (
7
+ ControllerMemberReflect,
8
+ inspect_controller,
9
+ )
10
+ from jararaca.scheduler.decorators import ScheduledAction, ScheduledActionData
7
11
 
8
12
  DECORATED_FUNC = TypeVar("DECORATED_FUNC", bound=Callable[..., Any])
9
- DECORATED_CLASS = TypeVar("DECORATED_CLASS", bound=Any)
13
+ DECORATED_T = TypeVar("DECORATED_T", bound=Any)
10
14
 
11
15
 
12
16
  class MessageHandler(Generic[INHERITS_MESSAGE_CO]):
@@ -60,7 +64,8 @@ class MessageHandler(Generic[INHERITS_MESSAGE_CO]):
60
64
  class MessageHandlerData:
61
65
  message_type: type[Any]
62
66
  spec: MessageHandler[Message]
63
- callable: Callable[[MessageOf[Any]], Awaitable[None]]
67
+ instance_callable: Callable[[MessageOf[Any]], Awaitable[None]]
68
+ controller_member: ControllerMemberReflect
64
69
 
65
70
 
66
71
  @dataclass(frozen=True)
@@ -68,16 +73,9 @@ class ScheduleDispatchData:
68
73
  timestamp: float
69
74
 
70
75
 
71
- @dataclass(frozen=True)
72
- class ScheduledActionData:
73
- spec: ScheduledAction
74
- callable: Callable[
75
- ..., Awaitable[None]
76
- ] # Callable[[ScheduleDispatchData], Awaitable[None]]
77
-
76
+ SCHEDULED_ACTION_DATA_SET = set[ScheduledActionData]
78
77
 
79
78
  MESSAGE_HANDLER_DATA_SET = set[MessageHandlerData]
80
- SCHEDULED_ACTION_DATA_SET = set[ScheduledActionData]
81
79
 
82
80
 
83
81
  class MessageBusController:
@@ -93,55 +91,60 @@ class MessageBusController:
93
91
  def get_messagebus_factory(
94
92
  self,
95
93
  ) -> Callable[
96
- [DECORATED_CLASS], tuple[MESSAGE_HANDLER_DATA_SET, SCHEDULED_ACTION_DATA_SET]
94
+ [DECORATED_T], tuple[MESSAGE_HANDLER_DATA_SET, SCHEDULED_ACTION_DATA_SET]
97
95
  ]:
98
96
  if self.messagebus_factory is None:
99
97
  raise Exception("MessageBus factory is not set")
100
98
  return self.messagebus_factory
101
99
 
102
- def __call__(self, func: type[DECORATED_CLASS]) -> type[DECORATED_CLASS]:
100
+ def __call__(self, cls_t: type[DECORATED_T]) -> type[DECORATED_T]:
103
101
 
104
102
  def messagebus_factory(
105
- instance: DECORATED_CLASS,
103
+ instance: DECORATED_T,
106
104
  ) -> tuple[MESSAGE_HANDLER_DATA_SET, SCHEDULED_ACTION_DATA_SET]:
107
105
  handlers: MESSAGE_HANDLER_DATA_SET = set()
108
106
 
109
107
  schedulers: SCHEDULED_ACTION_DATA_SET = set()
110
108
 
111
- members = inspect.getmembers(func, predicate=inspect.isfunction)
109
+ controller, members = inspect_controller(cls_t)
112
110
 
113
- for name, member in members:
114
- message_handler_decoration = MessageHandler.get_message_incoming(member)
111
+ for name, member in members.items():
112
+ message_handler_decoration = MessageHandler.get_message_incoming(
113
+ member.member_function
114
+ )
115
115
  scheduled_action_decoration = ScheduledAction.get_scheduled_action(
116
- member
116
+ member.member_function
117
117
  )
118
118
 
119
119
  if message_handler_decoration is not None:
120
120
 
121
- if not inspect.iscoroutinefunction(member):
121
+ if not inspect.iscoroutinefunction(member.member_function):
122
122
  raise Exception(
123
123
  "Message incoming handler '%s' from '%s.%s' must be a coroutine function"
124
- % (name, func.__module__, func.__qualname__)
124
+ % (name, cls_t.__module__, cls_t.__qualname__)
125
125
  )
126
126
 
127
127
  handlers.add(
128
128
  MessageHandlerData(
129
129
  message_type=message_handler_decoration.message_type,
130
130
  spec=message_handler_decoration,
131
- callable=getattr(instance, name),
131
+ instance_callable=getattr(instance, name),
132
+ controller_member=member,
132
133
  )
133
134
  )
134
135
  elif scheduled_action_decoration is not None:
135
- if not inspect.iscoroutinefunction(member):
136
+ if not inspect.iscoroutinefunction(member.member_function):
136
137
  raise Exception(
137
138
  "Scheduled action handler '%s' from '%s.%s' must be a coroutine function"
138
- % (name, func.__module__, func.__qualname__)
139
+ % (name, cls_t.__module__, cls_t.__qualname__)
139
140
  )
141
+ instance_callable = getattr(instance, name)
140
142
 
141
143
  schedulers.add(
142
144
  ScheduledActionData(
145
+ controller_member=member,
143
146
  spec=scheduled_action_decoration,
144
- callable=getattr(instance, name),
147
+ callable=instance_callable,
145
148
  )
146
149
  )
147
150
 
@@ -149,19 +152,17 @@ class MessageBusController:
149
152
 
150
153
  self.messagebus_factory = messagebus_factory
151
154
 
152
- MessageBusController.register(func, self)
155
+ MessageBusController.register(cls_t, self)
153
156
 
154
- return func
157
+ return cls_t
155
158
 
156
159
  @staticmethod
157
- def register(
158
- func: type[DECORATED_CLASS], messagebus: "MessageBusController"
159
- ) -> None:
160
+ def register(func: type[DECORATED_T], messagebus: "MessageBusController") -> None:
160
161
 
161
162
  setattr(func, MessageBusController.MESSAGEBUS_ATTR, messagebus)
162
163
 
163
164
  @staticmethod
164
- def get_messagebus(func: type[DECORATED_CLASS]) -> "MessageBusController | None":
165
+ def get_messagebus(func: type[DECORATED_T]) -> "MessageBusController | None":
165
166
  if not hasattr(func, MessageBusController.MESSAGEBUS_ATTR):
166
167
  return None
167
168
 
@@ -3,7 +3,7 @@ from typing import AsyncContextManager, AsyncGenerator, Protocol
3
3
 
4
4
  from jararaca.broker_backend import MessageBrokerBackend
5
5
  from jararaca.messagebus.publisher import MessagePublisher, provide_message_publisher
6
- from jararaca.microservice import AppContext, AppInterceptor
6
+ from jararaca.microservice import AppInterceptor, AppTransactionContext
7
7
 
8
8
 
9
9
  class MessageBusConnectionFactory(Protocol):
@@ -24,8 +24,10 @@ class MessageBusPublisherInterceptor(AppInterceptor):
24
24
  self.message_scheduler = message_scheduler
25
25
 
26
26
  @asynccontextmanager
27
- async def intercept(self, app_context: AppContext) -> AsyncGenerator[None, None]:
28
- if app_context.context_type == "websocket":
27
+ async def intercept(
28
+ self, app_context: AppTransactionContext
29
+ ) -> AsyncGenerator[None, None]:
30
+ if app_context.transaction_data.context_type == "websocket":
29
31
  yield
30
32
  return
31
33
 
@@ -25,7 +25,11 @@ from jararaca.messagebus.decorators import (
25
25
  MessageHandlerData,
26
26
  )
27
27
  from jararaca.messagebus.message import Message, MessageOf
28
- from jararaca.microservice import MessageBusAppContext, Microservice
28
+ from jararaca.microservice import (
29
+ AppTransactionContext,
30
+ MessageBusTransactionData,
31
+ Microservice,
32
+ )
29
33
  from jararaca.utils.rabbitmq_utils import RabbitmqUtils
30
34
 
31
35
  logger = logging.getLogger(__name__)
@@ -109,7 +113,7 @@ class AioPikaMicroserviceConsumer:
109
113
 
110
114
  for handler in self.message_handler_set:
111
115
 
112
- queue_name = f"{handler.message_type.MESSAGE_TOPIC}.{handler.callable.__module__}.{handler.callable.__qualname__}"
116
+ queue_name = f"{handler.message_type.MESSAGE_TOPIC}.{handler.instance_callable.__module__}.{handler.instance_callable.__qualname__}"
113
117
  routing_key = f"{handler.message_type.MESSAGE_TOPIC}.#"
114
118
 
115
119
  self.incoming_map[queue_name] = handler
@@ -219,7 +223,7 @@ class MessageHandlerCallback:
219
223
 
220
224
  return
221
225
 
222
- handler = handler_data.callable
226
+ handler = handler_data.instance_callable
223
227
 
224
228
  sig = inspect.signature(handler)
225
229
 
@@ -263,9 +267,12 @@ class MessageHandlerCallback:
263
267
  assert incoming_message_spec is not None
264
268
 
265
269
  async with self.consumer.uow_context_provider(
266
- MessageBusAppContext(
267
- message=builded_message,
268
- topic=routing_key,
270
+ AppTransactionContext(
271
+ controller_member_reflect=handler_data.controller_member,
272
+ transaction_data=MessageBusTransactionData(
273
+ message=builded_message,
274
+ topic=routing_key,
275
+ ),
269
276
  )
270
277
  ):
271
278
  ctx: AsyncContextManager[Any]
@@ -6,15 +6,7 @@ from abc import ABC
6
6
  from contextlib import asynccontextmanager, suppress
7
7
  from dataclasses import dataclass
8
8
  from datetime import UTC, datetime
9
- from typing import (
10
- Any,
11
- AsyncContextManager,
12
- AsyncGenerator,
13
- Awaitable,
14
- Callable,
15
- Type,
16
- get_origin,
17
- )
9
+ from typing import Any, AsyncContextManager, AsyncGenerator, Type, get_origin
18
10
  from urllib.parse import parse_qs, urlparse
19
11
 
20
12
  import aio_pika
@@ -37,15 +29,16 @@ from jararaca.messagebus.decorators import (
37
29
  MessageBusController,
38
30
  MessageHandler,
39
31
  MessageHandlerData,
40
- ScheduledActionData,
41
32
  ScheduleDispatchData,
42
33
  )
43
34
  from jararaca.messagebus.message import Message, MessageOf
44
35
  from jararaca.microservice import (
45
- MessageBusAppContext,
36
+ AppTransactionContext,
37
+ MessageBusTransactionData,
46
38
  Microservice,
47
- SchedulerAppContext,
39
+ SchedulerTransactionData,
48
40
  )
41
+ from jararaca.scheduler.decorators import ScheduledActionData
49
42
  from jararaca.utils.rabbitmq_utils import RabbitmqUtils
50
43
 
51
44
  logger = logging.getLogger(__name__)
@@ -152,7 +145,7 @@ class AioPikaMicroserviceConsumer(MessageBusConsumer):
152
145
 
153
146
  for handler in self.message_handler_set:
154
147
 
155
- queue_name = f"{handler.message_type.MESSAGE_TOPIC}.{handler.callable.__module__}.{handler.callable.__qualname__}"
148
+ queue_name = f"{handler.message_type.MESSAGE_TOPIC}.{handler.instance_callable.__module__}.{handler.instance_callable.__qualname__}"
156
149
  routing_key = f"{handler.message_type.MESSAGE_TOPIC}.#"
157
150
 
158
151
  self.incoming_map[queue_name] = handler
@@ -311,7 +304,6 @@ class ScheduledMessageHandlerCallback:
311
304
 
312
305
  task = asyncio.create_task(
313
306
  self.run_with_context(
314
- self.scheduled_action.callable,
315
307
  self.scheduled_action,
316
308
  (ScheduleDispatchData(int(aio_pika_message.body.decode("utf-8"))),),
317
309
  {},
@@ -321,7 +313,6 @@ class ScheduledMessageHandlerCallback:
321
313
  elif len(sig.parameters) == 0:
322
314
  task = asyncio.create_task(
323
315
  self.run_with_context(
324
- self.scheduled_action.callable,
325
316
  self.scheduled_action,
326
317
  (),
327
318
  {},
@@ -347,21 +338,22 @@ class ScheduledMessageHandlerCallback:
347
338
 
348
339
  async def run_with_context(
349
340
  self,
350
- func: Callable[..., Awaitable[None]],
351
341
  scheduled_action: ScheduledActionData,
352
342
  args: tuple[Any, ...],
353
343
  kwargs: dict[str, Any],
354
344
  ) -> None:
355
345
  async with self.consumer.uow_context_provider(
356
- SchedulerAppContext(
357
- action=func,
358
- scheduled_to=datetime.now(UTC),
359
- cron_expression=scheduled_action.spec.cron,
360
- triggered_at=datetime.now(UTC),
346
+ AppTransactionContext(
347
+ controller_member_reflect=scheduled_action.controller_member,
348
+ transaction_data=SchedulerTransactionData(
349
+ scheduled_to=datetime.now(UTC),
350
+ cron_expression=scheduled_action.spec.cron,
351
+ triggered_at=datetime.now(UTC),
352
+ ),
361
353
  )
362
354
  ):
363
355
 
364
- await func(*args, **kwargs)
356
+ await scheduled_action.callable(*args, **kwargs)
365
357
 
366
358
 
367
359
  class MessageHandlerCallback:
@@ -427,7 +419,7 @@ class MessageHandlerCallback:
427
419
 
428
420
  handler_data = self.message_handler
429
421
 
430
- handler = handler_data.callable
422
+ handler = handler_data.instance_callable
431
423
 
432
424
  sig = inspect.signature(handler)
433
425
 
@@ -471,9 +463,12 @@ class MessageHandlerCallback:
471
463
  assert incoming_message_spec is not None
472
464
 
473
465
  async with self.consumer.uow_context_provider(
474
- MessageBusAppContext(
475
- message=builded_message,
476
- topic=routing_key,
466
+ AppTransactionContext(
467
+ controller_member_reflect=handler_data.controller_member,
468
+ transaction_data=MessageBusTransactionData(
469
+ message=builded_message,
470
+ topic=routing_key,
471
+ ),
477
472
  )
478
473
  ):
479
474
  ctx: AsyncContextManager[Any]
jararaca/microservice.py CHANGED
@@ -23,64 +23,103 @@ from fastapi import Request, WebSocket
23
23
  from jararaca.core.providers import ProviderSpec, T, Token
24
24
  from jararaca.messagebus import MessageOf
25
25
  from jararaca.messagebus.message import Message
26
+ from jararaca.reflect.controller_inspect import ControllerMemberReflect
26
27
 
27
28
  if TYPE_CHECKING:
28
29
  from typing_extensions import TypeIs
29
30
 
30
31
 
31
32
  @dataclass
32
- class SchedulerAppContext:
33
+ class SchedulerTransactionData:
33
34
  triggered_at: datetime
34
35
  scheduled_to: datetime
35
36
  cron_expression: str
36
- action: Callable[..., Any]
37
37
  context_type: Literal["scheduler"] = "scheduler"
38
38
 
39
39
 
40
40
  @dataclass
41
- class HttpAppContext:
41
+ class HttpTransactionData:
42
42
  request: Request
43
43
  context_type: Literal["http"] = "http"
44
44
 
45
45
 
46
46
  @dataclass
47
- class MessageBusAppContext:
47
+ class MessageBusTransactionData:
48
48
  topic: str
49
49
  message: MessageOf[Message]
50
50
  context_type: Literal["message_bus"] = "message_bus"
51
51
 
52
52
 
53
53
  @dataclass
54
- class WebSocketAppContext:
54
+ class WebSocketTransactionData:
55
55
  websocket: WebSocket
56
56
  context_type: Literal["websocket"] = "websocket"
57
57
 
58
58
 
59
- AppContext = (
60
- MessageBusAppContext | HttpAppContext | SchedulerAppContext | WebSocketAppContext
59
+ TransactionData = (
60
+ MessageBusTransactionData
61
+ | HttpTransactionData
62
+ | SchedulerTransactionData
63
+ | WebSocketTransactionData
61
64
  )
62
65
 
63
- app_context_ctxvar = ContextVar[AppContext]("app_context")
64
66
 
67
+ @dataclass
68
+ class AppTransactionContext:
69
+ transaction_data: TransactionData
70
+ controller_member_reflect: ControllerMemberReflect
71
+
72
+
73
+ AppContext = AppTransactionContext
74
+ """
75
+ Alias for AppTransactionContext, used for compatibility with existing code.
76
+ """
77
+
78
+
79
+ app_transaction_context_var = ContextVar[AppTransactionContext]("app_context")
80
+
81
+
82
+ def use_app_transaction_context() -> AppTransactionContext:
83
+ """
84
+ Returns the current application transaction context.
85
+ This function is used to access the application transaction context in the context of an application transaction.
86
+ If no context is set, it raises a LookupError.
87
+ """
88
+
89
+ return app_transaction_context_var.get()
65
90
 
66
- def use_app_context() -> AppContext:
67
- return app_context_ctxvar.get()
91
+
92
+ def use_app_tx_ctx_data() -> TransactionData:
93
+ """
94
+ Returns the transaction data from the current app transaction context.
95
+ This function is used to access the transaction data in the context of an application transaction.
96
+ """
97
+
98
+ return use_app_transaction_context().transaction_data
99
+
100
+
101
+ use_app_context = use_app_tx_ctx_data
102
+ """Alias for use_app_tx_ctx_data, used for compatibility with existing code."""
68
103
 
69
104
 
70
105
  @contextmanager
71
- def provide_app_context(app_context: AppContext) -> Generator[None, None, None]:
72
- token = app_context_ctxvar.set(app_context)
106
+ def provide_app_context(
107
+ app_context: AppTransactionContext,
108
+ ) -> Generator[None, None, None]:
109
+ token = app_transaction_context_var.set(app_context)
73
110
  try:
74
111
  yield
75
112
  finally:
76
113
  with suppress(ValueError):
77
- app_context_ctxvar.reset(token)
114
+ app_transaction_context_var.reset(token)
78
115
 
79
116
 
80
117
  @runtime_checkable
81
118
  class AppInterceptor(Protocol):
82
119
 
83
- def intercept(self, app_context: AppContext) -> AsyncContextManager[None]: ...
120
+ def intercept(
121
+ self, app_context: AppTransactionContext
122
+ ) -> AsyncContextManager[None]: ...
84
123
 
85
124
 
86
125
  class AppInterceptorWithLifecycle(Protocol):
@@ -227,17 +266,21 @@ def provide_container(container: Container) -> Generator[None, None, None]:
227
266
 
228
267
 
229
268
  __all__ = [
230
- "AppContext",
269
+ "AppTransactionContext",
231
270
  "AppInterceptor",
232
271
  "AppInterceptorWithLifecycle",
233
272
  "Container",
234
273
  "Microservice",
235
- "SchedulerAppContext",
236
- "WebSocketAppContext",
237
- "app_context_ctxvar",
274
+ "SchedulerTransactionData",
275
+ "WebSocketTransactionData",
276
+ "app_transaction_context_var",
238
277
  "current_container_ctx",
239
278
  "provide_app_context",
240
279
  "provide_container",
241
280
  "use_app_context",
242
281
  "use_current_container",
282
+ "HttpTransactionData",
283
+ "MessageBusTransactionData",
284
+ "is_interceptor_with_lifecycle",
285
+ "AppContext",
243
286
  ]
@@ -13,7 +13,7 @@ from typing import (
13
13
  TypeVar,
14
14
  )
15
15
 
16
- from jararaca.microservice import AppContext
16
+ from jararaca.microservice import AppTransactionContext
17
17
 
18
18
  P = ParamSpec("P")
19
19
  R = TypeVar("R")
@@ -28,9 +28,13 @@ class TracingContextProvider(Protocol):
28
28
 
29
29
  class TracingContextProviderFactory(Protocol):
30
30
 
31
- def root_setup(self, app_context: AppContext) -> AsyncContextManager[None]: ...
31
+ def root_setup(
32
+ self, app_context: AppTransactionContext
33
+ ) -> AsyncContextManager[None]: ...
32
34
 
33
- def provide_provider(self, app_context: AppContext) -> TracingContextProvider: ...
35
+ def provide_provider(
36
+ self, app_context: AppTransactionContext
37
+ ) -> TracingContextProvider: ...
34
38
 
35
39
 
36
40
  tracing_ctx_provider_ctxv = ContextVar[TracingContextProvider]("tracing_ctx_provider")