dex-python-sdk 0.8.0__tar.gz → 0.9.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.8.0 → dex_python_sdk-0.9.0}/PKG-INFO +12 -1
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/README.md +11 -0
- dex_python_sdk-0.9.0/dex/_server_protocol.py +93 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/async_worker.py +29 -1
- dex_python_sdk-0.9.0/dex/dexpb/dex_pb2.py +505 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/dexpb/dex_pb2.pyi +10 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/dexpb/dex_pb2_grpc.py +44 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/worker.py +29 -1
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/pyproject.toml +1 -1
- dex_python_sdk-0.8.0/dex/dexpb/dex_pb2.py +0 -503
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/LEGACY_NOTICES.md +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/LICENSE +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/__init__.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/_async_value_hydrator.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/_async_worker_dispatcher.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/_async_worker_service.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/_grpc_errors.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/_invocation_context.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/_native.pyi +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/_utils.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/_value_hydrator.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/_value_mapper.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/_worker_dispatcher.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/_worker_service.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/async_client.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/attribute.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/attribute_match.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/blob_cache.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/channel.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/client.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/client_options.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/codec.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/condition.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/context.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/dexpb/__init__.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/flow.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/flow_config.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/flow_info.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/flow_options.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/flow_result.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/py.typed +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/runtime_errors.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/step.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/step_execution.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/stream.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/subflow.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/timer.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/wait.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/wait_options.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/dex/worker_options.py +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/Cargo.lock +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/Cargo.toml +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/crates/dex-blob-cache/Cargo.toml +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/crates/dex-blob-cache/LICENSE +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/crates/dex-blob-cache/src/config.rs +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/crates/dex-blob-cache/src/entry.rs +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/crates/dex-blob-cache/src/error.rs +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/crates/dex-blob-cache/src/format.rs +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/crates/dex-blob-cache/src/lib.rs +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/crates/dex-blob-cache/src/policy.rs +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/crates/dex-blob-cache/src/store.rs +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/crates/dex-blob-cache/tests/blob_cache_integration.rs +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/crates/dex-blob-cache-python/Cargo.toml +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.0}/sdk-rust/crates/dex-blob-cache-python/LICENSE +0 -0
- {dex_python_sdk-0.8.0 → dex_python_sdk-0.9.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.9.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
|
|
@@ -560,6 +560,17 @@ The dispatch `version` input is stamped the same way as a release tag.
|
|
|
560
560
|
|
|
561
561
|
See [CONTRIBUTING.md](../CONTRIBUTING.md#releases-monorepo-tags) for monorepo tag conventions.
|
|
562
562
|
|
|
563
|
+
## Server protocol compatibility
|
|
564
|
+
|
|
565
|
+
Both `Worker` and `AsyncWorker` call `GetServerInfo`, negotiate the highest
|
|
566
|
+
common protocol, synchronize Attribute indexes, and only then bind
|
|
567
|
+
WorkerService. The initial Python SDK interval is `[1,1]`. Missing information,
|
|
568
|
+
invalid or disjoint intervals, and RPC failures stop startup before binding.
|
|
569
|
+
|
|
570
|
+
The diagnostic SDK version comes from Python distribution metadata for
|
|
571
|
+
`dex-python-sdk` and falls back to `dev` when the distribution is unavailable.
|
|
572
|
+
It does not affect protocol compatibility.
|
|
573
|
+
|
|
563
574
|
## License
|
|
564
575
|
|
|
565
576
|
[Sustainable Use License 1.0](LICENSE), with legacy portions under their
|
|
@@ -544,6 +544,17 @@ The dispatch `version` input is stamped the same way as a release tag.
|
|
|
544
544
|
|
|
545
545
|
See [CONTRIBUTING.md](../CONTRIBUTING.md#releases-monorepo-tags) for monorepo tag conventions.
|
|
546
546
|
|
|
547
|
+
## Server protocol compatibility
|
|
548
|
+
|
|
549
|
+
Both `Worker` and `AsyncWorker` call `GetServerInfo`, negotiate the highest
|
|
550
|
+
common protocol, synchronize Attribute indexes, and only then bind
|
|
551
|
+
WorkerService. The initial Python SDK interval is `[1,1]`. Missing information,
|
|
552
|
+
invalid or disjoint intervals, and RPC failures stop startup before binding.
|
|
553
|
+
|
|
554
|
+
The diagnostic SDK version comes from Python distribution metadata for
|
|
555
|
+
`dex-python-sdk` and falls back to `dev` when the distribution is unavailable.
|
|
556
|
+
It does not affect protocol compatibility.
|
|
557
|
+
|
|
547
558
|
## License
|
|
548
559
|
|
|
549
560
|
[Sustainable Use License 1.0](LICENSE), with legacy portions under their
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Copyright (c) 2026 Super Durable, Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Sustainable Use License 1.0.
|
|
4
|
+
# You may not use this file except in compliance with the License.
|
|
5
|
+
# See the LICENSE file in the repository root.
|
|
6
|
+
#
|
|
7
|
+
# SPDX-License-Identifier: LicenseRef-Sustainable-Use-1.0
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
12
|
+
|
|
13
|
+
import grpc
|
|
14
|
+
|
|
15
|
+
from dex.dexpb import dex_pb2 as pb
|
|
16
|
+
|
|
17
|
+
MINIMUM_SUPPORTED_SERVER_PROTOCOL_VERSION = 1
|
|
18
|
+
MAXIMUM_SUPPORTED_SERVER_PROTOCOL_VERSION = 1
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def sdk_version() -> str:
|
|
22
|
+
try:
|
|
23
|
+
return version("dex-python-sdk")
|
|
24
|
+
except PackageNotFoundError:
|
|
25
|
+
return "dev"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def negotiate_server_protocol(server_info: pb.ServerInfo, artifact_version: str) -> int:
|
|
29
|
+
server_minimum = server_info.minimum_supported_protocol_version
|
|
30
|
+
server_current = server_info.current_protocol_version
|
|
31
|
+
if (
|
|
32
|
+
MINIMUM_SUPPORTED_SERVER_PROTOCOL_VERSION == 0
|
|
33
|
+
or MAXIMUM_SUPPORTED_SERVER_PROTOCOL_VERSION == 0
|
|
34
|
+
or MINIMUM_SUPPORTED_SERVER_PROTOCOL_VERSION
|
|
35
|
+
> MAXIMUM_SUPPORTED_SERVER_PROTOCOL_VERSION
|
|
36
|
+
):
|
|
37
|
+
raise _compatibility_error(
|
|
38
|
+
artifact_version,
|
|
39
|
+
server_info.server_version,
|
|
40
|
+
server_minimum,
|
|
41
|
+
server_current,
|
|
42
|
+
"Python SDK protocol interval is invalid",
|
|
43
|
+
)
|
|
44
|
+
if server_minimum == 0 or server_current == 0 or server_minimum > server_current:
|
|
45
|
+
raise _compatibility_error(
|
|
46
|
+
artifact_version,
|
|
47
|
+
server_info.server_version,
|
|
48
|
+
server_minimum,
|
|
49
|
+
server_current,
|
|
50
|
+
"Server protocol interval is invalid",
|
|
51
|
+
)
|
|
52
|
+
negotiated = min(server_current, MAXIMUM_SUPPORTED_SERVER_PROTOCOL_VERSION)
|
|
53
|
+
if (
|
|
54
|
+
negotiated < server_minimum
|
|
55
|
+
or negotiated < MINIMUM_SUPPORTED_SERVER_PROTOCOL_VERSION
|
|
56
|
+
):
|
|
57
|
+
raise _compatibility_error(
|
|
58
|
+
artifact_version,
|
|
59
|
+
server_info.server_version,
|
|
60
|
+
server_minimum,
|
|
61
|
+
server_current,
|
|
62
|
+
"protocol intervals do not overlap",
|
|
63
|
+
)
|
|
64
|
+
return negotiated
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def server_info_request_error(
|
|
68
|
+
artifact_version: str, failure: grpc.RpcError
|
|
69
|
+
) -> RuntimeError:
|
|
70
|
+
return RuntimeError(
|
|
71
|
+
f'Python SDK version "{artifact_version}" protocol '
|
|
72
|
+
f"[{MINIMUM_SUPPORTED_SERVER_PROTOCOL_VERSION},"
|
|
73
|
+
f"{MAXIMUM_SUPPORTED_SERVER_PROTOCOL_VERSION}] is incompatible "
|
|
74
|
+
'with Server version "unknown" protocol [unknown,unknown]: '
|
|
75
|
+
f"GetServerInfo failed: {failure}"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _compatibility_error(
|
|
80
|
+
artifact_version: str,
|
|
81
|
+
server_version: str,
|
|
82
|
+
server_minimum: int,
|
|
83
|
+
server_current: int,
|
|
84
|
+
reason: str,
|
|
85
|
+
) -> RuntimeError:
|
|
86
|
+
displayed_server_version = server_version or "unknown"
|
|
87
|
+
return RuntimeError(
|
|
88
|
+
f'Python SDK version "{artifact_version}" protocol '
|
|
89
|
+
f"[{MINIMUM_SUPPORTED_SERVER_PROTOCOL_VERSION},"
|
|
90
|
+
f"{MAXIMUM_SUPPORTED_SERVER_PROTOCOL_VERSION}] is incompatible with "
|
|
91
|
+
f'Server version "{displayed_server_version}" protocol '
|
|
92
|
+
f"[{server_minimum},{server_current}]: {reason}"
|
|
93
|
+
)
|
|
@@ -12,7 +12,13 @@ import asyncio
|
|
|
12
12
|
from types import TracebackType
|
|
13
13
|
|
|
14
14
|
import grpc
|
|
15
|
+
from google.protobuf import empty_pb2
|
|
15
16
|
|
|
17
|
+
from dex._server_protocol import (
|
|
18
|
+
negotiate_server_protocol,
|
|
19
|
+
sdk_version,
|
|
20
|
+
server_info_request_error,
|
|
21
|
+
)
|
|
16
22
|
from dex._async_value_hydrator import AsyncValueHydrator
|
|
17
23
|
from dex._async_worker_dispatcher import AsyncWorkerDispatcher
|
|
18
24
|
from dex._async_worker_service import AsyncWorkerService
|
|
@@ -64,6 +70,8 @@ class AsyncWorker:
|
|
|
64
70
|
self._flow_service = dex_pb2_grpc.FlowServiceStub( # type: ignore[no-untyped-call]
|
|
65
71
|
self._flow_channel
|
|
66
72
|
)
|
|
73
|
+
self._sdk_version = sdk_version()
|
|
74
|
+
self._negotiated_protocol_version = 0
|
|
67
75
|
values = ValueMapper(registry.codec_registry)
|
|
68
76
|
dispatcher = AsyncWorkerDispatcher(
|
|
69
77
|
registry,
|
|
@@ -116,7 +124,7 @@ class AsyncWorker:
|
|
|
116
124
|
await self.close()
|
|
117
125
|
|
|
118
126
|
async def start(self) -> None:
|
|
119
|
-
"""
|
|
127
|
+
"""Negotiate protocol, synchronize Attribute indexes, and serve.
|
|
120
128
|
|
|
121
129
|
``start`` may be awaited exactly once. It contacts FlowService before binding
|
|
122
130
|
and completes only after ``stop`` terminates the server.
|
|
@@ -126,6 +134,26 @@ class AsyncWorker:
|
|
|
126
134
|
"""
|
|
127
135
|
if self._state != "created":
|
|
128
136
|
raise RuntimeError(f"AsyncWorker cannot start from state {self._state}")
|
|
137
|
+
try:
|
|
138
|
+
server_info = await self._flow_service.GetServerInfo(
|
|
139
|
+
empty_pb2.Empty(),
|
|
140
|
+
timeout=self.options.attribute_index_sync_timeout.total_seconds(),
|
|
141
|
+
)
|
|
142
|
+
except grpc.RpcError as failure:
|
|
143
|
+
self._state = "stopped"
|
|
144
|
+
await self._flow_channel.close(None)
|
|
145
|
+
self._stopped.set()
|
|
146
|
+
raise server_info_request_error(self._sdk_version, failure) from failure
|
|
147
|
+
try:
|
|
148
|
+
self._negotiated_protocol_version = negotiate_server_protocol(
|
|
149
|
+
server_info,
|
|
150
|
+
self._sdk_version,
|
|
151
|
+
)
|
|
152
|
+
except RuntimeError:
|
|
153
|
+
self._state = "stopped"
|
|
154
|
+
await self._flow_channel.close(None)
|
|
155
|
+
self._stopped.set()
|
|
156
|
+
raise
|
|
129
157
|
try:
|
|
130
158
|
await self._flow_service.SyncAttributeIndexes(
|
|
131
159
|
pb.SyncAttributeIndexRequest(
|