ergon-framework-python 0.1.0__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.
- ergon/__init__.py +13 -0
- ergon/bootstrap/src/__project__/__init__.py +0 -0
- ergon/bootstrap/src/__project__/_observability/docker-compose.telemetry.yml +124 -0
- ergon/bootstrap/src/__project__/_observability/grafana.yaml +17 -0
- ergon/bootstrap/src/__project__/_observability/loki.yaml +48 -0
- ergon/bootstrap/src/__project__/_observability/otel-collector-config.yaml +53 -0
- ergon/bootstrap/src/__project__/_observability/prometheus.yaml +11 -0
- ergon/bootstrap/src/__project__/_observability/tempo.yaml +24 -0
- ergon/bootstrap/src/__project__/connectors/__init__.py +0 -0
- ergon/bootstrap/src/__project__/main.py +9 -0
- ergon/bootstrap/src/__project__/tasks/__init__.py +0 -0
- ergon/bootstrap/src/__project__/tasks/constants.py +13 -0
- ergon/bootstrap/src/__project__/tasks/example_task/__init__.py +0 -0
- ergon/bootstrap/src/__project__/tasks/example_task/config.py +4 -0
- ergon/bootstrap/src/__project__/tasks/example_task/exceptions.py +4 -0
- ergon/bootstrap/src/__project__/tasks/example_task/helpers.py +4 -0
- ergon/bootstrap/src/__project__/tasks/example_task/schemas.py +5 -0
- ergon/bootstrap/src/__project__/tasks/example_task/task.py +1 -0
- ergon/bootstrap/src/__project__/tasks/exceptions.py +0 -0
- ergon/bootstrap/src/__project__/tasks/helpers.py +0 -0
- ergon/bootstrap/src/__project__/tasks/schemas.py +0 -0
- ergon/bootstrap/src/__project__/tasks/settings.py +5 -0
- ergon/cli.py +174 -0
- ergon/connector/__init__.py +64 -0
- ergon/connector/connector.py +97 -0
- ergon/connector/excel/__init__.py +18 -0
- ergon/connector/excel/connector.py +175 -0
- ergon/connector/excel/models.py +24 -0
- ergon/connector/excel/service.py +98 -0
- ergon/connector/pipefy/__init__.py +21 -0
- ergon/connector/pipefy/async_connector.py +48 -0
- ergon/connector/pipefy/async_service.py +907 -0
- ergon/connector/pipefy/connector.py +36 -0
- ergon/connector/pipefy/models.py +48 -0
- ergon/connector/pipefy/service.py +1016 -0
- ergon/connector/pipefy/version.py +1 -0
- ergon/connector/postgres/__init__.py +11 -0
- ergon/connector/postgres/async_connector.py +119 -0
- ergon/connector/postgres/async_service.py +116 -0
- ergon/connector/postgres/models.py +34 -0
- ergon/connector/rabbitmq/__init__.py +25 -0
- ergon/connector/rabbitmq/async_connector.py +120 -0
- ergon/connector/rabbitmq/async_service.py +417 -0
- ergon/connector/rabbitmq/connector.py +54 -0
- ergon/connector/rabbitmq/helper.py +14 -0
- ergon/connector/rabbitmq/models.py +92 -0
- ergon/connector/rabbitmq/service.py +199 -0
- ergon/connector/sqs/__init__.py +15 -0
- ergon/connector/sqs/async_connector.py +120 -0
- ergon/connector/sqs/async_service.py +246 -0
- ergon/connector/sqs/connector.py +120 -0
- ergon/connector/sqs/models.py +36 -0
- ergon/connector/sqs/service.py +219 -0
- ergon/connector/transaction.py +14 -0
- ergon/py.typed +0 -0
- ergon/service/__init__.py +5 -0
- ergon/service/service.py +17 -0
- ergon/task/__init__.py +13 -0
- ergon/task/base.py +222 -0
- ergon/task/exceptions.py +217 -0
- ergon/task/helpers.py +691 -0
- ergon/task/manager.py +85 -0
- ergon/task/mixins/__init__.py +13 -0
- ergon/task/mixins/consumer.py +858 -0
- ergon/task/mixins/metrics.py +457 -0
- ergon/task/mixins/producer.py +486 -0
- ergon/task/policies.py +229 -0
- ergon/task/runner.py +386 -0
- ergon/task/utils.py +64 -0
- ergon/telemetry/__init__.py +7 -0
- ergon/telemetry/_resource.py +13 -0
- ergon/telemetry/logging.py +370 -0
- ergon/telemetry/metrics.py +101 -0
- ergon/telemetry/tracing.py +152 -0
- ergon/utils/__init__.py +5 -0
- ergon/utils/env.py +26 -0
- ergon_framework_python-0.1.0.dist-info/METADATA +449 -0
- ergon_framework_python-0.1.0.dist-info/RECORD +82 -0
- ergon_framework_python-0.1.0.dist-info/WHEEL +5 -0
- ergon_framework_python-0.1.0.dist-info/entry_points.txt +2 -0
- ergon_framework_python-0.1.0.dist-info/licenses/LICENSE +21 -0
- ergon_framework_python-0.1.0.dist-info/top_level.txt +1 -0
ergon/task/exceptions.py
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ExceptionType(str, Enum):
|
|
5
|
+
BUSINESS = "BUSINESS"
|
|
6
|
+
SYSTEM = "SYSTEM"
|
|
7
|
+
TIMEOUT = "TIMEOUT"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ConsumerLoopTimeoutException(Exception):
|
|
11
|
+
"""
|
|
12
|
+
Exception raised when a consumer loop times out.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
message: str = "A consumer loop timed out."
|
|
16
|
+
category: ExceptionType = ExceptionType.TIMEOUT
|
|
17
|
+
|
|
18
|
+
def __init__(self, message: str | None = None):
|
|
19
|
+
self.message = message or self.message
|
|
20
|
+
super().__init__(self.message)
|
|
21
|
+
|
|
22
|
+
def __str__(self):
|
|
23
|
+
return self.message
|
|
24
|
+
|
|
25
|
+
def to_dict(self):
|
|
26
|
+
return {
|
|
27
|
+
"message": self.message,
|
|
28
|
+
"category": self.category.value,
|
|
29
|
+
"type": self.__class__.__name__,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ProducerLoopTimeoutException(Exception):
|
|
34
|
+
"""
|
|
35
|
+
Exception raised when a producer loop times out.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
message: str = "A producer loop timed out."
|
|
39
|
+
category: ExceptionType = ExceptionType.TIMEOUT
|
|
40
|
+
|
|
41
|
+
def __init__(self, message: str | None = None):
|
|
42
|
+
self.message = message or self.message
|
|
43
|
+
super().__init__(self.message)
|
|
44
|
+
|
|
45
|
+
def __str__(self):
|
|
46
|
+
return self.message
|
|
47
|
+
|
|
48
|
+
def to_dict(self):
|
|
49
|
+
return {
|
|
50
|
+
"message": self.message,
|
|
51
|
+
"category": self.category.value,
|
|
52
|
+
"type": self.__class__.__name__,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TransactionException(Exception):
|
|
57
|
+
"""
|
|
58
|
+
Exception raised during the processing of a single transaction.
|
|
59
|
+
These errors are caught by the framework and routed to handle_transaction_error().
|
|
60
|
+
|
|
61
|
+
When wrapping an underlying exception, pass it as ``cause`` so the original
|
|
62
|
+
type, message, and traceback are preserved on ``__cause__`` / ``__traceback__``.
|
|
63
|
+
This avoids the failure mode where an upstream exception with an empty
|
|
64
|
+
``str()`` would degrade the wrapped error to the default placeholder message.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
message: str = "A transaction error occurred."
|
|
68
|
+
category: ExceptionType = ExceptionType.SYSTEM
|
|
69
|
+
|
|
70
|
+
def __init__(
|
|
71
|
+
self,
|
|
72
|
+
message: str | None = None,
|
|
73
|
+
category: ExceptionType | None = None,
|
|
74
|
+
transaction_id: str | None = None,
|
|
75
|
+
cause: BaseException | None = None,
|
|
76
|
+
):
|
|
77
|
+
# If no explicit message was supplied but a cause is given, derive a
|
|
78
|
+
# diagnostic message from the cause (using repr to survive empty str()).
|
|
79
|
+
if not message and cause is not None:
|
|
80
|
+
message = repr(cause)
|
|
81
|
+
self.message = message or self.message
|
|
82
|
+
self.category = category or self.category
|
|
83
|
+
self.transaction_id = transaction_id
|
|
84
|
+
self.cause = cause
|
|
85
|
+
super().__init__(self.message)
|
|
86
|
+
if cause is not None:
|
|
87
|
+
self.__cause__ = cause
|
|
88
|
+
if getattr(cause, "__traceback__", None) is not None and self.__traceback__ is None:
|
|
89
|
+
self.__traceback__ = cause.__traceback__
|
|
90
|
+
|
|
91
|
+
def __str__(self):
|
|
92
|
+
prefix = (
|
|
93
|
+
f"({self.category.value} - {self.transaction_id})" if self.transaction_id else f"({self.category.value})"
|
|
94
|
+
)
|
|
95
|
+
if self.cause is not None:
|
|
96
|
+
return f"{prefix} {self.message} [cause: {type(self.cause).__name__}: {self.cause!s}]"
|
|
97
|
+
return f"{prefix} {self.message}"
|
|
98
|
+
|
|
99
|
+
def to_dict(self):
|
|
100
|
+
return {
|
|
101
|
+
"message": self.message,
|
|
102
|
+
"category": self.category.value,
|
|
103
|
+
"type": self.__class__.__name__,
|
|
104
|
+
"transaction_id": self.transaction_id,
|
|
105
|
+
"cause": repr(self.cause) if self.cause is not None else None,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class FetchTimeoutException(Exception):
|
|
110
|
+
"""
|
|
111
|
+
Exception raised when a fetch operation times out.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
message: str = "A fetch operation timed out."
|
|
115
|
+
category: ExceptionType = ExceptionType.TIMEOUT
|
|
116
|
+
|
|
117
|
+
def __init__(self, message: str | None = None):
|
|
118
|
+
self.message = message or self.message
|
|
119
|
+
super().__init__(self.message)
|
|
120
|
+
|
|
121
|
+
def __str__(self):
|
|
122
|
+
return self.message
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class FetchException(Exception):
|
|
126
|
+
"""
|
|
127
|
+
Exception raised when a fetch operation fails.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
message: str = "A fetch operation failed."
|
|
131
|
+
category: ExceptionType = ExceptionType.SYSTEM
|
|
132
|
+
|
|
133
|
+
def __init__(self, message: str | None = None):
|
|
134
|
+
self.message = message or "A fetch operation failed."
|
|
135
|
+
super().__init__(self.message)
|
|
136
|
+
|
|
137
|
+
def __str__(self):
|
|
138
|
+
return self.message
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class NonRetryableException(Exception):
|
|
142
|
+
"""
|
|
143
|
+
Exception raised to explicitly signal that an execution
|
|
144
|
+
must NOT be retried, regardless of retry policy.
|
|
145
|
+
|
|
146
|
+
This exception is execution-scoped, not transaction-scoped.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
message: str = "Execution failed and should not be retried."
|
|
150
|
+
|
|
151
|
+
def __init__(self, message: str | None = None):
|
|
152
|
+
self.message = message or self.message
|
|
153
|
+
super().__init__(self.message)
|
|
154
|
+
|
|
155
|
+
def __str__(self):
|
|
156
|
+
return self.message
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class DeadChannelError(NonRetryableException):
|
|
160
|
+
"""
|
|
161
|
+
Base class for ack/nack failures against a cancelled or closed broker channel.
|
|
162
|
+
|
|
163
|
+
Raised by connector implementations when the underlying message-broker
|
|
164
|
+
channel is no longer usable. The framework's consumer mixin treats these
|
|
165
|
+
as a signal that the broker will redeliver the message and short-circuits
|
|
166
|
+
the lifecycle accordingly: it does NOT route to the exception handler
|
|
167
|
+
(which would typically fail the same way).
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
operation: str = "ack/nack"
|
|
171
|
+
message: str = "Cannot ack/nack: broker channel is closed or cancelled. Broker will redeliver."
|
|
172
|
+
|
|
173
|
+
def __init__(
|
|
174
|
+
self,
|
|
175
|
+
message: str | None = None,
|
|
176
|
+
delivery_tag: int | str | None = None,
|
|
177
|
+
queue: str | None = None,
|
|
178
|
+
cause: BaseException | None = None,
|
|
179
|
+
):
|
|
180
|
+
self.delivery_tag = delivery_tag
|
|
181
|
+
self.queue = queue
|
|
182
|
+
self.cause = cause
|
|
183
|
+
if not message:
|
|
184
|
+
message = (
|
|
185
|
+
f"Cannot {self.operation} delivery_tag={delivery_tag} on queue={queue!r}: "
|
|
186
|
+
"broker channel is closed or cancelled. Broker will redeliver."
|
|
187
|
+
)
|
|
188
|
+
super().__init__(message)
|
|
189
|
+
if cause is not None:
|
|
190
|
+
self.__cause__ = cause
|
|
191
|
+
|
|
192
|
+
def __str__(self):
|
|
193
|
+
return self.message
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class AckOnDeadChannelError(DeadChannelError):
|
|
197
|
+
"""
|
|
198
|
+
Raised when ``ack`` is attempted against a closed or cancelled channel.
|
|
199
|
+
|
|
200
|
+
Carries the original delivery tag and queue name for diagnostics. The
|
|
201
|
+
consumer mixin recognises this error and skips the exception handler,
|
|
202
|
+
since the broker will redeliver the message to a fresh consumer.
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
operation = "ack"
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class NackOnDeadChannelError(DeadChannelError):
|
|
209
|
+
"""
|
|
210
|
+
Raised when ``nack`` is attempted against a closed or cancelled channel.
|
|
211
|
+
|
|
212
|
+
Same semantics as :class:`AckOnDeadChannelError` but for negative
|
|
213
|
+
acknowledgements. Allows the consumer mixin to log a single WARNING
|
|
214
|
+
instead of cascading further failures.
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
operation = "nack"
|