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.
Files changed (82) hide show
  1. ergon/__init__.py +13 -0
  2. ergon/bootstrap/src/__project__/__init__.py +0 -0
  3. ergon/bootstrap/src/__project__/_observability/docker-compose.telemetry.yml +124 -0
  4. ergon/bootstrap/src/__project__/_observability/grafana.yaml +17 -0
  5. ergon/bootstrap/src/__project__/_observability/loki.yaml +48 -0
  6. ergon/bootstrap/src/__project__/_observability/otel-collector-config.yaml +53 -0
  7. ergon/bootstrap/src/__project__/_observability/prometheus.yaml +11 -0
  8. ergon/bootstrap/src/__project__/_observability/tempo.yaml +24 -0
  9. ergon/bootstrap/src/__project__/connectors/__init__.py +0 -0
  10. ergon/bootstrap/src/__project__/main.py +9 -0
  11. ergon/bootstrap/src/__project__/tasks/__init__.py +0 -0
  12. ergon/bootstrap/src/__project__/tasks/constants.py +13 -0
  13. ergon/bootstrap/src/__project__/tasks/example_task/__init__.py +0 -0
  14. ergon/bootstrap/src/__project__/tasks/example_task/config.py +4 -0
  15. ergon/bootstrap/src/__project__/tasks/example_task/exceptions.py +4 -0
  16. ergon/bootstrap/src/__project__/tasks/example_task/helpers.py +4 -0
  17. ergon/bootstrap/src/__project__/tasks/example_task/schemas.py +5 -0
  18. ergon/bootstrap/src/__project__/tasks/example_task/task.py +1 -0
  19. ergon/bootstrap/src/__project__/tasks/exceptions.py +0 -0
  20. ergon/bootstrap/src/__project__/tasks/helpers.py +0 -0
  21. ergon/bootstrap/src/__project__/tasks/schemas.py +0 -0
  22. ergon/bootstrap/src/__project__/tasks/settings.py +5 -0
  23. ergon/cli.py +174 -0
  24. ergon/connector/__init__.py +64 -0
  25. ergon/connector/connector.py +97 -0
  26. ergon/connector/excel/__init__.py +18 -0
  27. ergon/connector/excel/connector.py +175 -0
  28. ergon/connector/excel/models.py +24 -0
  29. ergon/connector/excel/service.py +98 -0
  30. ergon/connector/pipefy/__init__.py +21 -0
  31. ergon/connector/pipefy/async_connector.py +48 -0
  32. ergon/connector/pipefy/async_service.py +907 -0
  33. ergon/connector/pipefy/connector.py +36 -0
  34. ergon/connector/pipefy/models.py +48 -0
  35. ergon/connector/pipefy/service.py +1016 -0
  36. ergon/connector/pipefy/version.py +1 -0
  37. ergon/connector/postgres/__init__.py +11 -0
  38. ergon/connector/postgres/async_connector.py +119 -0
  39. ergon/connector/postgres/async_service.py +116 -0
  40. ergon/connector/postgres/models.py +34 -0
  41. ergon/connector/rabbitmq/__init__.py +25 -0
  42. ergon/connector/rabbitmq/async_connector.py +120 -0
  43. ergon/connector/rabbitmq/async_service.py +417 -0
  44. ergon/connector/rabbitmq/connector.py +54 -0
  45. ergon/connector/rabbitmq/helper.py +14 -0
  46. ergon/connector/rabbitmq/models.py +92 -0
  47. ergon/connector/rabbitmq/service.py +199 -0
  48. ergon/connector/sqs/__init__.py +15 -0
  49. ergon/connector/sqs/async_connector.py +120 -0
  50. ergon/connector/sqs/async_service.py +246 -0
  51. ergon/connector/sqs/connector.py +120 -0
  52. ergon/connector/sqs/models.py +36 -0
  53. ergon/connector/sqs/service.py +219 -0
  54. ergon/connector/transaction.py +14 -0
  55. ergon/py.typed +0 -0
  56. ergon/service/__init__.py +5 -0
  57. ergon/service/service.py +17 -0
  58. ergon/task/__init__.py +13 -0
  59. ergon/task/base.py +222 -0
  60. ergon/task/exceptions.py +217 -0
  61. ergon/task/helpers.py +691 -0
  62. ergon/task/manager.py +85 -0
  63. ergon/task/mixins/__init__.py +13 -0
  64. ergon/task/mixins/consumer.py +858 -0
  65. ergon/task/mixins/metrics.py +457 -0
  66. ergon/task/mixins/producer.py +486 -0
  67. ergon/task/policies.py +229 -0
  68. ergon/task/runner.py +386 -0
  69. ergon/task/utils.py +64 -0
  70. ergon/telemetry/__init__.py +7 -0
  71. ergon/telemetry/_resource.py +13 -0
  72. ergon/telemetry/logging.py +370 -0
  73. ergon/telemetry/metrics.py +101 -0
  74. ergon/telemetry/tracing.py +152 -0
  75. ergon/utils/__init__.py +5 -0
  76. ergon/utils/env.py +26 -0
  77. ergon_framework_python-0.1.0.dist-info/METADATA +449 -0
  78. ergon_framework_python-0.1.0.dist-info/RECORD +82 -0
  79. ergon_framework_python-0.1.0.dist-info/WHEEL +5 -0
  80. ergon_framework_python-0.1.0.dist-info/entry_points.txt +2 -0
  81. ergon_framework_python-0.1.0.dist-info/licenses/LICENSE +21 -0
  82. ergon_framework_python-0.1.0.dist-info/top_level.txt +1 -0
@@ -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"