hypha-rpc 0.20.92__tar.gz → 0.20.93__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.
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/PKG-INFO +1 -1
- hypha_rpc-0.20.93/hypha_rpc/VERSION +3 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/http_client.py +21 -5
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc.egg-info/PKG-INFO +1 -1
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc.egg-info/SOURCES.txt +1 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/pyproject.toml +1 -1
- hypha_rpc-0.20.93/tests/test_http_rpc.py +422 -0
- hypha_rpc-0.20.92/hypha_rpc/VERSION +0 -3
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/MANIFEST.in +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/README.md +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/__init__.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/pyodide_sse.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/pyodide_websocket.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/rpc.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/sync.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/utils/__init__.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/utils/launch.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/utils/mcp.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/utils/pydantic.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/utils/schema.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/utils/serve.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/webrtc_client.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc/websocket_client.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc.egg-info/dependency_links.txt +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc.egg-info/requires.txt +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/hypha_rpc.egg-info/top_level.txt +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/setup.cfg +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/tests/test_mcp.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/tests/test_reconnection_runner.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/tests/test_reconnection_stability.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/tests/test_schema.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/tests/test_server_compatibility.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/tests/test_utils.py +0 -0
- {hypha_rpc-0.20.92 → hypha_rpc-0.20.93}/tests/test_websocket_rpc.py +0 -0
|
@@ -174,15 +174,26 @@ class HTTPStreamingRPCConnection:
|
|
|
174
174
|
elif self._ssl is not None:
|
|
175
175
|
verify = self._ssl
|
|
176
176
|
|
|
177
|
+
# Try to enable HTTP/2 if h2 is available
|
|
178
|
+
try:
|
|
179
|
+
import h2 # noqa
|
|
180
|
+
http2_enabled = True
|
|
181
|
+
logger.info("HTTP/2 enabled for improved performance")
|
|
182
|
+
except ImportError:
|
|
183
|
+
http2_enabled = False
|
|
184
|
+
logger.debug("HTTP/2 not available (install httpx[http2] for better performance)")
|
|
185
|
+
|
|
177
186
|
return httpx.AsyncClient(
|
|
178
187
|
timeout=httpx.Timeout(self._timeout, connect=30.0),
|
|
179
188
|
verify=verify,
|
|
180
|
-
#
|
|
189
|
+
# Optimized connection pooling for high-performance RPC
|
|
181
190
|
limits=httpx.Limits(
|
|
182
|
-
max_connections=
|
|
183
|
-
max_keepalive_connections=
|
|
184
|
-
keepalive_expiry=
|
|
191
|
+
max_connections=200, # Max total connections (increased for parallel requests)
|
|
192
|
+
max_keepalive_connections=50, # More reusable connections (up from 20)
|
|
193
|
+
keepalive_expiry=300.0, # Keep connections alive longer (5 minutes)
|
|
185
194
|
),
|
|
195
|
+
# Enable HTTP/2 for better multiplexing if available
|
|
196
|
+
http2=http2_enabled,
|
|
186
197
|
)
|
|
187
198
|
|
|
188
199
|
async def open(self):
|
|
@@ -418,7 +429,11 @@ class HTTPStreamingRPCConnection:
|
|
|
418
429
|
self._handle_message(data)
|
|
419
430
|
|
|
420
431
|
async def emit_message(self, data: bytes):
|
|
421
|
-
"""Send a message to the server via HTTP POST.
|
|
432
|
+
"""Send a message to the server via HTTP POST.
|
|
433
|
+
|
|
434
|
+
Uses optimized connection pooling with keep-alive for better performance.
|
|
435
|
+
HTTP client automatically handles efficient transfer for all payload sizes.
|
|
436
|
+
"""
|
|
422
437
|
if self._closed:
|
|
423
438
|
raise ConnectionError("Connection is closed")
|
|
424
439
|
|
|
@@ -430,6 +445,7 @@ class HTTPStreamingRPCConnection:
|
|
|
430
445
|
params = {"client_id": self._client_id}
|
|
431
446
|
|
|
432
447
|
try:
|
|
448
|
+
# httpx handles large payloads efficiently with connection pooling
|
|
433
449
|
response = await self._http_client.post(
|
|
434
450
|
url,
|
|
435
451
|
content=data,
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hypha_rpc"
|
|
7
|
-
version = "0.20.
|
|
7
|
+
version = "0.20.93"
|
|
8
8
|
description = "Hypha RPC client for connecting to Hypha server for data management and AI model serving"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
"""Test HTTP RPC transport for hypha-rpc standalone tests.
|
|
2
|
+
|
|
3
|
+
These tests connect to a remote Hypha server to test HTTP transport functionality.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
import numpy as np
|
|
8
|
+
import asyncio
|
|
9
|
+
from hypha_rpc import connect_to_server
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Use public test server - these tests require a running Hypha server
|
|
13
|
+
SERVER_URL = "https://hypha.aicell.io"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestHTTPObjectTransmission:
|
|
17
|
+
"""Test HTTP transport with complex objects and callbacks."""
|
|
18
|
+
|
|
19
|
+
@pytest.mark.asyncio
|
|
20
|
+
async def test_http_numpy_array_transmission(self):
|
|
21
|
+
"""Test transmitting numpy arrays over HTTP transport."""
|
|
22
|
+
# Service provider via WebSocket
|
|
23
|
+
ws_server = await connect_to_server({
|
|
24
|
+
"server_url": SERVER_URL,
|
|
25
|
+
"client_id": "numpy-provider-ws-test",
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
workspace = ws_server.config["workspace"]
|
|
30
|
+
|
|
31
|
+
# Register service that works with numpy arrays
|
|
32
|
+
await ws_server.register_service({
|
|
33
|
+
"id": "numpy-service",
|
|
34
|
+
"name": "Numpy Service",
|
|
35
|
+
"config": {"visibility": "public"},
|
|
36
|
+
"process_array": lambda arr: {
|
|
37
|
+
"shape": list(arr.shape), # Convert to list for JSON
|
|
38
|
+
"dtype": str(arr.dtype),
|
|
39
|
+
"sum": float(np.sum(arr)),
|
|
40
|
+
"mean": float(np.mean(arr)),
|
|
41
|
+
"result_array": arr * 2, # Return modified array
|
|
42
|
+
},
|
|
43
|
+
"reshape": lambda arr, shape: np.reshape(arr, shape),
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
token = await ws_server.generate_token()
|
|
47
|
+
|
|
48
|
+
# HTTP client connects
|
|
49
|
+
http_server = await connect_to_server({
|
|
50
|
+
"server_url": SERVER_URL,
|
|
51
|
+
"workspace": workspace,
|
|
52
|
+
"client_id": "numpy-consumer-http-test",
|
|
53
|
+
"transport": "http",
|
|
54
|
+
"token": token,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
service = await http_server.get_service(f"{ws_server.config['client_id']}:numpy-service")
|
|
59
|
+
|
|
60
|
+
# Test 1: Send and receive numpy array
|
|
61
|
+
test_array = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32)
|
|
62
|
+
result = await service.process_array(test_array)
|
|
63
|
+
|
|
64
|
+
assert result["shape"] == [2, 3]
|
|
65
|
+
assert result["dtype"] == "float32"
|
|
66
|
+
assert result["sum"] == 21.0
|
|
67
|
+
assert result["mean"] == 3.5
|
|
68
|
+
|
|
69
|
+
result_array = result["result_array"]
|
|
70
|
+
assert isinstance(result_array, np.ndarray)
|
|
71
|
+
assert np.array_equal(result_array, test_array * 2)
|
|
72
|
+
|
|
73
|
+
# Test 2: Large array
|
|
74
|
+
large_array = np.random.rand(100, 100)
|
|
75
|
+
result2 = await service.process_array(large_array)
|
|
76
|
+
assert result2["shape"] == [100, 100]
|
|
77
|
+
|
|
78
|
+
# Test 3: Reshape operation
|
|
79
|
+
flat_array = np.arange(12)
|
|
80
|
+
reshaped = await service.reshape(flat_array, (3, 4))
|
|
81
|
+
assert reshaped.shape == (3, 4)
|
|
82
|
+
assert np.array_equal(reshaped, np.arange(12).reshape(3, 4))
|
|
83
|
+
|
|
84
|
+
finally:
|
|
85
|
+
await http_server.disconnect()
|
|
86
|
+
|
|
87
|
+
finally:
|
|
88
|
+
await ws_server.disconnect()
|
|
89
|
+
|
|
90
|
+
@pytest.mark.asyncio
|
|
91
|
+
async def test_http_nested_objects_transmission(self):
|
|
92
|
+
"""Test transmitting nested complex objects over HTTP."""
|
|
93
|
+
ws_server = await connect_to_server({
|
|
94
|
+
"server_url": SERVER_URL,
|
|
95
|
+
"client_id": "nested-provider-ws-test",
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
workspace = ws_server.config["workspace"]
|
|
100
|
+
|
|
101
|
+
# Service that handles nested objects
|
|
102
|
+
await ws_server.register_service({
|
|
103
|
+
"id": "nested-service",
|
|
104
|
+
"name": "Nested Object Service",
|
|
105
|
+
"config": {"visibility": "public"},
|
|
106
|
+
"process_nested": lambda data: {
|
|
107
|
+
"received_keys": list(data.keys()),
|
|
108
|
+
"array_sum": float(np.sum(data["array"])) if "array" in data else 0,
|
|
109
|
+
"nested_count": len(data.get("nested", {}).get("items", [])),
|
|
110
|
+
"echo": data,
|
|
111
|
+
},
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
token = await ws_server.generate_token()
|
|
115
|
+
|
|
116
|
+
http_server = await connect_to_server({
|
|
117
|
+
"server_url": SERVER_URL,
|
|
118
|
+
"workspace": workspace,
|
|
119
|
+
"client_id": "nested-consumer-http-test",
|
|
120
|
+
"transport": "http",
|
|
121
|
+
"token": token,
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
service = await http_server.get_service(f"{ws_server.config['client_id']}:nested-service")
|
|
126
|
+
|
|
127
|
+
# Complex nested structure
|
|
128
|
+
test_data = {
|
|
129
|
+
"string": "test",
|
|
130
|
+
"number": 42,
|
|
131
|
+
"array": np.array([1, 2, 3, 4, 5]),
|
|
132
|
+
"nested": {
|
|
133
|
+
"items": [1, 2, 3],
|
|
134
|
+
"metadata": {
|
|
135
|
+
"name": "test_item",
|
|
136
|
+
"values": [10, 20, 30],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
"list_of_arrays": [
|
|
140
|
+
np.array([1, 2]),
|
|
141
|
+
np.array([3, 4]),
|
|
142
|
+
],
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
result = await service.process_nested(test_data)
|
|
146
|
+
|
|
147
|
+
assert set(result["received_keys"]) == set(test_data.keys())
|
|
148
|
+
assert result["array_sum"] == 15.0
|
|
149
|
+
assert result["nested_count"] == 3
|
|
150
|
+
|
|
151
|
+
# Verify echo preserves structure
|
|
152
|
+
echo = result["echo"]
|
|
153
|
+
assert echo["string"] == "test"
|
|
154
|
+
assert echo["number"] == 42
|
|
155
|
+
assert np.array_equal(echo["array"], test_data["array"])
|
|
156
|
+
assert echo["nested"]["metadata"]["name"] == "test_item"
|
|
157
|
+
|
|
158
|
+
finally:
|
|
159
|
+
await http_server.disconnect()
|
|
160
|
+
|
|
161
|
+
finally:
|
|
162
|
+
await ws_server.disconnect()
|
|
163
|
+
|
|
164
|
+
@pytest.mark.asyncio
|
|
165
|
+
async def test_http_callbacks_basic(self):
|
|
166
|
+
"""Test basic callback functionality over HTTP transport."""
|
|
167
|
+
ws_server = await connect_to_server({
|
|
168
|
+
"server_url": SERVER_URL,
|
|
169
|
+
"client_id": "callback-provider-ws-test",
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
workspace = ws_server.config["workspace"]
|
|
174
|
+
|
|
175
|
+
# Service that uses callbacks
|
|
176
|
+
async def call_multiple_times(callback, count):
|
|
177
|
+
results = []
|
|
178
|
+
for i in range(count):
|
|
179
|
+
result = await callback(i)
|
|
180
|
+
results.append(result)
|
|
181
|
+
return results
|
|
182
|
+
|
|
183
|
+
async def process_with_progress(data, progress_callback):
|
|
184
|
+
for i in range(len(data)):
|
|
185
|
+
await progress_callback({"step": i, "total": len(data)})
|
|
186
|
+
return sum(data)
|
|
187
|
+
|
|
188
|
+
await ws_server.register_service({
|
|
189
|
+
"id": "callback-service",
|
|
190
|
+
"name": "Callback Service",
|
|
191
|
+
"config": {"visibility": "public"},
|
|
192
|
+
"call_multiple_times": call_multiple_times,
|
|
193
|
+
"process_with_progress": process_with_progress,
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
token = await ws_server.generate_token()
|
|
197
|
+
|
|
198
|
+
http_server = await connect_to_server({
|
|
199
|
+
"server_url": SERVER_URL,
|
|
200
|
+
"workspace": workspace,
|
|
201
|
+
"client_id": "callback-consumer-http-test",
|
|
202
|
+
"transport": "http",
|
|
203
|
+
"token": token,
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
service = await http_server.get_service(f"{ws_server.config['client_id']}:callback-service")
|
|
208
|
+
|
|
209
|
+
# Test 1: Simple callback
|
|
210
|
+
callback_results = []
|
|
211
|
+
|
|
212
|
+
def test_callback(value):
|
|
213
|
+
callback_results.append(value)
|
|
214
|
+
return value * 2
|
|
215
|
+
|
|
216
|
+
results = await service.call_multiple_times(test_callback, 5)
|
|
217
|
+
assert len(callback_results) == 5
|
|
218
|
+
assert callback_results == [0, 1, 2, 3, 4]
|
|
219
|
+
assert results == [0, 2, 4, 6, 8]
|
|
220
|
+
|
|
221
|
+
# Test 2: Progress callback
|
|
222
|
+
progress_updates = []
|
|
223
|
+
|
|
224
|
+
def progress_callback(info):
|
|
225
|
+
progress_updates.append(info)
|
|
226
|
+
|
|
227
|
+
test_data = [10, 20, 30, 40]
|
|
228
|
+
result = await service.process_with_progress(test_data, progress_callback)
|
|
229
|
+
assert result == 100
|
|
230
|
+
assert len(progress_updates) == 4
|
|
231
|
+
assert progress_updates[0] == {"step": 0, "total": 4}
|
|
232
|
+
assert progress_updates[-1] == {"step": 3, "total": 4}
|
|
233
|
+
|
|
234
|
+
finally:
|
|
235
|
+
await http_server.disconnect()
|
|
236
|
+
|
|
237
|
+
finally:
|
|
238
|
+
await ws_server.disconnect()
|
|
239
|
+
|
|
240
|
+
@pytest.mark.asyncio
|
|
241
|
+
async def test_http_async_callbacks(self):
|
|
242
|
+
"""Test async callback functionality over HTTP transport."""
|
|
243
|
+
ws_server = await connect_to_server({
|
|
244
|
+
"server_url": SERVER_URL,
|
|
245
|
+
"client_id": "async-callback-provider-ws-test",
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
try:
|
|
249
|
+
workspace = ws_server.config["workspace"]
|
|
250
|
+
|
|
251
|
+
# Service with async callback support
|
|
252
|
+
async def process_async_callback(items, async_callback):
|
|
253
|
+
results = []
|
|
254
|
+
for item in items:
|
|
255
|
+
result = await async_callback(item)
|
|
256
|
+
results.append(result)
|
|
257
|
+
return results
|
|
258
|
+
|
|
259
|
+
await ws_server.register_service({
|
|
260
|
+
"id": "async-callback-service",
|
|
261
|
+
"name": "Async Callback Service",
|
|
262
|
+
"config": {"visibility": "public"},
|
|
263
|
+
"process_async": process_async_callback,
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
token = await ws_server.generate_token()
|
|
267
|
+
|
|
268
|
+
http_server = await connect_to_server({
|
|
269
|
+
"server_url": SERVER_URL,
|
|
270
|
+
"workspace": workspace,
|
|
271
|
+
"client_id": "async-callback-consumer-http-test",
|
|
272
|
+
"transport": "http",
|
|
273
|
+
"token": token,
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
try:
|
|
277
|
+
service = await http_server.get_service(f"{ws_server.config['client_id']}:async-callback-service")
|
|
278
|
+
|
|
279
|
+
# Async callback
|
|
280
|
+
async def async_transform(value):
|
|
281
|
+
await asyncio.sleep(0.01) # Simulate async work
|
|
282
|
+
return value ** 2
|
|
283
|
+
|
|
284
|
+
test_items = [1, 2, 3, 4, 5]
|
|
285
|
+
results = await service.process_async(test_items, async_transform)
|
|
286
|
+
assert results == [1, 4, 9, 16, 25]
|
|
287
|
+
|
|
288
|
+
finally:
|
|
289
|
+
await http_server.disconnect()
|
|
290
|
+
|
|
291
|
+
finally:
|
|
292
|
+
await ws_server.disconnect()
|
|
293
|
+
|
|
294
|
+
@pytest.mark.asyncio
|
|
295
|
+
async def test_http_callback_with_numpy(self):
|
|
296
|
+
"""Test callbacks that pass numpy arrays over HTTP."""
|
|
297
|
+
ws_server = await connect_to_server({
|
|
298
|
+
"server_url": SERVER_URL,
|
|
299
|
+
"client_id": "numpy-callback-provider-ws-test",
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
try:
|
|
303
|
+
workspace = ws_server.config["workspace"]
|
|
304
|
+
|
|
305
|
+
# Service that sends arrays to callbacks
|
|
306
|
+
async def transform_batch(arrays, transform_callback):
|
|
307
|
+
results = []
|
|
308
|
+
for arr in arrays:
|
|
309
|
+
result = await transform_callback(arr)
|
|
310
|
+
results.append(result)
|
|
311
|
+
return results
|
|
312
|
+
|
|
313
|
+
await ws_server.register_service({
|
|
314
|
+
"id": "numpy-callback-service",
|
|
315
|
+
"name": "Numpy Callback Service",
|
|
316
|
+
"config": {"visibility": "public"},
|
|
317
|
+
"transform_batch": transform_batch,
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
token = await ws_server.generate_token()
|
|
321
|
+
|
|
322
|
+
http_server = await connect_to_server({
|
|
323
|
+
"server_url": SERVER_URL,
|
|
324
|
+
"workspace": workspace,
|
|
325
|
+
"client_id": "numpy-callback-consumer-http-test",
|
|
326
|
+
"transport": "http",
|
|
327
|
+
"token": token,
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
try:
|
|
331
|
+
service = await http_server.get_service(f"{ws_server.config['client_id']}:numpy-callback-service")
|
|
332
|
+
|
|
333
|
+
# Callback that processes numpy arrays
|
|
334
|
+
def array_processor(arr):
|
|
335
|
+
return {
|
|
336
|
+
"sum": float(np.sum(arr)),
|
|
337
|
+
"modified": arr * 3,
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
test_arrays = [
|
|
341
|
+
np.array([1, 2, 3]),
|
|
342
|
+
np.array([4, 5, 6]),
|
|
343
|
+
np.array([7, 8, 9]),
|
|
344
|
+
]
|
|
345
|
+
|
|
346
|
+
results = await service.transform_batch(test_arrays, array_processor)
|
|
347
|
+
|
|
348
|
+
assert len(results) == 3
|
|
349
|
+
assert results[0]["sum"] == 6.0
|
|
350
|
+
assert results[1]["sum"] == 15.0
|
|
351
|
+
assert results[2]["sum"] == 24.0
|
|
352
|
+
|
|
353
|
+
assert np.array_equal(results[0]["modified"], np.array([3, 6, 9]))
|
|
354
|
+
assert np.array_equal(results[1]["modified"], np.array([12, 15, 18]))
|
|
355
|
+
assert np.array_equal(results[2]["modified"], np.array([21, 24, 27]))
|
|
356
|
+
|
|
357
|
+
finally:
|
|
358
|
+
await http_server.disconnect()
|
|
359
|
+
|
|
360
|
+
finally:
|
|
361
|
+
await ws_server.disconnect()
|
|
362
|
+
|
|
363
|
+
@pytest.mark.asyncio
|
|
364
|
+
async def test_http_binary_data_transmission(self):
|
|
365
|
+
"""Test transmitting raw binary data over HTTP."""
|
|
366
|
+
ws_server = await connect_to_server({
|
|
367
|
+
"server_url": SERVER_URL,
|
|
368
|
+
"client_id": "binary-provider-ws-test",
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
workspace = ws_server.config["workspace"]
|
|
373
|
+
|
|
374
|
+
# Service that handles binary data
|
|
375
|
+
await ws_server.register_service({
|
|
376
|
+
"id": "binary-service",
|
|
377
|
+
"name": "Binary Service",
|
|
378
|
+
"config": {"visibility": "public"},
|
|
379
|
+
"process_binary": lambda data: {
|
|
380
|
+
"length": len(data),
|
|
381
|
+
"first_bytes": data[:10],
|
|
382
|
+
"reversed": bytes(reversed(data)),
|
|
383
|
+
},
|
|
384
|
+
"concat_binary": lambda parts: b"".join(parts),
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
token = await ws_server.generate_token()
|
|
388
|
+
|
|
389
|
+
http_server = await connect_to_server({
|
|
390
|
+
"server_url": SERVER_URL,
|
|
391
|
+
"workspace": workspace,
|
|
392
|
+
"client_id": "binary-consumer-http-test",
|
|
393
|
+
"transport": "http",
|
|
394
|
+
"token": token,
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
try:
|
|
398
|
+
service = await http_server.get_service(f"{ws_server.config['client_id']}:binary-service")
|
|
399
|
+
|
|
400
|
+
# Test 1: Send binary data
|
|
401
|
+
test_data = b"Hello, World! This is binary data."
|
|
402
|
+
result = await service.process_binary(test_data)
|
|
403
|
+
|
|
404
|
+
assert result["length"] == len(test_data)
|
|
405
|
+
assert result["first_bytes"] == test_data[:10]
|
|
406
|
+
assert result["reversed"] == bytes(reversed(test_data))
|
|
407
|
+
|
|
408
|
+
# Test 2: Multiple binary chunks
|
|
409
|
+
parts = [b"Part1", b"Part2", b"Part3"]
|
|
410
|
+
concatenated = await service.concat_binary(parts)
|
|
411
|
+
assert concatenated == b"Part1Part2Part3"
|
|
412
|
+
|
|
413
|
+
finally:
|
|
414
|
+
await http_server.disconnect()
|
|
415
|
+
|
|
416
|
+
finally:
|
|
417
|
+
await ws_server.disconnect()
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
if __name__ == "__main__":
|
|
421
|
+
# Allow running tests directly
|
|
422
|
+
pytest.main([__file__, "-v", "-s"])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|