jararaca 0.3.11a16__py3-none-any.whl → 0.4.0a5__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.
Files changed (88) hide show
  1. README.md +121 -0
  2. jararaca/__init__.py +184 -12
  3. jararaca/__main__.py +4 -0
  4. jararaca/broker_backend/__init__.py +4 -0
  5. jararaca/broker_backend/mapper.py +4 -0
  6. jararaca/broker_backend/redis_broker_backend.py +9 -3
  7. jararaca/cli.py +272 -47
  8. jararaca/common/__init__.py +3 -0
  9. jararaca/core/__init__.py +3 -0
  10. jararaca/core/providers.py +4 -0
  11. jararaca/core/uow.py +41 -7
  12. jararaca/di.py +4 -0
  13. jararaca/files/entity.py.mako +4 -0
  14. jararaca/lifecycle.py +6 -2
  15. jararaca/messagebus/__init__.py +4 -0
  16. jararaca/messagebus/bus_message_controller.py +4 -0
  17. jararaca/messagebus/consumers/__init__.py +3 -0
  18. jararaca/messagebus/decorators.py +33 -67
  19. jararaca/messagebus/implicit_headers.py +49 -0
  20. jararaca/messagebus/interceptors/__init__.py +3 -0
  21. jararaca/messagebus/interceptors/aiopika_publisher_interceptor.py +13 -4
  22. jararaca/messagebus/interceptors/publisher_interceptor.py +4 -0
  23. jararaca/messagebus/message.py +4 -0
  24. jararaca/messagebus/publisher.py +6 -0
  25. jararaca/messagebus/worker.py +850 -383
  26. jararaca/microservice.py +110 -1
  27. jararaca/observability/constants.py +7 -0
  28. jararaca/observability/decorators.py +170 -13
  29. jararaca/observability/fastapi_exception_handler.py +37 -0
  30. jararaca/observability/hooks.py +109 -0
  31. jararaca/observability/interceptor.py +4 -0
  32. jararaca/observability/providers/__init__.py +3 -0
  33. jararaca/observability/providers/otel.py +202 -11
  34. jararaca/persistence/base.py +38 -2
  35. jararaca/persistence/exports.py +4 -0
  36. jararaca/persistence/interceptors/__init__.py +3 -0
  37. jararaca/persistence/interceptors/aiosqa_interceptor.py +86 -73
  38. jararaca/persistence/interceptors/constants.py +5 -0
  39. jararaca/persistence/interceptors/decorators.py +50 -0
  40. jararaca/persistence/session.py +3 -0
  41. jararaca/persistence/sort_filter.py +4 -0
  42. jararaca/persistence/utilities.py +50 -20
  43. jararaca/presentation/__init__.py +3 -0
  44. jararaca/presentation/decorators.py +88 -86
  45. jararaca/presentation/exceptions.py +23 -0
  46. jararaca/presentation/hooks.py +4 -0
  47. jararaca/presentation/http_microservice.py +4 -0
  48. jararaca/presentation/server.py +97 -45
  49. jararaca/presentation/websocket/__init__.py +3 -0
  50. jararaca/presentation/websocket/base_types.py +4 -0
  51. jararaca/presentation/websocket/context.py +4 -0
  52. jararaca/presentation/websocket/decorators.py +8 -41
  53. jararaca/presentation/websocket/redis.py +280 -53
  54. jararaca/presentation/websocket/types.py +4 -0
  55. jararaca/presentation/websocket/websocket_interceptor.py +46 -19
  56. jararaca/reflect/__init__.py +3 -0
  57. jararaca/reflect/controller_inspect.py +16 -10
  58. jararaca/reflect/decorators.py +238 -0
  59. jararaca/reflect/metadata.py +34 -25
  60. jararaca/rpc/__init__.py +3 -0
  61. jararaca/rpc/http/__init__.py +101 -0
  62. jararaca/rpc/http/backends/__init__.py +14 -0
  63. jararaca/rpc/http/backends/httpx.py +43 -9
  64. jararaca/rpc/http/backends/otel.py +4 -0
  65. jararaca/rpc/http/decorators.py +378 -113
  66. jararaca/rpc/http/httpx.py +3 -0
  67. jararaca/scheduler/__init__.py +3 -0
  68. jararaca/scheduler/beat_worker.py +521 -105
  69. jararaca/scheduler/decorators.py +15 -22
  70. jararaca/scheduler/types.py +4 -0
  71. jararaca/tools/app_config/__init__.py +3 -0
  72. jararaca/tools/app_config/decorators.py +7 -19
  73. jararaca/tools/app_config/interceptor.py +6 -2
  74. jararaca/tools/typescript/__init__.py +3 -0
  75. jararaca/tools/typescript/decorators.py +120 -0
  76. jararaca/tools/typescript/interface_parser.py +1074 -173
  77. jararaca/utils/__init__.py +3 -0
  78. jararaca/utils/rabbitmq_utils.py +65 -39
  79. jararaca/utils/retry.py +10 -3
  80. jararaca-0.4.0a5.dist-info/LICENSE +674 -0
  81. jararaca-0.4.0a5.dist-info/LICENSES/GPL-3.0-or-later.txt +232 -0
  82. {jararaca-0.3.11a16.dist-info → jararaca-0.4.0a5.dist-info}/METADATA +11 -7
  83. jararaca-0.4.0a5.dist-info/RECORD +88 -0
  84. {jararaca-0.3.11a16.dist-info → jararaca-0.4.0a5.dist-info}/WHEEL +1 -1
  85. pyproject.toml +131 -0
  86. jararaca-0.3.11a16.dist-info/RECORD +0 -74
  87. /jararaca-0.3.11a16.dist-info/LICENSE → /LICENSE +0 -0
  88. {jararaca-0.3.11a16.dist-info → jararaca-0.4.0a5.dist-info}/entry_points.txt +0 -0
