dex-python-sdk 0.1.10__tar.gz → 0.2.0__tar.gz

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 (61) hide show
  1. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/PKG-INFO +2 -2
  2. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/README.md +1 -1
  3. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/_grpc_errors.py +52 -2
  4. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/async_client.py +0 -1
  5. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/client.py +0 -1
  6. dex_python_sdk-0.2.0/dex/dexpb/dex_pb2.py +441 -0
  7. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/dexpb/dex_pb2.pyi +2 -4
  8. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/flow_options.py +0 -2
  9. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/pyproject.toml +1 -1
  10. dex_python_sdk-0.1.10/dex/dexpb/dex_pb2.py +0 -441
  11. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/LEGACY_NOTICES.md +0 -0
  12. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/LICENSE +0 -0
  13. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/__init__.py +0 -0
  14. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/_async_value_hydrator.py +0 -0
  15. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/_async_worker_dispatcher.py +0 -0
  16. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/_async_worker_service.py +0 -0
  17. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/_invocation_context.py +0 -0
  18. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/_native.pyi +0 -0
  19. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/_utils.py +0 -0
  20. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/_value_hydrator.py +0 -0
  21. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/_value_mapper.py +0 -0
  22. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/_worker_dispatcher.py +0 -0
  23. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/_worker_service.py +0 -0
  24. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/async_worker.py +0 -0
  25. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/attribute.py +0 -0
  26. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/blob_cache.py +0 -0
  27. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/channel.py +0 -0
  28. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/client_options.py +0 -0
  29. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/codec.py +0 -0
  30. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/condition.py +0 -0
  31. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/context.py +0 -0
  32. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/dexpb/__init__.py +0 -0
  33. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/dexpb/dex_pb2_grpc.py +0 -0
  34. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/flow.py +0 -0
  35. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/flow_config.py +0 -0
  36. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/flow_info.py +0 -0
  37. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/flow_result.py +0 -0
  38. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/py.typed +0 -0
  39. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/runtime_errors.py +0 -0
  40. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/step.py +0 -0
  41. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/step_execution.py +0 -0
  42. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/subflow.py +0 -0
  43. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/timer.py +0 -0
  44. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/wait.py +0 -0
  45. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/worker.py +0 -0
  46. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/dex/worker_options.py +0 -0
  47. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/Cargo.lock +0 -0
  48. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/Cargo.toml +0 -0
  49. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/crates/dex-blob-cache/Cargo.toml +0 -0
  50. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/crates/dex-blob-cache/LICENSE +0 -0
  51. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/crates/dex-blob-cache/src/config.rs +0 -0
  52. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/crates/dex-blob-cache/src/entry.rs +0 -0
  53. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/crates/dex-blob-cache/src/error.rs +0 -0
  54. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/crates/dex-blob-cache/src/format.rs +0 -0
  55. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/crates/dex-blob-cache/src/lib.rs +0 -0
  56. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/crates/dex-blob-cache/src/policy.rs +0 -0
  57. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/crates/dex-blob-cache/src/store.rs +0 -0
  58. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/crates/dex-blob-cache/tests/blob_cache_integration.rs +0 -0
  59. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/crates/dex-blob-cache-python/Cargo.toml +0 -0
  60. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/crates/dex-blob-cache-python/LICENSE +0 -0
  61. {dex_python_sdk-0.1.10 → dex_python_sdk-0.2.0}/sdk-rust/crates/dex-blob-cache-python/src/lib.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dex-python-sdk
3
- Version: 0.1.10
3
+ Version: 0.2.0
4
4
  Requires-Dist: grpcio>=1.83.0
5
5
  Requires-Dist: grpcio-status>=1.83.0
6
6
  Requires-Dist: protobuf>=7.35.1
