refract-io-lib 0.1.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.
- refract_io_lib-0.1.0/PKG-INFO +11 -0
- refract_io_lib-0.1.0/pyproject.toml +74 -0
- refract_io_lib-0.1.0/src/refract_io/__init__.py +30 -0
- refract_io_lib-0.1.0/src/refract_io/_client.py +269 -0
- refract_io_lib-0.1.0/src/refract_io/_discovery.py +60 -0
- refract_io_lib-0.1.0/src/refract_io/_proto/__init__.py +0 -0
- refract_io_lib-0.1.0/src/refract_io/_proto/kvstream_pb2.py +48 -0
- refract_io_lib-0.1.0/src/refract_io/_proto/kvstream_pb2_grpc.py +97 -0
- refract_io_lib-0.1.0/src/refract_io/_types.py +33 -0
- refract_io_lib-0.1.0/src/refract_io/py.typed +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: refract-io-lib
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Refract I/O library
|
|
5
|
+
Author: Richard Chen
|
|
6
|
+
Author-email: Richard Chen <richardchen.93@gmail.com>
|
|
7
|
+
Requires-Dist: grpcio>=1.60.0
|
|
8
|
+
Requires-Dist: protobuf>=4.25.0
|
|
9
|
+
Requires-Dist: zeroconf>=0.131.0
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Project-URL: Homepage, https://refractvisualizer.ca/
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "refract-io-lib"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Refract I/O library"
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "Richard Chen", email = "richardchen.93@gmail.com" }
|
|
7
|
+
]
|
|
8
|
+
requires-python = ">=3.10"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"grpcio>=1.60.0",
|
|
11
|
+
"protobuf>=4.25.0",
|
|
12
|
+
"zeroconf>=0.131.0",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
Homepage = "https://refractvisualizer.ca/"
|
|
17
|
+
|
|
18
|
+
[dependency-groups]
|
|
19
|
+
dev = [
|
|
20
|
+
"black>=25.1.0",
|
|
21
|
+
"grpc-stubs>=1.53.0",
|
|
22
|
+
"grpcio-tools>=1.60.0",
|
|
23
|
+
"isort>=6.0.0",
|
|
24
|
+
"mypy>=1.15.0",
|
|
25
|
+
"pre-commit>=4.0.0",
|
|
26
|
+
"ruff>=0.11.0",
|
|
27
|
+
"types-protobuf>=4.25.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["uv_build>=0.9.21,<0.10.0"]
|
|
32
|
+
build-backend = "uv_build"
|
|
33
|
+
|
|
34
|
+
[tool.uv.build-backend]
|
|
35
|
+
module-name = "refract_io"
|
|
36
|
+
|
|
37
|
+
[tool.black]
|
|
38
|
+
line-length = 88
|
|
39
|
+
target-version = ["py310"]
|
|
40
|
+
extend-exclude = "src/refract_io/_proto"
|
|
41
|
+
|
|
42
|
+
[tool.isort]
|
|
43
|
+
profile = "black"
|
|
44
|
+
line_length = 88
|
|
45
|
+
src_paths = ["src"]
|
|
46
|
+
extend_skip_glob = ["src/refract_io/_proto/*"]
|
|
47
|
+
|
|
48
|
+
[tool.ruff]
|
|
49
|
+
line-length = 88
|
|
50
|
+
target-version = "py310"
|
|
51
|
+
src = ["src"]
|
|
52
|
+
extend-exclude = ["src/refract_io/_proto"]
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint]
|
|
55
|
+
# isort runs in pre-commit; ruff lint omits "I" to avoid conflicting import rules.
|
|
56
|
+
select = ["E", "W", "F", "B", "UP", "C4"]
|
|
57
|
+
|
|
58
|
+
[tool.mypy]
|
|
59
|
+
python_version = "3.10"
|
|
60
|
+
strict = true
|
|
61
|
+
warn_return_any = true
|
|
62
|
+
warn_unused_configs = true
|
|
63
|
+
mypy_path = "src"
|
|
64
|
+
explicit_package_bases = true
|
|
65
|
+
packages = ["refract_io"]
|
|
66
|
+
|
|
67
|
+
[[tool.mypy.overrides]]
|
|
68
|
+
module = "refract_io._proto.*"
|
|
69
|
+
ignore_errors = true
|
|
70
|
+
follow_imports = "skip"
|
|
71
|
+
|
|
72
|
+
[[tool.mypy.overrides]]
|
|
73
|
+
module = "zeroconf"
|
|
74
|
+
ignore_missing_imports = true
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from refract_io._client import RefractStream
|
|
2
|
+
from refract_io._discovery import discover_refract
|
|
3
|
+
from refract_io._types import ValueType
|
|
4
|
+
|
|
5
|
+
float32 = ValueType.FLOAT32
|
|
6
|
+
float64 = ValueType.FLOAT64
|
|
7
|
+
int8 = ValueType.INT8
|
|
8
|
+
int16 = ValueType.INT16
|
|
9
|
+
int32 = ValueType.INT32
|
|
10
|
+
int64 = ValueType.INT64
|
|
11
|
+
uint8 = ValueType.UINT8
|
|
12
|
+
uint16 = ValueType.UINT16
|
|
13
|
+
uint32 = ValueType.UINT32
|
|
14
|
+
uint64 = ValueType.UINT64
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"RefractStream",
|
|
18
|
+
"ValueType",
|
|
19
|
+
"discover_refract",
|
|
20
|
+
"float32",
|
|
21
|
+
"float64",
|
|
22
|
+
"int8",
|
|
23
|
+
"int16",
|
|
24
|
+
"int32",
|
|
25
|
+
"int64",
|
|
26
|
+
"uint8",
|
|
27
|
+
"uint16",
|
|
28
|
+
"uint32",
|
|
29
|
+
"uint64",
|
|
30
|
+
]
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import queue
|
|
5
|
+
import struct
|
|
6
|
+
import threading
|
|
7
|
+
from collections.abc import Generator, Sequence
|
|
8
|
+
from types import TracebackType
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import grpc
|
|
12
|
+
|
|
13
|
+
from refract_io._discovery import discover_refract
|
|
14
|
+
from refract_io._proto import kvstream_pb2 as _pb2
|
|
15
|
+
from refract_io._proto import kvstream_pb2_grpc as _pb2_grpc
|
|
16
|
+
from refract_io._types import DTYPE_INFO, ValueType
|
|
17
|
+
|
|
18
|
+
# Re-bind as Any so mypy doesn't complain about dynamic proto attributes
|
|
19
|
+
kvstream_pb2: Any = _pb2
|
|
20
|
+
kvstream_pb2_grpc: Any = _pb2_grpc
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
_DEFAULT_HOST = "localhost"
|
|
25
|
+
_DEFAULT_PORT = 50051
|
|
26
|
+
|
|
27
|
+
# Sentinel used to signal the background thread to stop.
|
|
28
|
+
_STOP = object()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RefractStream:
|
|
32
|
+
"""Stream tabular data over gRPC to the Refract app.
|
|
33
|
+
|
|
34
|
+
``host`` and ``port`` are optional. When both are ``None`` the client
|
|
35
|
+
attempts Bonjour autodiscovery and falls back to ``localhost:50051``.
|
|
36
|
+
``port`` accepts either an ``int`` or a numeric ``str``.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
host: str | None = None,
|
|
42
|
+
port: int | str | None = None,
|
|
43
|
+
) -> None:
|
|
44
|
+
resolved_host, resolved_port = self._resolve_address(host, port)
|
|
45
|
+
self._host: str = resolved_host
|
|
46
|
+
self._port: int = resolved_port
|
|
47
|
+
|
|
48
|
+
self._channel: grpc.Channel | None = None
|
|
49
|
+
self._stub: kvstream_pb2_grpc.KVStreamStub | None = None
|
|
50
|
+
self._queue: queue.Queue[object] = queue.Queue()
|
|
51
|
+
self._thread: threading.Thread | None = None
|
|
52
|
+
self._running: bool = False
|
|
53
|
+
|
|
54
|
+
self._lock = threading.Lock()
|
|
55
|
+
# table_id -> list of (struct_fmt, byte_size) per column
|
|
56
|
+
self._tables: dict[int, list[tuple[str, int]]] = {}
|
|
57
|
+
self._pending_registrations: list[kvstream_pb2.StreamMessage] = []
|
|
58
|
+
|
|
59
|
+
# ------------------------------------------------------------------
|
|
60
|
+
# Address resolution
|
|
61
|
+
# ------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def _resolve_address(
|
|
65
|
+
host: str | None,
|
|
66
|
+
port: int | str | None,
|
|
67
|
+
) -> tuple[str, int]:
|
|
68
|
+
if host is not None or port is not None:
|
|
69
|
+
return (
|
|
70
|
+
host if host is not None else _DEFAULT_HOST,
|
|
71
|
+
int(port) if port is not None else _DEFAULT_PORT,
|
|
72
|
+
)
|
|
73
|
+
# Autodiscover
|
|
74
|
+
result = discover_refract()
|
|
75
|
+
if result is not None:
|
|
76
|
+
logger.info("Autodiscovered Refract at %s:%d", *result)
|
|
77
|
+
return result
|
|
78
|
+
logger.info(
|
|
79
|
+
"No Refract service found via Bonjour, " "falling back to %s:%d",
|
|
80
|
+
_DEFAULT_HOST,
|
|
81
|
+
_DEFAULT_PORT,
|
|
82
|
+
)
|
|
83
|
+
return (_DEFAULT_HOST, _DEFAULT_PORT)
|
|
84
|
+
|
|
85
|
+
# ------------------------------------------------------------------
|
|
86
|
+
# Table registration
|
|
87
|
+
# ------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
def create_table(
|
|
90
|
+
self,
|
|
91
|
+
id: int,
|
|
92
|
+
name: str,
|
|
93
|
+
columns: dict[str, ValueType],
|
|
94
|
+
) -> None:
|
|
95
|
+
"""Register a table schema.
|
|
96
|
+
|
|
97
|
+
``columns`` maps column names to :class:`ValueType` constants
|
|
98
|
+
(e.g. ``{'timestamp': refract_io.float64, ...}``).
|
|
99
|
+
"""
|
|
100
|
+
col_defs: list[kvstream_pb2.ColumnDef] = []
|
|
101
|
+
fmt_parts: list[tuple[str, int]] = []
|
|
102
|
+
for col_name, vtype in columns.items():
|
|
103
|
+
vtype = ValueType(vtype) # validate
|
|
104
|
+
fmt, size = DTYPE_INFO[vtype]
|
|
105
|
+
col_defs.append(
|
|
106
|
+
kvstream_pb2.ColumnDef(name=col_name, value_type=int(vtype))
|
|
107
|
+
)
|
|
108
|
+
fmt_parts.append((fmt, size))
|
|
109
|
+
|
|
110
|
+
with self._lock:
|
|
111
|
+
self._tables[id] = fmt_parts
|
|
112
|
+
|
|
113
|
+
msg = kvstream_pb2.StreamMessage(
|
|
114
|
+
register_table=kvstream_pb2.RegisterTable(
|
|
115
|
+
table_id=id,
|
|
116
|
+
name=name,
|
|
117
|
+
columns=col_defs,
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
if self._running:
|
|
121
|
+
self._queue.put(msg)
|
|
122
|
+
else:
|
|
123
|
+
self._pending_registrations.append(msg)
|
|
124
|
+
|
|
125
|
+
# ------------------------------------------------------------------
|
|
126
|
+
# Sending rows
|
|
127
|
+
# ------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
def send_row(self, table_id: int, values: Sequence[int | float]) -> None:
|
|
130
|
+
"""Send a single row for *table_id*.
|
|
131
|
+
|
|
132
|
+
Values are packed in registration order using the column dtypes.
|
|
133
|
+
Auto-starts the connection if not already running.
|
|
134
|
+
"""
|
|
135
|
+
self._ensure_started()
|
|
136
|
+
|
|
137
|
+
with self._lock:
|
|
138
|
+
fmt_parts = self._tables.get(table_id)
|
|
139
|
+
if fmt_parts is None:
|
|
140
|
+
raise ValueError(f"Table {table_id} not registered")
|
|
141
|
+
if len(values) != len(fmt_parts):
|
|
142
|
+
raise ValueError(f"Expected {len(fmt_parts)} values, got {len(values)}")
|
|
143
|
+
|
|
144
|
+
packed = b"".join(
|
|
145
|
+
struct.pack(fmt, val)
|
|
146
|
+
for val, (fmt, _) in zip(values, fmt_parts, strict=True)
|
|
147
|
+
)
|
|
148
|
+
msg = kvstream_pb2.StreamMessage(
|
|
149
|
+
table_row=kvstream_pb2.TableRow(
|
|
150
|
+
table_id=table_id,
|
|
151
|
+
values=packed,
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
self._queue.put(msg)
|
|
155
|
+
|
|
156
|
+
def send_rows(
|
|
157
|
+
self,
|
|
158
|
+
table_id: int,
|
|
159
|
+
rows: Sequence[Sequence[int | float]],
|
|
160
|
+
) -> None:
|
|
161
|
+
"""Send multiple rows for *table_id* in one call."""
|
|
162
|
+
self._ensure_started()
|
|
163
|
+
|
|
164
|
+
with self._lock:
|
|
165
|
+
fmt_parts = self._tables.get(table_id)
|
|
166
|
+
if fmt_parts is None:
|
|
167
|
+
raise ValueError(f"Table {table_id} not registered")
|
|
168
|
+
|
|
169
|
+
expected = len(fmt_parts)
|
|
170
|
+
for row in rows:
|
|
171
|
+
if len(row) != expected:
|
|
172
|
+
raise ValueError(f"Expected {expected} values per row, got {len(row)}")
|
|
173
|
+
packed = b"".join(
|
|
174
|
+
struct.pack(fmt, val)
|
|
175
|
+
for val, (fmt, _) in zip(row, fmt_parts, strict=True)
|
|
176
|
+
)
|
|
177
|
+
msg = kvstream_pb2.StreamMessage(
|
|
178
|
+
table_row=kvstream_pb2.TableRow(
|
|
179
|
+
table_id=table_id,
|
|
180
|
+
values=packed,
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
self._queue.put(msg)
|
|
184
|
+
|
|
185
|
+
# ------------------------------------------------------------------
|
|
186
|
+
# Lifecycle
|
|
187
|
+
# ------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
def start(self) -> None:
|
|
190
|
+
"""Open the gRPC channel and start the background streaming thread."""
|
|
191
|
+
if self._running:
|
|
192
|
+
return
|
|
193
|
+
self._running = True
|
|
194
|
+
self._channel = grpc.insecure_channel(f"{self._host}:{self._port}")
|
|
195
|
+
self._stub = kvstream_pb2_grpc.KVStreamStub(self._channel)
|
|
196
|
+
self._thread = threading.Thread(
|
|
197
|
+
target=self._stream_loop, daemon=True, name="refract-stream"
|
|
198
|
+
)
|
|
199
|
+
self._thread.start()
|
|
200
|
+
|
|
201
|
+
def close(self) -> None:
|
|
202
|
+
"""Stop the background thread and close the gRPC channel."""
|
|
203
|
+
if not self._running:
|
|
204
|
+
return
|
|
205
|
+
self._running = False
|
|
206
|
+
self._queue.put(_STOP)
|
|
207
|
+
if self._thread is not None:
|
|
208
|
+
self._thread.join(timeout=5)
|
|
209
|
+
self._thread = None
|
|
210
|
+
if self._channel is not None:
|
|
211
|
+
self._channel.close()
|
|
212
|
+
self._channel = None
|
|
213
|
+
|
|
214
|
+
stop = close
|
|
215
|
+
|
|
216
|
+
def _ensure_started(self) -> None:
|
|
217
|
+
if not self._running:
|
|
218
|
+
self.start()
|
|
219
|
+
|
|
220
|
+
# ------------------------------------------------------------------
|
|
221
|
+
# Context manager
|
|
222
|
+
# ------------------------------------------------------------------
|
|
223
|
+
|
|
224
|
+
def __enter__(self) -> RefractStream:
|
|
225
|
+
self.start()
|
|
226
|
+
return self
|
|
227
|
+
|
|
228
|
+
def __exit__(
|
|
229
|
+
self,
|
|
230
|
+
exc_type: type[BaseException] | None,
|
|
231
|
+
exc_val: BaseException | None,
|
|
232
|
+
exc_tb: TracebackType | None,
|
|
233
|
+
) -> None:
|
|
234
|
+
self.close()
|
|
235
|
+
|
|
236
|
+
# ------------------------------------------------------------------
|
|
237
|
+
# Background streaming
|
|
238
|
+
# ------------------------------------------------------------------
|
|
239
|
+
|
|
240
|
+
def _message_generator(
|
|
241
|
+
self,
|
|
242
|
+
) -> Generator[kvstream_pb2.StreamMessage, None, None]:
|
|
243
|
+
# Send pending registrations first
|
|
244
|
+
yield from self._pending_registrations
|
|
245
|
+
self._pending_registrations.clear()
|
|
246
|
+
|
|
247
|
+
# Stream from queue
|
|
248
|
+
while self._running:
|
|
249
|
+
try:
|
|
250
|
+
item = self._queue.get(timeout=0.1)
|
|
251
|
+
if item is _STOP:
|
|
252
|
+
break
|
|
253
|
+
assert isinstance(item, kvstream_pb2.StreamMessage) # noqa: S101
|
|
254
|
+
yield item
|
|
255
|
+
except queue.Empty:
|
|
256
|
+
continue
|
|
257
|
+
|
|
258
|
+
def _stream_loop(self) -> None:
|
|
259
|
+
try:
|
|
260
|
+
assert self._stub is not None # noqa: S101
|
|
261
|
+
response = self._stub.Stream(self._message_generator())
|
|
262
|
+
if not response.ok:
|
|
263
|
+
logger.error("Server error: %s", response.error)
|
|
264
|
+
except grpc.RpcError as exc:
|
|
265
|
+
if self._running:
|
|
266
|
+
logger.error("gRPC error: %s", exc)
|
|
267
|
+
except Exception:
|
|
268
|
+
if self._running:
|
|
269
|
+
logger.exception("Unexpected error in stream loop")
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import socket
|
|
5
|
+
import threading
|
|
6
|
+
|
|
7
|
+
from zeroconf import ServiceBrowser, ServiceStateChange, Zeroconf
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
SERVICE_TYPE = "_refract-stream._tcp.local."
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def discover_refract(timeout: float = 3.0) -> tuple[str, int] | None:
|
|
15
|
+
"""Browse for a Refract Stream service advertised via Bonjour.
|
|
16
|
+
|
|
17
|
+
Returns ``(host, port)`` of the first instance found, or ``None``
|
|
18
|
+
if no service is discovered within *timeout* seconds.
|
|
19
|
+
"""
|
|
20
|
+
result: tuple[str, int] | None = None
|
|
21
|
+
found = threading.Event()
|
|
22
|
+
|
|
23
|
+
def on_state_change(
|
|
24
|
+
zc: Zeroconf,
|
|
25
|
+
service_type: str,
|
|
26
|
+
name: str,
|
|
27
|
+
state_change: ServiceStateChange,
|
|
28
|
+
) -> None:
|
|
29
|
+
nonlocal result
|
|
30
|
+
if state_change is not ServiceStateChange.Added:
|
|
31
|
+
return
|
|
32
|
+
info = zc.get_service_info(service_type, name)
|
|
33
|
+
if info is None:
|
|
34
|
+
return
|
|
35
|
+
addresses = info.parsed_addresses()
|
|
36
|
+
if not addresses or info.port is None:
|
|
37
|
+
return
|
|
38
|
+
port = info.port
|
|
39
|
+
# Prefer non-link-local IPv4 addresses
|
|
40
|
+
host = addresses[0]
|
|
41
|
+
for addr in addresses:
|
|
42
|
+
try:
|
|
43
|
+
packed = socket.inet_aton(addr)
|
|
44
|
+
if packed[0:2] != b"\xa9\xfe": # not 169.254.x.x
|
|
45
|
+
host = addr
|
|
46
|
+
break
|
|
47
|
+
except OSError:
|
|
48
|
+
continue
|
|
49
|
+
result = (host, port)
|
|
50
|
+
logger.debug("Discovered Refract at %s:%d", host, port)
|
|
51
|
+
found.set()
|
|
52
|
+
|
|
53
|
+
zc = Zeroconf()
|
|
54
|
+
try:
|
|
55
|
+
ServiceBrowser(zc, SERVICE_TYPE, handlers=[on_state_change])
|
|
56
|
+
found.wait(timeout=timeout)
|
|
57
|
+
finally:
|
|
58
|
+
zc.close()
|
|
59
|
+
|
|
60
|
+
return result
|
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
|
+
# source: kvstream.proto
|
|
5
|
+
# Protobuf Python Version: 6.31.1
|
|
6
|
+
"""Generated protocol buffer code."""
|
|
7
|
+
from google.protobuf import descriptor as _descriptor
|
|
8
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
9
|
+
from google.protobuf import runtime_version as _runtime_version
|
|
10
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
11
|
+
from google.protobuf.internal import builder as _builder
|
|
12
|
+
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
|
+
_runtime_version.Domain.PUBLIC,
|
|
14
|
+
6,
|
|
15
|
+
31,
|
|
16
|
+
1,
|
|
17
|
+
'',
|
|
18
|
+
'kvstream.proto'
|
|
19
|
+
)
|
|
20
|
+
# @@protoc_insertion_point(imports)
|
|
21
|
+
|
|
22
|
+
_sym_db = _symbol_database.Default()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0ekvstream.proto\x12\x10refract.kvstream\"J\n\tColumnDef\x12\x0c\n\x04name\x18\x01 \x01(\t\x12/\n\nvalue_type\x18\x02 \x01(\x0e\x32\x1b.refract.kvstream.ValueType\"]\n\rRegisterTable\x12\x10\n\x08table_id\x18\x01 \x01(\r\x12\x0c\n\x04name\x18\x02 \x01(\t\x12,\n\x07\x63olumns\x18\x03 \x03(\x0b\x32\x1b.refract.kvstream.ColumnDef\",\n\x08TableRow\x12\x10\n\x08table_id\x18\x01 \x01(\r\x12\x0e\n\x06values\x18\x02 \x01(\x0c\"\x86\x01\n\rStreamMessage\x12\x39\n\x0eregister_table\x18\x01 \x01(\x0b\x32\x1f.refract.kvstream.RegisterTableH\x00\x12/\n\ttable_row\x18\x02 \x01(\x0b\x32\x1a.refract.kvstream.TableRowH\x00\x42\t\n\x07payload\"+\n\x0eStreamResponse\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t*\x7f\n\tValueType\x12\x0b\n\x07\x46LOAT32\x10\x00\x12\x0b\n\x07\x46LOAT64\x10\x01\x12\x08\n\x04INT8\x10\x02\x12\t\n\x05INT16\x10\x03\x12\t\n\x05INT32\x10\x04\x12\t\n\x05INT64\x10\x05\x12\t\n\x05UINT8\x10\x06\x12\n\n\x06UINT16\x10\x07\x12\n\n\x06UINT32\x10\x08\x12\n\n\x06UINT64\x10\t2Y\n\x08KVStream\x12M\n\x06Stream\x12\x1f.refract.kvstream.StreamMessage\x1a .refract.kvstream.StreamResponse(\x01\x62\x06proto3')
|
|
28
|
+
|
|
29
|
+
_globals = globals()
|
|
30
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
31
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'kvstream_pb2', _globals)
|
|
32
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
|
33
|
+
DESCRIPTOR._loaded_options = None
|
|
34
|
+
_globals['_VALUETYPE']._serialized_start=435
|
|
35
|
+
_globals['_VALUETYPE']._serialized_end=562
|
|
36
|
+
_globals['_COLUMNDEF']._serialized_start=36
|
|
37
|
+
_globals['_COLUMNDEF']._serialized_end=110
|
|
38
|
+
_globals['_REGISTERTABLE']._serialized_start=112
|
|
39
|
+
_globals['_REGISTERTABLE']._serialized_end=205
|
|
40
|
+
_globals['_TABLEROW']._serialized_start=207
|
|
41
|
+
_globals['_TABLEROW']._serialized_end=251
|
|
42
|
+
_globals['_STREAMMESSAGE']._serialized_start=254
|
|
43
|
+
_globals['_STREAMMESSAGE']._serialized_end=388
|
|
44
|
+
_globals['_STREAMRESPONSE']._serialized_start=390
|
|
45
|
+
_globals['_STREAMRESPONSE']._serialized_end=433
|
|
46
|
+
_globals['_KVSTREAM']._serialized_start=564
|
|
47
|
+
_globals['_KVSTREAM']._serialized_end=653
|
|
48
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
|
2
|
+
"""Client and server classes corresponding to protobuf-defined services."""
|
|
3
|
+
import grpc
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
from . import kvstream_pb2 as kvstream__pb2
|
|
7
|
+
|
|
8
|
+
GRPC_GENERATED_VERSION = '1.80.0'
|
|
9
|
+
GRPC_VERSION = grpc.__version__
|
|
10
|
+
_version_not_supported = False
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from grpc._utilities import first_version_is_lower
|
|
14
|
+
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
|
|
15
|
+
except ImportError:
|
|
16
|
+
_version_not_supported = True
|
|
17
|
+
|
|
18
|
+
if _version_not_supported:
|
|
19
|
+
raise RuntimeError(
|
|
20
|
+
f'The grpc package installed is at version {GRPC_VERSION},'
|
|
21
|
+
+ ' but the generated code in kvstream_pb2_grpc.py depends on'
|
|
22
|
+
+ f' grpcio>={GRPC_GENERATED_VERSION}.'
|
|
23
|
+
+ f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
|
|
24
|
+
+ f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class KVStreamStub(object):
|
|
29
|
+
"""Missing associated documentation comment in .proto file."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, channel):
|
|
32
|
+
"""Constructor.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
channel: A grpc.Channel.
|
|
36
|
+
"""
|
|
37
|
+
self.Stream = channel.stream_unary(
|
|
38
|
+
'/refract.kvstream.KVStream/Stream',
|
|
39
|
+
request_serializer=kvstream__pb2.StreamMessage.SerializeToString,
|
|
40
|
+
response_deserializer=kvstream__pb2.StreamResponse.FromString,
|
|
41
|
+
_registered_method=True)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class KVStreamServicer(object):
|
|
45
|
+
"""Missing associated documentation comment in .proto file."""
|
|
46
|
+
|
|
47
|
+
def Stream(self, request_iterator, context):
|
|
48
|
+
"""Missing associated documentation comment in .proto file."""
|
|
49
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
50
|
+
context.set_details('Method not implemented!')
|
|
51
|
+
raise NotImplementedError('Method not implemented!')
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def add_KVStreamServicer_to_server(servicer, server):
|
|
55
|
+
rpc_method_handlers = {
|
|
56
|
+
'Stream': grpc.stream_unary_rpc_method_handler(
|
|
57
|
+
servicer.Stream,
|
|
58
|
+
request_deserializer=kvstream__pb2.StreamMessage.FromString,
|
|
59
|
+
response_serializer=kvstream__pb2.StreamResponse.SerializeToString,
|
|
60
|
+
),
|
|
61
|
+
}
|
|
62
|
+
generic_handler = grpc.method_handlers_generic_handler(
|
|
63
|
+
'refract.kvstream.KVStream', rpc_method_handlers)
|
|
64
|
+
server.add_generic_rpc_handlers((generic_handler,))
|
|
65
|
+
server.add_registered_method_handlers('refract.kvstream.KVStream', rpc_method_handlers)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# This class is part of an EXPERIMENTAL API.
|
|
69
|
+
class KVStream(object):
|
|
70
|
+
"""Missing associated documentation comment in .proto file."""
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def Stream(request_iterator,
|
|
74
|
+
target,
|
|
75
|
+
options=(),
|
|
76
|
+
channel_credentials=None,
|
|
77
|
+
call_credentials=None,
|
|
78
|
+
insecure=False,
|
|
79
|
+
compression=None,
|
|
80
|
+
wait_for_ready=None,
|
|
81
|
+
timeout=None,
|
|
82
|
+
metadata=None):
|
|
83
|
+
return grpc.experimental.stream_unary(
|
|
84
|
+
request_iterator,
|
|
85
|
+
target,
|
|
86
|
+
'/refract.kvstream.KVStream/Stream',
|
|
87
|
+
kvstream__pb2.StreamMessage.SerializeToString,
|
|
88
|
+
kvstream__pb2.StreamResponse.FromString,
|
|
89
|
+
options,
|
|
90
|
+
channel_credentials,
|
|
91
|
+
insecure,
|
|
92
|
+
call_credentials,
|
|
93
|
+
compression,
|
|
94
|
+
wait_for_ready,
|
|
95
|
+
timeout,
|
|
96
|
+
metadata,
|
|
97
|
+
_registered_method=True)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import IntEnum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ValueType(IntEnum):
|
|
7
|
+
"""Column data types matching the kvstream protobuf ValueType enum."""
|
|
8
|
+
|
|
9
|
+
FLOAT32 = 0
|
|
10
|
+
FLOAT64 = 1
|
|
11
|
+
INT8 = 2
|
|
12
|
+
INT16 = 3
|
|
13
|
+
INT32 = 4
|
|
14
|
+
INT64 = 5
|
|
15
|
+
UINT8 = 6
|
|
16
|
+
UINT16 = 7
|
|
17
|
+
UINT32 = 8
|
|
18
|
+
UINT64 = 9
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Internal mapping: ValueType -> (struct format, byte size)
|
|
22
|
+
DTYPE_INFO: dict[ValueType, tuple[str, int]] = {
|
|
23
|
+
ValueType.FLOAT32: ("<f", 4),
|
|
24
|
+
ValueType.FLOAT64: ("<d", 8),
|
|
25
|
+
ValueType.INT8: ("<b", 1),
|
|
26
|
+
ValueType.INT16: ("<h", 2),
|
|
27
|
+
ValueType.INT32: ("<i", 4),
|
|
28
|
+
ValueType.INT64: ("<q", 8),
|
|
29
|
+
ValueType.UINT8: ("<B", 1),
|
|
30
|
+
ValueType.UINT16: ("<H", 2),
|
|
31
|
+
ValueType.UINT32: ("<I", 4),
|
|
32
|
+
ValueType.UINT64: ("<Q", 8),
|
|
33
|
+
}
|
|
File without changes
|