@@ -1,17 +1,22 @@
1
+ # SPDX-FileCopyrightText: 2025 Lucas S
2
+ #
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
1
5
  import inspect
2
6
  from dataclasses import dataclass
3
- from typing import Any, Awaitable, Callable, TypeVar, cast
7
+ from types import FunctionType
8
+ from typing import Any, Awaitable, Callable, TypeVar
4
9
 
5
10
  from jararaca.reflect.controller_inspect import (
6
11
  ControllerMemberReflect,
7
12
  inspect_controller,
8
13
  )
14
+ from jararaca.reflect.decorators import StackableDecorator
9
15
 
10
16
  DECORATED_FUNC = TypeVar("DECORATED_FUNC", bound=Callable[..., Any])
11
17
 
12
18
 
13
- class ScheduledAction:
14
- SCHEDULED_ACTION_ATTR = "__scheduled_action__"
19
+ class ScheduledAction(StackableDecorator):
15
20
 
16
21
  def __init__(
17
22
  self,
@@ -62,23 +67,6 @@ class ScheduledAction:
62
67
  An optional name for the scheduled action, used for filtering which actions to run.
63
68
  """
64
69
 
65
- def __call__(self, func: DECORATED_FUNC) -> DECORATED_FUNC:
66
- ScheduledAction.register(func, self)
67
- return func
68
-
69
- @staticmethod
70
- def register(func: DECORATED_FUNC, scheduled_action: "ScheduledAction") -> None:
71
- setattr(func, ScheduledAction.SCHEDULED_ACTION_ATTR, scheduled_action)
72
-
73
- @staticmethod
74
- def get_scheduled_action(func: DECORATED_FUNC) -> "ScheduledAction | None":
75
- if not hasattr(func, ScheduledAction.SCHEDULED_ACTION_ATTR):
76
- return None
77
-
78
- return cast(
79
- ScheduledAction, getattr(func, ScheduledAction.SCHEDULED_ACTION_ATTR)
80
- )
81
-
82
70
  @staticmethod
83
71
  def get_function_id(
84
72
  func: Callable[..., Any],
@@ -103,12 +91,17 @@ def get_type_scheduled_actions(
103
91
 
104
92
  _, member_metadata_map = inspect_controller(instance.__class__)
105
93
 
106
- members = inspect.getmembers(instance, predicate=inspect.ismethod)
94
+ members: list[tuple[str, FunctionType]] = []
95
+ for name, value in inspect.getmembers_static(
96
+ instance, predicate=inspect.isfunction
97
+ ):
98
+
99
+ members.append((name, value))
107
100
 
108
101
  scheduled_actions: list[ScheduledActionData] = []
109
102
 
110
103
  for name, member in members:
111
- scheduled_action = ScheduledAction.get_scheduled_action(member)
104
+ scheduled_action = ScheduledAction.get_last(member)
112
105
 
113
106
  if scheduled_action is None:
114
107
  continue
@@ -1,3 +1,7 @@
1
+ # SPDX-FileCopyrightText: 2025 Lucas S
2
+ #
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
1
5
  from pydantic import BaseModel
2
6
 
3
7
 
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: 2025 Lucas S
2
+ #
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
@@ -1,31 +1,19 @@
1
- from typing import Any, Type, TypeVar, cast
1
+ # SPDX-FileCopyrightText: 2025 Lucas S
2
+ #
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ from typing import Any, Type, TypeVar
2
6
 
3
7
  from pydantic import BaseModel
4
8
 
5
9
  from jararaca.core.providers import Token
10
+ from jararaca.reflect.decorators import StackableDecorator
6
11
 
7
12
  DECORATED_CLASS = TypeVar("DECORATED_CLASS", bound=Any)
8
13
 
9
14
 
10
- class RequiresConfig:
11
-
12
- REQUIRE_CONFIG_ATTR = "__requires_config__"
15
+ class RequiresConfig(StackableDecorator):
13
16
 
14
17
  def __init__(self, token: Token[Any], config: Type[BaseModel]):
15
18
  self.config = config
16
19
  self.token = token
17
-
18
- @staticmethod
19
- def register(cls: Type[DECORATED_CLASS], config: "RequiresConfig") -> None:
20
- setattr(cls, RequiresConfig.REQUIRE_CONFIG_ATTR, config)
21
-
22
- @staticmethod
23
- def get(cls: Type[DECORATED_CLASS]) -> "RequiresConfig | None":
24
- if not hasattr(cls, RequiresConfig.REQUIRE_CONFIG_ATTR):
25
- return None
26
-
27
- return cast(RequiresConfig, getattr(cls, RequiresConfig.REQUIRE_CONFIG_ATTR))
28
-
29
- def __call__(self, cls: Type[DECORATED_CLASS]) -> Type[DECORATED_CLASS]:
30
- RequiresConfig.register(cls, self)
31
- return cls
@@ -1,3 +1,7 @@
1
+ # SPDX-FileCopyrightText: 2025 Lucas S
2
+ #
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
1
5
  import logging
2
6
  import os
3
7
  from contextlib import asynccontextmanager
@@ -56,7 +60,7 @@ class AppConfigurationInterceptor(AppInterceptor, AppInterceptorWithLifecycle):
56
60
  *[
57
61
  (config.token, config.config)
58
62
  for controller in app.controllers
59
- if (config := RequiresConfig.get(controller))
63
+ if (config := RequiresConfig.get_last(controller))
60
64
  ],
61
65
  ]
62
66
 
@@ -90,7 +94,7 @@ class AppConfigurationInterceptor(AppInterceptor, AppInterceptorWithLifecycle):
90
94
 
91
95
  yield
92
96
 
93
- logger.info("finalizando")
97
+ logger.debug("finalizando")
94
98
 
95
99
 
96
100
  class AppConfigValidationError(Exception):
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: 2025 Lucas S
2
+ #
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
@@ -0,0 +1,120 @@
1
+ # SPDX-FileCopyrightText: 2025 Lucas S
2
+ #
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ from typing import Any, Callable, TypeVar
6
+
7
+ from pydantic import BaseModel
8
+
9
+ from jararaca.reflect.decorators import StackableDecorator
10
+
11
+ DECORATED_FUNC = TypeVar("DECORATED_FUNC", bound=Callable[..., Any])
12
+
13
+
14
+ class QueryEndpoint(StackableDecorator):
15
+ """
16
+ Decorator to mark a endpoint function as a query endpoint for Typescript generation.
17
+ """
18
+
19
+ def __init__(self, has_infinite_query: bool = False) -> None:
20
+ """
21
+ Initialize the QueryEndpoint decorator.
22
+
23
+ Args:
24
+ has_infinite_query: Whether the query endpoint supports infinite queries.
25
+ Important:
26
+ - Make sure a PaginatedQuery child instance is on the first argument
27
+ - Make sure the endpoint is a Patch (recommended) or Put method
28
+ - Make sure the endpoint returns a Paginated[T]
29
+ """
30
+ self.has_infinite_query = has_infinite_query
31
+
32
+ @staticmethod
33
+ def extract_query_endpoint(func: Any) -> "QueryEndpoint | None":
34
+ """
35
+ Check if the function is marked as a query endpoint.
36
+ """
37
+ return QueryEndpoint.get_last(func)
38
+
39
+
40
+ class MutationEndpoint(StackableDecorator):
41
+ """
42
+ Decorator to mark a endpoint function as a mutation endpoint for Typescript generation.
43
+ """
44
+
45
+ def __init__(self) -> None: ...
46
+
47
+ @staticmethod
48
+ def is_mutation(func: Any) -> bool:
49
+ """
50
+ Check if the function is marked as a mutation endpoint.
51
+ """
52
+ return MutationEndpoint.get_last(func) is not None
53
+
54
+
55
+ BASEMODEL_T = TypeVar("BASEMODEL_T", bound=BaseModel)
56
+
57
+
58
+ class SplitInputOutput(StackableDecorator):
59
+ """
60
+ Decorator to mark a Pydantic model to generate separate Input and Output TypeScript interfaces.
61
+
62
+ Input interface: Used for API inputs (mutations/queries), handles optional fields with defaults
63
+ Output interface: Used for API outputs, represents the complete object structure
64
+ """
65
+
66
+ def __init__(self) -> None:
67
+ pass
68
+
69
+ @staticmethod
70
+ def is_split_model(cls: type) -> bool:
71
+ """
72
+ Check if the Pydantic model is marked for split interface generation.
73
+ """
74
+ return SplitInputOutput.get_last(cls) is not None
75
+
76
+
77
+ class ExposeType:
78
+ """
79
+ Decorator to explicitly expose types for TypeScript interface generation.
80
+
81
+ Use this decorator to include types in the generated TypeScript output without
82
+ needing them as request/response bodies or indirect dependencies.
83
+
84
+ Example:
85
+ @ExposeType()
86
+ class UserRole(BaseModel):
87
+ id: str
88
+ name: str
89
+
90
+ # This ensures UserRole interface is generated even if it's not
91
+ # directly referenced in any REST endpoint
92
+ """
93
+
94
+ METADATA_KEY = "__jararaca_expose_type__"
95
+ _exposed_types: set[type] = set()
96
+
97
+ def __init__(self) -> None:
98
+ pass
99
+
100
+ def __call__(self, cls: type[BASEMODEL_T]) -> type[BASEMODEL_T]:
101
+ """
102
+ Decorate the type to mark it for explicit TypeScript generation.
103
+ """
104
+ setattr(cls, self.METADATA_KEY, True)
105
+ ExposeType._exposed_types.add(cls)
106
+ return cls
107
+
108
+ @staticmethod
109
+ def is_exposed_type(cls: type) -> bool:
110
+ """
111
+ Check if the type is marked for explicit exposure.
112
+ """
113
+ return getattr(cls, ExposeType.METADATA_KEY, False)
114
+
115
+ @staticmethod
116
+ def get_all_exposed_types() -> set[type]:
117
+ """
118
+ Get all types that have been marked for explicit exposure.
119
+ """
120
+ return ExposeType._exposed_types.copy()