jararaca 0.3.11a14__py3-none-any.whl → 0.3.11a16__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.
@@ -0,0 +1,141 @@
1
+ import asyncio
2
+ import logging
3
+ import random
4
+ from functools import wraps
5
+ from typing import Awaitable, Callable, Optional, ParamSpec, TypeVar
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ P = ParamSpec("P")
10
+ T = TypeVar("T")
11
+
12
+
13
+ class RetryConfig:
14
+ """Configuration for the retry mechanism."""
15
+
16
+ def __init__(
17
+ self,
18
+ max_retries: int = 5,
19
+ initial_delay: float = 1.0,
20
+ max_delay: float = 60.0,
21
+ backoff_factor: float = 2.0,
22
+ jitter: bool = True,
23
+ ):
24
+ """
25
+ Initialize retry configuration.
26
+
27
+ Args:
28
+ max_retries: Maximum number of retry attempts (default: 5)
29
+ initial_delay: Initial delay in seconds between retries (default: 1.0)
30
+ max_delay: Maximum delay in seconds between retries (default: 60.0)
31
+ backoff_factor: Multiplier for the delay after each retry (default: 2.0)
32
+ jitter: Whether to add randomness to the delay to prevent thundering herd (default: True)
33
+ """
34
+ self.max_retries = max_retries
35
+ self.initial_delay = initial_delay
36
+ self.max_delay = max_delay
37
+ self.backoff_factor = backoff_factor
38
+ self.jitter = jitter
39
+
40
+
41
+ E = TypeVar("E", bound=Exception)
42
+
43
+
44
+ async def retry_with_backoff(
45
+ fn: Callable[[], Awaitable[T]],
46
+ # args: P.args,
47
+ # kwargs: P.kwargs,
48
+ retry_config: Optional[RetryConfig] = None,
49
+ on_retry_callback: Optional[Callable[[int, E, float], None]] = None,
50
+ retry_exceptions: tuple[type[E], ...] = (),
51
+ ) -> T:
52
+ """
53
+ Execute a function with exponential backoff retry mechanism.
54
+
55
+ Args:
56
+ fn: The async function to execute with retry
57
+ *args: Arguments to pass to the function
58
+ retry_config: Configuration for the retry mechanism
59
+ on_retry_callback: Optional callback function called on each retry with retry count, exception, and next delay
60
+ retry_exceptions: Tuple of exception types that should trigger a retry
61
+ **kwargs: Keyword arguments to pass to the function
62
+
63
+ Returns:
64
+ The result of the function if successful
65
+
66
+ Raises:
67
+ The last exception encountered if all retries fail
68
+ """
69
+ if retry_config is None:
70
+ retry_config = RetryConfig()
71
+
72
+ last_exception = None
73
+ delay = retry_config.initial_delay
74
+
75
+ for retry_count in range(retry_config.max_retries + 1):
76
+ try:
77
+ return await fn()
78
+ except retry_exceptions as e:
79
+ last_exception = e
80
+
81
+ if retry_count >= retry_config.max_retries:
82
+ logger.error(
83
+ f"Max retries ({retry_config.max_retries}) exceeded: {str(e)}"
84
+ )
85
+ raise
86
+
87
+ # Calculate next delay with exponential backoff
88
+ if retry_count > 0: # Don't increase delay on the first failure
89
+ delay = min(delay * retry_config.backoff_factor, retry_config.max_delay)
90
+
91
+ # Apply jitter if configured (±25% randomness)
92
+ if retry_config.jitter:
93
+ jitter_amount = delay * 0.25
94
+ delay = delay + random.uniform(-jitter_amount, jitter_amount)
95
+ # Ensure delay doesn't go negative due to jitter
96
+ delay = max(delay, 0.1)
97
+
98
+ logger.warning(
99
+ f"Retry {retry_count+1}/{retry_config.max_retries} after error: {str(e)}. "
100
+ f"Retrying in {delay:.2f}s"
101
+ )
102
+
103
+ # Call the optional retry callback if provided
104
+ if on_retry_callback:
105
+ on_retry_callback(retry_count, e, delay)
106
+
107
+ await asyncio.sleep(delay)
108
+
109
+ # This should never be reached with the current implementation
110
+ if last_exception:
111
+ raise last_exception
112
+ raise RuntimeError("Unexpected error in retry logic")
113
+
114
+
115
+ def with_retry(
116
+ retry_config: Optional[RetryConfig] = None,
117
+ retry_exceptions: tuple[type[Exception], ...] = (Exception,),
118
+ ) -> Callable[[Callable[P, Awaitable[T]]], Callable[P, Awaitable[T]]]:
119
+ """
120
+ Decorator to wrap an async function with retry logic.
121
+
122
+ Args:
123
+ retry_config: Configuration for the retry mechanism
124
+ retry_exceptions: Tuple of exception types that should trigger a retry
125
+
126
+ Returns:
127
+ Decorated function with retry mechanism
128
+ """
129
+
130
+ def decorator(fn: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:
131
+ @wraps(fn)
132
+ async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
133
+ return await retry_with_backoff(
134
+ lambda: fn(*args, **kwargs),
135
+ retry_config=retry_config,
136
+ retry_exceptions=retry_exceptions,
137
+ )
138
+
139
+ return wrapper
140
+
141
+ return decorator
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: jararaca
3
- Version: 0.3.11a14
3
+ Version: 0.3.11a16
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=1mFBAWE_eYNc7PpRBe9cHciU6MEJrjjuRCTQZBxYx2U,19901
6
+ jararaca/cli.py,sha256=zkWRcqllY_C0sIR7h_crlptq2cA6sxRM4nvMMLBaANs,25946
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
@@ -20,7 +20,7 @@ jararaca/messagebus/interceptors/aiopika_publisher_interceptor.py,sha256=_DEHwIH
20
20
  jararaca/messagebus/interceptors/publisher_interceptor.py,sha256=ojy1bRhqMgrkQljcGGS8cd8-8pUjL8ZHjIUkdmaAnNM,1325
21
21
  jararaca/messagebus/message.py,sha256=U6cyd2XknX8mtm0333slz5fanky2PFLWCmokAO56vvU,819
22
22
  jararaca/messagebus/publisher.py,sha256=JTkxdKbvxvDWT8nK8PVEyyX061vYYbKQMxRHXrZtcEY,2173
23
- jararaca/messagebus/worker.py,sha256=18oD-6Ip_rOa90p53EcEPlvkXho3SWrC40l4OVSIsE4,22356
23
+ jararaca/messagebus/worker.py,sha256=CrSIejWMGII4_JK0aH4jxdj0oBJX4hSXY0SmVa6KURA,54187
24
24
  jararaca/microservice.py,sha256=rRIimfeP2-wf289PKoUbk9wrSdA0ga_qWz5JNgQ5IE0,9667
25
25
  jararaca/observability/decorators.py,sha256=MOIr2PttPYYvRwEdfQZEwD5RxKHOTv8UEy9n1YQVoKw,2281
26
26
  jararaca/observability/interceptor.py,sha256=U4ZLM0f8j6Q7gMUKKnA85bnvD-Qa0ii79Qa_X8KsXAQ,1498
@@ -66,8 +66,9 @@ jararaca/tools/app_config/interceptor.py,sha256=HV8h4AxqUc_ACs5do4BSVlyxlRXzx7Hq
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=ytdAFUyv-OBkaVnxezuJaJoLrmN7giZgtKeet_IsMBs,10918
69
- jararaca-0.3.11a14.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
70
- jararaca-0.3.11a14.dist-info/METADATA,sha256=QctSa-5oHXPeay_yQplmGs8d_kwMjQS_yLmQrmk4cp4,4998
71
- jararaca-0.3.11a14.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
72
- jararaca-0.3.11a14.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
73
- jararaca-0.3.11a14.dist-info/RECORD,,
69
+ jararaca/utils/retry.py,sha256=DzPX_fXUvTqej6BQ8Mt2dvLo9nNlTBm7Kx2pFZ26P2Q,4668
70
+ jararaca-0.3.11a16.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
71
+ jararaca-0.3.11a16.dist-info/METADATA,sha256=E5OUx4jCVKdki-auNqJUVcOflGaKTt37W3FAtKYS7ow,4998
72
+ jararaca-0.3.11a16.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
73
+ jararaca-0.3.11a16.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
74
+ jararaca-0.3.11a16.dist-info/RECORD,,