ringo-task-queue 0.1.0.dev6__py3-none-win_amd64.whl → 0.1.0.dev7__py3-none-win_amd64.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.
- ringo_task_queue/__init__.py +75 -71
- ringo_task_queue/_pb.py +154 -154
- ringo_task_queue/_proto/ringo/v1/queue_pb2.py +93 -85
- ringo_task_queue/_proto/ringo/v1/queue_pb2_grpc.py +93 -5
- ringo_task_queue/_version.py +17 -17
- ringo_task_queue/bin/manifest.json +7 -7
- ringo_task_queue/bin/ringo-task-queue-windows-amd64.exe +0 -0
- ringo_task_queue/binary.py +253 -253
- ringo_task_queue/client.py +1198 -1071
- ringo_task_queue/concurrency.py +480 -480
- ringo_task_queue/daemon.py +507 -507
- ringo_task_queue/errors.py +159 -159
- ringo_task_queue/models.py +449 -364
- ringo_task_queue/shared.py +307 -305
- ringo_task_queue/worker.py +1531 -1297
- {ringo_task_queue-0.1.0.dev6.dist-info → ringo_task_queue-0.1.0.dev7.dist-info}/METADATA +24 -6
- ringo_task_queue-0.1.0.dev7.dist-info/RECORD +20 -0
- {ringo_task_queue-0.1.0.dev6.dist-info → ringo_task_queue-0.1.0.dev7.dist-info}/licenses/LICENSE +21 -21
- ringo_task_queue-0.1.0.dev6.dist-info/RECORD +0 -20
- {ringo_task_queue-0.1.0.dev6.dist-info → ringo_task_queue-0.1.0.dev7.dist-info}/WHEEL +0 -0
ringo_task_queue/errors.py
CHANGED
|
@@ -1,159 +1,159 @@
|
|
|
1
|
-
"""Public exception hierarchy for the Ringo Task Queue Python SDK.
|
|
2
|
-
|
|
3
|
-
All SDK transport failures derive from :class:`RingoError` and carry one of
|
|
4
|
-
the stable public codes from ``docs/contracts/errors.md``. gRPC status
|
|
5
|
-
details (``ringo.v1.ErrorDetail``) are preferred over the coarser gRPC code,
|
|
6
|
-
exactly as the error contract requires.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
|
|
11
|
-
from typing import Mapping
|
|
12
|
-
|
|
13
|
-
import grpc
|
|
14
|
-
from grpc_status import rpc_status
|
|
15
|
-
|
|
16
|
-
from ._proto.ringo.v1 import queue_pb2
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class RingoError(Exception):
|
|
20
|
-
"""Base class for every SDK-raised transport/protocol error."""
|
|
21
|
-
|
|
22
|
-
#: Stable public code (see docs/contracts/errors.md).
|
|
23
|
-
code = "INTERNAL"
|
|
24
|
-
|
|
25
|
-
def __init__(
|
|
26
|
-
self,
|
|
27
|
-
message: str = "",
|
|
28
|
-
*,
|
|
29
|
-
request_id: str | None = None,
|
|
30
|
-
metadata: Mapping[str, str] | None = None,
|
|
31
|
-
grpc_code: grpc.StatusCode | None = None,
|
|
32
|
-
) -> None:
|
|
33
|
-
super().__init__(message)
|
|
34
|
-
self.message = message
|
|
35
|
-
self.request_id = request_id
|
|
36
|
-
self.metadata = dict(metadata) if metadata else {}
|
|
37
|
-
self.grpc_code = grpc_code
|
|
38
|
-
|
|
39
|
-
def __repr__(self) -> str: # pragma: no cover - debugging aid
|
|
40
|
-
return f"{type(self).__name__}(code={self.code!r}, message={self.message!r})"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class InvalidArgumentError(RingoError):
|
|
44
|
-
code = "INVALID_ARGUMENT"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class DuplicateError(RingoError):
|
|
48
|
-
code = "DUPLICATE"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class NotFoundError(RingoError):
|
|
52
|
-
code = "NOT_FOUND"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class ConflictError(RingoError):
|
|
56
|
-
code = "CONFLICT"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
class LeaseLostError(RingoError):
|
|
60
|
-
code = "LEASE_LOST"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class UnavailableError(RingoError):
|
|
64
|
-
code = "UNAVAILABLE"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
class DeadlineExceededError(RingoError):
|
|
68
|
-
code = "DEADLINE_EXCEEDED"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class IncompatibleVersionError(RingoError):
|
|
72
|
-
code = "INCOMPATIBLE_VERSION"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class InternalError(RingoError):
|
|
76
|
-
code = "INTERNAL"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
_CODE_TO_ERROR: dict[int, type[RingoError]] = {
|
|
80
|
-
queue_pb2.ERROR_CODE_INVALID_ARGUMENT: InvalidArgumentError,
|
|
81
|
-
queue_pb2.ERROR_CODE_DUPLICATE: DuplicateError,
|
|
82
|
-
queue_pb2.ERROR_CODE_NOT_FOUND: NotFoundError,
|
|
83
|
-
queue_pb2.ERROR_CODE_CONFLICT: ConflictError,
|
|
84
|
-
queue_pb2.ERROR_CODE_LEASE_LOST: LeaseLostError,
|
|
85
|
-
queue_pb2.ERROR_CODE_UNAVAILABLE: UnavailableError,
|
|
86
|
-
queue_pb2.ERROR_CODE_DEADLINE_EXCEEDED: DeadlineExceededError,
|
|
87
|
-
queue_pb2.ERROR_CODE_INCOMPATIBLE_VERSION: IncompatibleVersionError,
|
|
88
|
-
queue_pb2.ERROR_CODE_INTERNAL: InternalError,
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
# gRPC codes that map unambiguously onto a single public Ringo code. Other
|
|
92
|
-
# gRPC codes raise the base RingoError without inventing a more specific
|
|
93
|
-
# public code (docs/contracts/errors.md).
|
|
94
|
-
_GRPC_FALLBACK: dict[grpc.StatusCode, type[RingoError]] = {
|
|
95
|
-
grpc.StatusCode.INVALID_ARGUMENT: InvalidArgumentError,
|
|
96
|
-
grpc.StatusCode.NOT_FOUND: NotFoundError,
|
|
97
|
-
grpc.StatusCode.ALREADY_EXISTS: DuplicateError,
|
|
98
|
-
grpc.StatusCode.ABORTED: LeaseLostError,
|
|
99
|
-
grpc.StatusCode.UNAVAILABLE: UnavailableError,
|
|
100
|
-
grpc.StatusCode.DEADLINE_EXCEEDED: DeadlineExceededError,
|
|
101
|
-
grpc.StatusCode.INTERNAL: InternalError,
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def error_from_detail(detail: queue_pb2.ErrorDetail) -> RingoError:
|
|
106
|
-
"""Build the mapped exception from a ``ringo.v1.ErrorDetail`` message."""
|
|
107
|
-
cls = _CODE_TO_ERROR.get(detail.code, RingoError)
|
|
108
|
-
return cls(
|
|
109
|
-
detail.message,
|
|
110
|
-
request_id=detail.request_id or None,
|
|
111
|
-
metadata=detail.metadata,
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def map_rpc_error(exc: grpc.aio.AioRpcError) -> RingoError:
|
|
116
|
-
"""Map a gRPC call failure to the stable public exception.
|
|
117
|
-
|
|
118
|
-
Prefers the ``ringo.v1.ErrorDetail`` serialized in ``google.rpc.Status``
|
|
119
|
-
details; falls back to the gRPC code only when details are unavailable.
|
|
120
|
-
"""
|
|
121
|
-
rich = None
|
|
122
|
-
try:
|
|
123
|
-
rich = rpc_status.from_call(exc)
|
|
124
|
-
except Exception:
|
|
125
|
-
rich = None
|
|
126
|
-
if rich is not None:
|
|
127
|
-
for any_msg in rich.details:
|
|
128
|
-
if any_msg.Is(queue_pb2.ErrorDetail.DESCRIPTOR):
|
|
129
|
-
detail = queue_pb2.ErrorDetail()
|
|
130
|
-
any_msg.Unpack(detail)
|
|
131
|
-
err = error_from_detail(detail)
|
|
132
|
-
err.grpc_code = exc.code()
|
|
133
|
-
return err
|
|
134
|
-
cls = _GRPC_FALLBACK.get(exc.code(), RingoError)
|
|
135
|
-
message = exc.details() or str(exc)
|
|
136
|
-
return cls(message, grpc_code=exc.code())
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
# ---------------------------------------------------------------------------
|
|
140
|
-
# Handler signalling errors (execution outcomes, not transport errors).
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
class RetryTaskError(Exception):
|
|
144
|
-
"""Raise from a handler to retry with an optional per-attempt delay."""
|
|
145
|
-
|
|
146
|
-
def __init__(
|
|
147
|
-
self,
|
|
148
|
-
message: str = "",
|
|
149
|
-
*,
|
|
150
|
-
delay: float | None = None,
|
|
151
|
-
congestion: bool = False,
|
|
152
|
-
) -> None:
|
|
153
|
-
super().__init__(message)
|
|
154
|
-
self.delay = delay
|
|
155
|
-
self.congestion = congestion
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
class PermanentTaskError(Exception):
|
|
159
|
-
"""Raise from a handler to mark the task permanently failed (DEAD)."""
|
|
1
|
+
"""Public exception hierarchy for the Ringo Task Queue Python SDK.
|
|
2
|
+
|
|
3
|
+
All SDK transport failures derive from :class:`RingoError` and carry one of
|
|
4
|
+
the stable public codes from ``docs/contracts/errors.md``. gRPC status
|
|
5
|
+
details (``ringo.v1.ErrorDetail``) are preferred over the coarser gRPC code,
|
|
6
|
+
exactly as the error contract requires.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Mapping
|
|
12
|
+
|
|
13
|
+
import grpc
|
|
14
|
+
from grpc_status import rpc_status
|
|
15
|
+
|
|
16
|
+
from ._proto.ringo.v1 import queue_pb2
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RingoError(Exception):
|
|
20
|
+
"""Base class for every SDK-raised transport/protocol error."""
|
|
21
|
+
|
|
22
|
+
#: Stable public code (see docs/contracts/errors.md).
|
|
23
|
+
code = "INTERNAL"
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
message: str = "",
|
|
28
|
+
*,
|
|
29
|
+
request_id: str | None = None,
|
|
30
|
+
metadata: Mapping[str, str] | None = None,
|
|
31
|
+
grpc_code: grpc.StatusCode | None = None,
|
|
32
|
+
) -> None:
|
|
33
|
+
super().__init__(message)
|
|
34
|
+
self.message = message
|
|
35
|
+
self.request_id = request_id
|
|
36
|
+
self.metadata = dict(metadata) if metadata else {}
|
|
37
|
+
self.grpc_code = grpc_code
|
|
38
|
+
|
|
39
|
+
def __repr__(self) -> str: # pragma: no cover - debugging aid
|
|
40
|
+
return f"{type(self).__name__}(code={self.code!r}, message={self.message!r})"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class InvalidArgumentError(RingoError):
|
|
44
|
+
code = "INVALID_ARGUMENT"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class DuplicateError(RingoError):
|
|
48
|
+
code = "DUPLICATE"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class NotFoundError(RingoError):
|
|
52
|
+
code = "NOT_FOUND"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ConflictError(RingoError):
|
|
56
|
+
code = "CONFLICT"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class LeaseLostError(RingoError):
|
|
60
|
+
code = "LEASE_LOST"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class UnavailableError(RingoError):
|
|
64
|
+
code = "UNAVAILABLE"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class DeadlineExceededError(RingoError):
|
|
68
|
+
code = "DEADLINE_EXCEEDED"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class IncompatibleVersionError(RingoError):
|
|
72
|
+
code = "INCOMPATIBLE_VERSION"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class InternalError(RingoError):
|
|
76
|
+
code = "INTERNAL"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
_CODE_TO_ERROR: dict[int, type[RingoError]] = {
|
|
80
|
+
queue_pb2.ERROR_CODE_INVALID_ARGUMENT: InvalidArgumentError,
|
|
81
|
+
queue_pb2.ERROR_CODE_DUPLICATE: DuplicateError,
|
|
82
|
+
queue_pb2.ERROR_CODE_NOT_FOUND: NotFoundError,
|
|
83
|
+
queue_pb2.ERROR_CODE_CONFLICT: ConflictError,
|
|
84
|
+
queue_pb2.ERROR_CODE_LEASE_LOST: LeaseLostError,
|
|
85
|
+
queue_pb2.ERROR_CODE_UNAVAILABLE: UnavailableError,
|
|
86
|
+
queue_pb2.ERROR_CODE_DEADLINE_EXCEEDED: DeadlineExceededError,
|
|
87
|
+
queue_pb2.ERROR_CODE_INCOMPATIBLE_VERSION: IncompatibleVersionError,
|
|
88
|
+
queue_pb2.ERROR_CODE_INTERNAL: InternalError,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# gRPC codes that map unambiguously onto a single public Ringo code. Other
|
|
92
|
+
# gRPC codes raise the base RingoError without inventing a more specific
|
|
93
|
+
# public code (docs/contracts/errors.md).
|
|
94
|
+
_GRPC_FALLBACK: dict[grpc.StatusCode, type[RingoError]] = {
|
|
95
|
+
grpc.StatusCode.INVALID_ARGUMENT: InvalidArgumentError,
|
|
96
|
+
grpc.StatusCode.NOT_FOUND: NotFoundError,
|
|
97
|
+
grpc.StatusCode.ALREADY_EXISTS: DuplicateError,
|
|
98
|
+
grpc.StatusCode.ABORTED: LeaseLostError,
|
|
99
|
+
grpc.StatusCode.UNAVAILABLE: UnavailableError,
|
|
100
|
+
grpc.StatusCode.DEADLINE_EXCEEDED: DeadlineExceededError,
|
|
101
|
+
grpc.StatusCode.INTERNAL: InternalError,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def error_from_detail(detail: queue_pb2.ErrorDetail) -> RingoError:
|
|
106
|
+
"""Build the mapped exception from a ``ringo.v1.ErrorDetail`` message."""
|
|
107
|
+
cls = _CODE_TO_ERROR.get(detail.code, RingoError)
|
|
108
|
+
return cls(
|
|
109
|
+
detail.message,
|
|
110
|
+
request_id=detail.request_id or None,
|
|
111
|
+
metadata=detail.metadata,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def map_rpc_error(exc: grpc.aio.AioRpcError) -> RingoError:
|
|
116
|
+
"""Map a gRPC call failure to the stable public exception.
|
|
117
|
+
|
|
118
|
+
Prefers the ``ringo.v1.ErrorDetail`` serialized in ``google.rpc.Status``
|
|
119
|
+
details; falls back to the gRPC code only when details are unavailable.
|
|
120
|
+
"""
|
|
121
|
+
rich = None
|
|
122
|
+
try:
|
|
123
|
+
rich = rpc_status.from_call(exc)
|
|
124
|
+
except Exception:
|
|
125
|
+
rich = None
|
|
126
|
+
if rich is not None:
|
|
127
|
+
for any_msg in rich.details:
|
|
128
|
+
if any_msg.Is(queue_pb2.ErrorDetail.DESCRIPTOR):
|
|
129
|
+
detail = queue_pb2.ErrorDetail()
|
|
130
|
+
any_msg.Unpack(detail)
|
|
131
|
+
err = error_from_detail(detail)
|
|
132
|
+
err.grpc_code = exc.code()
|
|
133
|
+
return err
|
|
134
|
+
cls = _GRPC_FALLBACK.get(exc.code(), RingoError)
|
|
135
|
+
message = exc.details() or str(exc)
|
|
136
|
+
return cls(message, grpc_code=exc.code())
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# ---------------------------------------------------------------------------
|
|
140
|
+
# Handler signalling errors (execution outcomes, not transport errors).
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class RetryTaskError(Exception):
|
|
144
|
+
"""Raise from a handler to retry with an optional per-attempt delay."""
|
|
145
|
+
|
|
146
|
+
def __init__(
|
|
147
|
+
self,
|
|
148
|
+
message: str = "",
|
|
149
|
+
*,
|
|
150
|
+
delay: float | None = None,
|
|
151
|
+
congestion: bool = False,
|
|
152
|
+
) -> None:
|
|
153
|
+
super().__init__(message)
|
|
154
|
+
self.delay = delay
|
|
155
|
+
self.congestion = congestion
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class PermanentTaskError(Exception):
|
|
159
|
+
"""Raise from a handler to mark the task permanently failed (DEAD)."""
|