@@ -193,7 +193,7 @@ options = dex.StartFlowOptions(
193
193
  Register async hooks with `allow_async_handlers=True` and run them with
194
194
  `AsyncWorker`. `FAIL` produces `FlowErrorType.FLOW_TIMEOUT` and permits Flow
195
195
  retry; `CANCEL` cancels without retry. Continue-as-new preserves the deadline,
196
- while retry and cron runs receive a fresh budget. A zero or absent timeout
196
+ while retry runs receive a fresh budget. A zero or absent timeout
197
197
  disables the feature.
198
198
 
199
199
  `Registry` validates every Flow, Step, RPC signature, durable name, lock, and
@@ -177,7 +177,7 @@ options = dex.StartFlowOptions(
177
177
  Register async hooks with `allow_async_handlers=True` and run them with
178
178
  `AsyncWorker`. `FAIL` produces `FlowErrorType.FLOW_TIMEOUT` and permits Flow
179
179
  retry; `CANCEL` cancels without retry. Continue-as-new preserves the deadline,
180
- while retry and cron runs receive a fresh budget. A zero or absent timeout
180
+ while retry runs receive a fresh budget. A zero or absent timeout
181
181
  disables the feature.
182
182
 
183
183
  `Registry` validates every Flow, Step, RPC signature, durable name, lock, and
@@ -8,6 +8,8 @@
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ import traceback
12
+ from dataclasses import dataclass
11
13
  from typing import Any, Literal, cast
12
14
 
13
15
  import grpc
@@ -29,6 +31,28 @@ from dex.runtime_errors import (
29
31
 
30
32
  FlowTargetRequirement = Literal["none", "existing", "active"]
31
33
 
34
+ MAX_WORKER_STACK_TRACE_BYTES = 16 * 1024
35
+ _STACK_TRACE_TRUNCATION_MARKER = b"\n... stack trace truncated by Dex Python SDK ..."
36
+
37
+
38
+ @dataclass(frozen=True)
39
+ class RetryAfterError(Exception):
40
+ """Requests a delay before the next retry while preserving the current failure."""
41
+
42
+ after_seconds: int
43
+ cause: BaseException
44
+
45
+ def __str__(self) -> str:
46
+ return str(self.cause)
47
+
48
+
49
+ def retry_after(after_seconds: int, cause: BaseException) -> RetryAfterError:
50
+ if cause is None:
51
+ raise ValueError("cause is required")
52
+ if after_seconds <= 0:
53
+ raise ValueError("after_seconds must be positive")
54
+ return RetryAfterError(after_seconds=after_seconds, cause=cause)
55
+
32
56
 
33
57
  def translate_rpc_error(
34
58
  error: grpc.RpcError,
@@ -118,11 +142,21 @@ async def async_abort_worker_error(
118
142
 
119
143
 
120
144
  def _worker_error_status(error: BaseException) -> status_pb2.Status:
121
- message = str(error) or type(error).__name__
145
+ retry_after_error: RetryAfterError | None = None
146
+ reported = error
147
+ if isinstance(error, RetryAfterError):
148
+ retry_after_error = error
149
+ reported = error.cause
150
+
151
+ message = str(reported) or type(reported).__name__
152
+ stack_trace_source = error if retry_after_error is not None else reported
122
153
  worker_error = pb.WorkerErrorResponse(
123
154
  detail=message,
124
- error_type=f"{type(error).__module__}.{type(error).__qualname__}",
155
+ error_type=f"{type(reported).__module__}.{type(reported).__qualname__}",
156
+ stack_trace=_worker_stack_trace(stack_trace_source),
125
157
  )
158
+ if retry_after_error is not None:
159
+ worker_error.retry_after_seconds = retry_after_error.after_seconds
126
160
  packed = any_pb2.Any()
127
161
  packed.Pack(worker_error)
128
162
  return status_pb2.Status(
@@ -132,6 +166,22 @@ def _worker_error_status(error: BaseException) -> status_pb2.Status:
132
166
  )
133
167
 
134
168
 
169
+ def _worker_stack_trace(error: BaseException) -> str:
170
+ if error.__traceback__ is None:
171
+ return ""
172
+ lines = traceback.format_exception(type(error), error, error.__traceback__)
173
+ encoded = "".join(lines).encode()
174
+ if len(encoded) <= MAX_WORKER_STACK_TRACE_BYTES:
175
+ return encoded.decode()
176
+ prefix_length = MAX_WORKER_STACK_TRACE_BYTES - len(_STACK_TRACE_TRUNCATION_MARKER)
177
+ while prefix_length > 0 and (encoded[prefix_length] & 0xC0) == 0x80:
178
+ prefix_length -= 1
179
+ return (
180
+ encoded[:prefix_length].decode(errors="replace")
181
+ + _STACK_TRACE_TRUNCATION_MARKER.decode()
182
+ )
183
+
184
+
135
185
  def _map_sub_status(value: int) -> ErrorSubStatus:
136
186
  statuses: dict[int, ErrorSubStatus] = {
137
187
  int(pb.ERROR_SUB_STATUS_UNCATEGORIZED): ErrorSubStatus.UNCATEGORIZED,
@@ -1018,7 +1018,6 @@ class AsyncClient:
1018
1018
  ),
1019
1019
  IdReusePolicy.DISALLOW: pb.ID_REUSE_POLICY_DISALLOW_REUSE,
1020
1020
  }[options.id_reuse_policy],
1021
- cron_schedule=options.cron_schedule or "",
1022
1021
  flow_already_started_options=pb.FlowAlreadyStartedOptions(
1023
1022
  ignore_already_started_error=options.ignore_already_started
1024
1023
  ),
@@ -1019,7 +1019,6 @@ class Client:
1019
1019
  ),
1020
1020
  IdReusePolicy.DISALLOW: pb.ID_REUSE_POLICY_DISALLOW_REUSE,
1021
1021
  }[options.id_reuse_policy],
1022
- cron_schedule=options.cron_schedule or "",
1023
1022
  flow_already_started_options=pb.FlowAlreadyStartedOptions(
1024
1023
  ignore_already_started_error=options.ignore_already_started
1025
1024
  ),