dex-python-sdk 0.5.0__tar.gz → 0.7.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.
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/PKG-INFO +28 -9
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/README.md +27 -8
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/__init__.py +8 -3
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/_grpc_errors.py +38 -11
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/async_client.py +90 -440
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/client.py +91 -440
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/dexpb/dex_pb2.py +22 -22
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/dexpb/dex_pb2.pyi +2 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/dexpb/dex_pb2_grpc.py +18 -18
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/runtime_errors.py +8 -0
- dex_python_sdk-0.7.0/dex/wait_options.py +76 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/pyproject.toml +1 -1
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/Cargo.lock +6 -6
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/Cargo.toml +3 -3
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/LEGACY_NOTICES.md +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/LICENSE +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/_async_value_hydrator.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/_async_worker_dispatcher.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/_async_worker_service.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/_invocation_context.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/_native.pyi +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/_utils.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/_value_hydrator.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/_value_mapper.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/_worker_dispatcher.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/_worker_service.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/async_worker.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/attribute.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/attribute_match.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/blob_cache.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/channel.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/client_options.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/codec.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/condition.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/context.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/dexpb/__init__.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/flow.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/flow_config.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/flow_info.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/flow_options.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/flow_result.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/py.typed +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/step.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/step_execution.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/stream.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/subflow.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/timer.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/wait.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/worker.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/dex/worker_options.py +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/crates/dex-blob-cache/Cargo.toml +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/crates/dex-blob-cache/LICENSE +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/crates/dex-blob-cache/src/config.rs +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/crates/dex-blob-cache/src/entry.rs +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/crates/dex-blob-cache/src/error.rs +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/crates/dex-blob-cache/src/format.rs +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/crates/dex-blob-cache/src/lib.rs +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/crates/dex-blob-cache/src/policy.rs +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/crates/dex-blob-cache/src/store.rs +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/crates/dex-blob-cache/tests/blob_cache_integration.rs +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/crates/dex-blob-cache-python/Cargo.toml +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.0}/sdk-rust/crates/dex-blob-cache-python/LICENSE +0 -0
- {dex_python_sdk-0.5.0 → dex_python_sdk-0.7.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.
|
|
3
|
+
Version: 0.7.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
|
|
@@ -17,13 +17,12 @@ Project-URL: Repository, https://github.com/superdurable/dex
|
|
|
17
17
|
|
|
18
18
|
# Dex SDK for Python
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## Flow state I/O
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
deletion.
|
|
22
|
+
Applications read and write Flow state through typed RPCs. Client and AsyncClient
|
|
23
|
+
do not expose direct Attribute reads or writes, Channel publication, or
|
|
24
|
+
pending-message mutation. This keeps each external state transition behind a
|
|
25
|
+
Flow-owned method.
|
|
27
26
|
|
|
28
27
|
RPC handlers can stage `channel.delete(context, message_id)`. Declare the RPC as
|
|
29
28
|
`@rpc(is_transactional=True)` when a missing message must abort its other writes.
|
|
@@ -279,6 +278,15 @@ message = client.read_stream(flow_id, progress)
|
|
|
279
278
|
print(message.source)
|
|
280
279
|
```
|
|
281
280
|
|
|
281
|
+
`Client.read_stream` and `AsyncClient.read_stream` move forward one message at
|
|
282
|
+
a time and can long-poll. Their `list_stream_messages` methods return one
|
|
283
|
+
non-blocking newest-first retained page. Pass the registered `Stream`, a
|
|
284
|
+
positive page size, and an empty token for the first page. Pass
|
|
285
|
+
`StreamMessagesPage.next_page_token` unchanged to read older messages until it
|
|
286
|
+
is empty. The server caps page size at 1000 by default. A trimmed anchor returns
|
|
287
|
+
an empty page, and newer concurrent writes do not enter an existing older-page
|
|
288
|
+
chain.
|
|
289
|
+
|
|
282
290
|
### Canceling Step executions
|
|
283
291
|
|
|
284
292
|
A successful Step can cancel queued or active executions while continuing with
|
|
@@ -353,6 +361,17 @@ buffered sets and deletes. The matching `ChannelMap` methods are RPC-only,
|
|
|
353
361
|
include buffered publishes, and omit empty instances. Keys are decoded and
|
|
354
362
|
sorted. Use `force_complete_if_channels_empty(...)` for conditional completion.
|
|
355
363
|
|
|
364
|
+
Request IDs are optional for both durable waits. When omitted, the server
|
|
365
|
+
derives a namespaced stable ID from the Step execution or Attribute condition,
|
|
366
|
+
such as `wait-for-attribute:myInt>10`. Reuse an override only for the same
|
|
367
|
+
logical wait. The Client automatically reattaches transport long polls. If an
|
|
368
|
+
earlier Update with that ID exhausted its handler budget, the server appends an
|
|
369
|
+
increasing `-N` suffix and starts a new Update. `maximum_wait_time` is optional
|
|
370
|
+
and is the total handler budget across reattachments; zero waits indefinitely.
|
|
371
|
+
A positive budget expiry raises `WaitHandlerTimeoutError`. An abandoned infinite
|
|
372
|
+
wait remains accepted and counts against Temporal's in-flight Update limit until
|
|
373
|
+
it completes or the Flow closes.
|
|
374
|
+
|
|
356
375
|
`Client.wait_for_flow` and `AsyncClient.wait_for_flow` return a
|
|
357
376
|
`FlowResult` after hydrating every output-bearing completion. Use
|
|
358
377
|
`single_output` only when the Flow contract produces exactly one output:
|
|
@@ -392,14 +411,14 @@ SubFlow.
|
|
|
392
411
|
### Errors
|
|
393
412
|
|
|
394
413
|
Client calls raise concrete `DexServiceError` subclasses. Existing-Flow reads
|
|
395
|
-
(`
|
|
414
|
+
(`describe_flow`, `wait_for_flow`, and `time_travel`) raise
|
|
396
415
|
`FlowNotFoundError` when the Flow does not exist. Mutations, RPCs, timer/Step
|
|
397
416
|
waits, config updates, and continue-as-new triggers raise
|
|
398
417
|
`FlowNotActiveError` when no running Flow can accept the operation.
|
|
399
418
|
|
|
400
419
|
```python
|
|
401
420
|
try:
|
|
402
|
-
client.
|
|
421
|
+
client.invoke_rpc(orders.update_order, order_id, update)
|
|
403
422
|
except dex.FlowNotActiveError:
|
|
404
423
|
# The Flow is missing or already closed.
|
|
405
424
|
pass
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
|
|
2
2
|
# Dex SDK for Python
|
|
3
3
|
|
|
4
|
-
##
|
|
4
|
+
## Flow state I/O
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
deletion.
|
|
6
|
+
Applications read and write Flow state through typed RPCs. Client and AsyncClient
|
|
7
|
+
do not expose direct Attribute reads or writes, Channel publication, or
|
|
8
|
+
pending-message mutation. This keeps each external state transition behind a
|
|
9
|
+
Flow-owned method.
|
|
11
10
|
|
|
12
11
|
RPC handlers can stage `channel.delete(context, message_id)`. Declare the RPC as
|
|
13
12
|
`@rpc(is_transactional=True)` when a missing message must abort its other writes.
|
|
@@ -263,6 +262,15 @@ message = client.read_stream(flow_id, progress)
|
|
|
263
262
|
print(message.source)
|
|
264
263
|
```
|
|
265
264
|
|
|
265
|
+
`Client.read_stream` and `AsyncClient.read_stream` move forward one message at
|
|
266
|
+
a time and can long-poll. Their `list_stream_messages` methods return one
|
|
267
|
+
non-blocking newest-first retained page. Pass the registered `Stream`, a
|
|
268
|
+
positive page size, and an empty token for the first page. Pass
|
|
269
|
+
`StreamMessagesPage.next_page_token` unchanged to read older messages until it
|
|
270
|
+
is empty. The server caps page size at 1000 by default. A trimmed anchor returns
|
|
271
|
+
an empty page, and newer concurrent writes do not enter an existing older-page
|
|
272
|
+
chain.
|
|
273
|
+
|
|
266
274
|
### Canceling Step executions
|
|
267
275
|
|
|
268
276
|
A successful Step can cancel queued or active executions while continuing with
|
|
@@ -337,6 +345,17 @@ buffered sets and deletes. The matching `ChannelMap` methods are RPC-only,
|
|
|
337
345
|
include buffered publishes, and omit empty instances. Keys are decoded and
|
|
338
346
|
sorted. Use `force_complete_if_channels_empty(...)` for conditional completion.
|
|
339
347
|
|
|
348
|
+
Request IDs are optional for both durable waits. When omitted, the server
|
|
349
|
+
derives a namespaced stable ID from the Step execution or Attribute condition,
|
|
350
|
+
such as `wait-for-attribute:myInt>10`. Reuse an override only for the same
|
|
351
|
+
logical wait. The Client automatically reattaches transport long polls. If an
|
|
352
|
+
earlier Update with that ID exhausted its handler budget, the server appends an
|
|
353
|
+
increasing `-N` suffix and starts a new Update. `maximum_wait_time` is optional
|
|
354
|
+
and is the total handler budget across reattachments; zero waits indefinitely.
|
|
355
|
+
A positive budget expiry raises `WaitHandlerTimeoutError`. An abandoned infinite
|
|
356
|
+
wait remains accepted and counts against Temporal's in-flight Update limit until
|
|
357
|
+
it completes or the Flow closes.
|
|
358
|
+
|
|
340
359
|
`Client.wait_for_flow` and `AsyncClient.wait_for_flow` return a
|
|
341
360
|
`FlowResult` after hydrating every output-bearing completion. Use
|
|
342
361
|
`single_output` only when the Flow contract produces exactly one output:
|
|
@@ -376,14 +395,14 @@ SubFlow.
|
|
|
376
395
|
### Errors
|
|
377
396
|
|
|
378
397
|
Client calls raise concrete `DexServiceError` subclasses. Existing-Flow reads
|
|
379
|
-
(`
|
|
398
|
+
(`describe_flow`, `wait_for_flow`, and `time_travel`) raise
|
|
380
399
|
`FlowNotFoundError` when the Flow does not exist. Mutations, RPCs, timer/Step
|
|
381
400
|
waits, config updates, and continue-as-new triggers raise
|
|
382
401
|
`FlowNotActiveError` when no running Flow can accept the operation.
|
|
383
402
|
|
|
384
403
|
```python
|
|
385
404
|
try:
|
|
386
|
-
client.
|
|
405
|
+
client.invoke_rpc(orders.update_order, order_id, update)
|
|
387
406
|
except dex.FlowNotActiveError:
|
|
388
407
|
# The Flow is missing or already closed.
|
|
389
408
|
pass
|
|
@@ -62,10 +62,11 @@ from dex.flow_options import (
|
|
|
62
62
|
)
|
|
63
63
|
from dex.flow_result import FlowResult, StepCompletion
|
|
64
64
|
from dex.runtime_errors import (
|
|
65
|
-
DexServiceError,
|
|
66
|
-
ErrorSubStatus,
|
|
67
65
|
AttributeMapNotLoadedError,
|
|
66
|
+
ChannelMessageNotFoundError,
|
|
68
67
|
ChannelMessagesNotLoadedError,
|
|
68
|
+
DexServiceError,
|
|
69
|
+
ErrorSubStatus,
|
|
69
70
|
FlowAlreadyStartedError,
|
|
70
71
|
FlowDefinitionError,
|
|
71
72
|
FlowErrorType,
|
|
@@ -73,9 +74,9 @@ from dex.runtime_errors import (
|
|
|
73
74
|
FlowNotFoundError,
|
|
74
75
|
InvalidStepResultError,
|
|
75
76
|
LongPollTimeoutError,
|
|
76
|
-
ChannelMessageNotFoundError,
|
|
77
77
|
RpcLockConflictError,
|
|
78
78
|
ValueMappingError,
|
|
79
|
+
WaitHandlerTimeoutError,
|
|
79
80
|
WorkerInvocationError,
|
|
80
81
|
)
|
|
81
82
|
from dex.step import (
|
|
@@ -108,6 +109,7 @@ from dex.stream import (
|
|
|
108
109
|
from dex.subflow import SubFlow
|
|
109
110
|
from dex.timer import Timer
|
|
110
111
|
from dex.wait import Wait
|
|
112
|
+
from dex.wait_options import WaitForAttributeOptions, WaitForStepCompletionOptions
|
|
111
113
|
from dex.worker import Worker
|
|
112
114
|
from dex.worker_options import WorkerOptions, WorkerTarget
|
|
113
115
|
|
|
@@ -164,6 +166,7 @@ __all__ = [
|
|
|
164
166
|
"IndexType",
|
|
165
167
|
"JsonCodec",
|
|
166
168
|
"LongPollTimeoutError",
|
|
169
|
+
"WaitHandlerTimeoutError",
|
|
167
170
|
"PersistenceSchema",
|
|
168
171
|
"RPCResult",
|
|
169
172
|
"RecoveryErrorInfo",
|
|
@@ -205,6 +208,8 @@ __all__ = [
|
|
|
205
208
|
"WorkerInvocationError",
|
|
206
209
|
"WorkerOptions",
|
|
207
210
|
"WorkerTarget",
|
|
211
|
+
"WaitForAttributeOptions",
|
|
212
|
+
"WaitForStepCompletionOptions",
|
|
208
213
|
"dead_end",
|
|
209
214
|
"force_complete",
|
|
210
215
|
"force_complete_if_channels_empty",
|
|
@@ -27,12 +27,17 @@ from dex.runtime_errors import (
|
|
|
27
27
|
FlowNotFoundError,
|
|
28
28
|
LongPollTimeoutError,
|
|
29
29
|
RpcLockConflictError,
|
|
30
|
+
WaitHandlerTimeoutError,
|
|
30
31
|
WorkerInvocationError,
|
|
31
32
|
)
|
|
32
33
|
|
|
33
34
|
FlowTargetRequirement = Literal["none", "existing", "active"]
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
MAX_WORKER_ERROR_DETAIL_BYTES = 1024
|
|
37
|
+
MAX_WORKER_ERROR_TYPE_BYTES = 256
|
|
38
|
+
MAX_WORKER_STACK_TRACE_BYTES = 4 * 1024
|
|
39
|
+
_ERROR_DETAIL_TRUNCATION_MARKER = b"\n... error detail truncated by Dex Python SDK ..."
|
|
40
|
+
_ERROR_TYPE_TRUNCATION_MARKER = b"\n... error type truncated by Dex Python SDK ..."
|
|
36
41
|
_STACK_TRACE_TRUNCATION_MARKER = b"\n... stack trace truncated by Dex Python SDK ..."
|
|
37
42
|
|
|
38
43
|
|
|
@@ -139,6 +144,8 @@ def translate_rpc_error(
|
|
|
139
144
|
return LongPollTimeoutError(*parameters)
|
|
140
145
|
if sub_status is ErrorSubStatus.CHANNEL_MESSAGE_NOT_FOUND:
|
|
141
146
|
return ChannelMessageNotFoundError(*parameters)
|
|
147
|
+
if sub_status is ErrorSubStatus.WAIT_HANDLER_TIMEOUT:
|
|
148
|
+
return WaitHandlerTimeoutError(*parameters)
|
|
142
149
|
return DexServiceError(*parameters)
|
|
143
150
|
|
|
144
151
|
|
|
@@ -179,11 +186,19 @@ def _worker_error_status(error: BaseException) -> status_pb2.Status:
|
|
|
179
186
|
retry_after_error = error
|
|
180
187
|
reported = error.cause
|
|
181
188
|
|
|
182
|
-
message =
|
|
189
|
+
message = _truncate_worker_failure_field(
|
|
190
|
+
str(reported) or type(reported).__name__,
|
|
191
|
+
MAX_WORKER_ERROR_DETAIL_BYTES,
|
|
192
|
+
_ERROR_DETAIL_TRUNCATION_MARKER,
|
|
193
|
+
)
|
|
183
194
|
stack_trace_source = error if retry_after_error is not None else reported
|
|
184
195
|
worker_error = pb.WorkerErrorResponse(
|
|
185
196
|
detail=message,
|
|
186
|
-
error_type=
|
|
197
|
+
error_type=_truncate_worker_failure_field(
|
|
198
|
+
f"{type(reported).__module__}.{type(reported).__qualname__}",
|
|
199
|
+
MAX_WORKER_ERROR_TYPE_BYTES,
|
|
200
|
+
_ERROR_TYPE_TRUNCATION_MARKER,
|
|
201
|
+
),
|
|
187
202
|
stack_trace=_worker_stack_trace(stack_trace_source),
|
|
188
203
|
)
|
|
189
204
|
if retry_after_error is not None:
|
|
@@ -201,16 +216,25 @@ def _worker_stack_trace(error: BaseException) -> str:
|
|
|
201
216
|
if error.__traceback__ is None:
|
|
202
217
|
return ""
|
|
203
218
|
lines = traceback.format_exception(type(error), error, error.__traceback__)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
219
|
+
return _truncate_worker_failure_field(
|
|
220
|
+
"".join(lines),
|
|
221
|
+
MAX_WORKER_STACK_TRACE_BYTES,
|
|
222
|
+
_STACK_TRACE_TRUNCATION_MARKER,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _truncate_worker_failure_field(
|
|
227
|
+
value: str,
|
|
228
|
+
maximum_bytes: int,
|
|
229
|
+
truncation_marker: bytes,
|
|
230
|
+
) -> str:
|
|
231
|
+
encoded = value.encode()
|
|
232
|
+
if len(encoded) <= maximum_bytes:
|
|
233
|
+
return value
|
|
234
|
+
prefix_length = maximum_bytes - len(truncation_marker)
|
|
208
235
|
while prefix_length > 0 and (encoded[prefix_length] & 0xC0) == 0x80:
|
|
209
236
|
prefix_length -= 1
|
|
210
|
-
return (
|
|
211
|
-
encoded[:prefix_length].decode(errors="replace")
|
|
212
|
-
+ _STACK_TRACE_TRUNCATION_MARKER.decode()
|
|
213
|
-
)
|
|
237
|
+
return encoded[:prefix_length].decode() + truncation_marker.decode()
|
|
214
238
|
|
|
215
239
|
|
|
216
240
|
def _map_sub_status(value: int) -> ErrorSubStatus:
|
|
@@ -225,5 +249,8 @@ def _map_sub_status(value: int) -> ErrorSubStatus:
|
|
|
225
249
|
int(
|
|
226
250
|
pb.ERROR_SUB_STATUS_CHANNEL_MESSAGE_NOT_FOUND
|
|
227
251
|
): ErrorSubStatus.CHANNEL_MESSAGE_NOT_FOUND,
|
|
252
|
+
int(
|
|
253
|
+
pb.ERROR_SUB_STATUS_WAIT_HANDLER_TIME_OUT
|
|
254
|
+
): ErrorSubStatus.WAIT_HANDLER_TIMEOUT,
|
|
228
255
|
}
|
|
229
256
|
return statuses.get(value, ErrorSubStatus.UNCATEGORIZED)
|