QuLab 2.10.10__cp313-cp313-win_amd64.whl

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 (107) hide show
  1. qulab/__init__.py +33 -0
  2. qulab/__main__.py +4 -0
  3. qulab/cli/__init__.py +0 -0
  4. qulab/cli/commands.py +30 -0
  5. qulab/cli/config.py +170 -0
  6. qulab/cli/decorators.py +28 -0
  7. qulab/dicttree.py +523 -0
  8. qulab/executor/__init__.py +5 -0
  9. qulab/executor/analyze.py +188 -0
  10. qulab/executor/cli.py +434 -0
  11. qulab/executor/load.py +563 -0
  12. qulab/executor/registry.py +185 -0
  13. qulab/executor/schedule.py +543 -0
  14. qulab/executor/storage.py +615 -0
  15. qulab/executor/template.py +259 -0
  16. qulab/executor/utils.py +194 -0
  17. qulab/expression.py +827 -0
  18. qulab/fun.cp313-win_amd64.pyd +0 -0
  19. qulab/monitor/__init__.py +1 -0
  20. qulab/monitor/__main__.py +8 -0
  21. qulab/monitor/config.py +41 -0
  22. qulab/monitor/dataset.py +77 -0
  23. qulab/monitor/event_queue.py +54 -0
  24. qulab/monitor/mainwindow.py +234 -0
  25. qulab/monitor/monitor.py +115 -0
  26. qulab/monitor/ploter.py +123 -0
  27. qulab/monitor/qt_compat.py +16 -0
  28. qulab/monitor/toolbar.py +265 -0
  29. qulab/scan/__init__.py +2 -0
  30. qulab/scan/curd.py +221 -0
  31. qulab/scan/models.py +554 -0
  32. qulab/scan/optimize.py +76 -0
  33. qulab/scan/query.py +387 -0
  34. qulab/scan/record.py +603 -0
  35. qulab/scan/scan.py +1166 -0
  36. qulab/scan/server.py +450 -0
  37. qulab/scan/space.py +213 -0
  38. qulab/scan/utils.py +234 -0
  39. qulab/storage/__init__.py +0 -0
  40. qulab/storage/__main__.py +51 -0
  41. qulab/storage/backend/__init__.py +0 -0
  42. qulab/storage/backend/redis.py +204 -0
  43. qulab/storage/base_dataset.py +352 -0
  44. qulab/storage/chunk.py +60 -0
  45. qulab/storage/dataset.py +127 -0
  46. qulab/storage/file.py +273 -0
  47. qulab/storage/models/__init__.py +22 -0
  48. qulab/storage/models/base.py +4 -0
  49. qulab/storage/models/config.py +28 -0
  50. qulab/storage/models/file.py +89 -0
  51. qulab/storage/models/ipy.py +58 -0
  52. qulab/storage/models/models.py +88 -0
  53. qulab/storage/models/record.py +161 -0
  54. qulab/storage/models/report.py +22 -0
  55. qulab/storage/models/tag.py +93 -0
  56. qulab/storage/storage.py +95 -0
  57. qulab/sys/__init__.py +2 -0
  58. qulab/sys/chat.py +688 -0
  59. qulab/sys/device/__init__.py +3 -0
  60. qulab/sys/device/basedevice.py +255 -0
  61. qulab/sys/device/loader.py +86 -0
  62. qulab/sys/device/utils.py +79 -0
  63. qulab/sys/drivers/FakeInstrument.py +68 -0
  64. qulab/sys/drivers/__init__.py +0 -0
  65. qulab/sys/ipy_events.py +125 -0
  66. qulab/sys/net/__init__.py +0 -0
  67. qulab/sys/net/bencoder.py +205 -0
  68. qulab/sys/net/cli.py +169 -0
  69. qulab/sys/net/dhcp.py +543 -0
  70. qulab/sys/net/dhcpd.py +176 -0
  71. qulab/sys/net/kad.py +1142 -0
  72. qulab/sys/net/kcp.py +192 -0
  73. qulab/sys/net/nginx.py +194 -0
  74. qulab/sys/progress.py +190 -0
  75. qulab/sys/rpc/__init__.py +0 -0
  76. qulab/sys/rpc/client.py +0 -0
  77. qulab/sys/rpc/exceptions.py +96 -0
  78. qulab/sys/rpc/msgpack.py +1052 -0
  79. qulab/sys/rpc/msgpack.pyi +41 -0
  80. qulab/sys/rpc/router.py +35 -0
  81. qulab/sys/rpc/rpc.py +412 -0
  82. qulab/sys/rpc/serialize.py +139 -0
  83. qulab/sys/rpc/server.py +29 -0
  84. qulab/sys/rpc/socket.py +29 -0
  85. qulab/sys/rpc/utils.py +25 -0
  86. qulab/sys/rpc/worker.py +0 -0
  87. qulab/sys/rpc/zmq_socket.py +227 -0
  88. qulab/tools/__init__.py +0 -0
  89. qulab/tools/connection_helper.py +39 -0
  90. qulab/typing.py +2 -0
  91. qulab/utils.py +95 -0
  92. qulab/version.py +1 -0
  93. qulab/visualization/__init__.py +188 -0
  94. qulab/visualization/__main__.py +71 -0
  95. qulab/visualization/_autoplot.py +464 -0
  96. qulab/visualization/plot_circ.py +319 -0
  97. qulab/visualization/plot_layout.py +408 -0
  98. qulab/visualization/plot_seq.py +242 -0
  99. qulab/visualization/qdat.py +152 -0
  100. qulab/visualization/rot3d.py +23 -0
  101. qulab/visualization/widgets.py +86 -0
  102. qulab-2.10.10.dist-info/METADATA +110 -0
  103. qulab-2.10.10.dist-info/RECORD +107 -0
  104. qulab-2.10.10.dist-info/WHEEL +5 -0
  105. qulab-2.10.10.dist-info/entry_points.txt +2 -0
  106. qulab-2.10.10.dist-info/licenses/LICENSE +21 -0
  107. qulab-2.10.10.dist-info/top_level.txt +1 -0
@@ -0,0 +1,41 @@
1
+ from typing import Any
2
+
3
+ __version__: str
4
+
5
+ version: tuple[int, int, int]
6
+
7
+ def pack(obj, fp, **options) -> None: ...
8
+ def packb(obj, **options) -> bytes: ...
9
+ def dump(obj, fp, **options) -> None: ...
10
+ def dumps(obj, **options) -> bytes: ...
11
+
12
+ def unpackb(s: bytes | bytearray, **options) -> Any: ...
13
+ def unpack(fp, **options) -> Any: ...
14
+ def loads(s: bytes | bytearray, **options) -> Any: ...
15
+ def load(fp, **options) -> Any: ...
16
+
17
+ class Ext:
18
+ type: int
19
+ data: bytes
20
+ def __init__(self, type: int, data: bytes) -> None: ...
21
+ def __eq__(self, other) -> bool: ...
22
+ def __ne__(self, other) -> bool: ...
23
+ def __hash__(self) -> int: ...
24
+
25
+ class InvalidString(bytes): ...
26
+
27
+ def ext_serializable(ext_type: int): ...
28
+
29
+ class PackException(Exception): ...
30
+ class UnpackException(Exception): ...
31
+ class UnsupportedTypeException(PackException): ...
32
+ class InsufficientDataException(UnpackException): ...
33
+ class InvalidStringException(UnpackException): ...
34
+ class UnsupportedTimestampException(UnpackException): ...
35
+ class ReservedCodeException(UnpackException): ...
36
+ class UnhashableKeyException(UnpackException): ...
37
+ class DuplicateKeyException(UnpackException): ...
38
+ KeyNotPrimitiveException = UnhashableKeyException
39
+ KeyDuplicateException = DuplicateKeyException
40
+
41
+ compatibility: bool
@@ -0,0 +1,35 @@
1
+ import asyncio
2
+ import random
3
+
4
+ import zmq
5
+ import zmq.asyncio
6
+
7
+
8
+ async def handle_client(socket, identity, message):
9
+ print(f"Received request from {identity}: {message.decode()}")
10
+ # 随机延时 0 到 3 秒
11
+ await asyncio.sleep(random.uniform(0, 3))
12
+ task_id = random.randint(1000, 9999) # 随机生成一个任务 ID
13
+ await socket.send_multipart([identity, f"Task ID: {task_id}".encode()])
14
+ print(f"Sent Task ID {task_id} to {identity}")
15
+
16
+
17
+ async def server():
18
+ context = zmq.asyncio.Context()
19
+ socket = context.socket(zmq.ROUTER)
20
+ socket.bind("tcp://*:5555")
21
+
22
+ while True:
23
+ try:
24
+ identity, message = await socket.recv_multipart()
25
+ asyncio.create_task(handle_client(socket, identity, message))
26
+ except Exception as e:
27
+ print(f"An error occurred: {e}")
28
+ break
29
+
30
+ socket.close()
31
+ context.term()
32
+
33
+
34
+ if __name__ == "__main__":
35
+ asyncio.run(server())
qulab/sys/rpc/rpc.py ADDED
@@ -0,0 +1,412 @@
1
+ import asyncio
2
+ import functools
3
+ import inspect
4
+ import logging
5
+ import platform
6
+ import struct
7
+ from abc import ABC, abstractmethod
8
+ from collections.abc import Awaitable
9
+
10
+ from ...version import __version__
11
+ from .exceptions import RPCError, RPCServerError, RPCTimeout
12
+ from .serialize import pack, unpack
13
+ from .utils import acceptArg
14
+
15
+ if platform.system() == "Windows":
16
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
17
+
18
+ log = logging.getLogger(__name__) # pylint: disable=invalid-name
19
+
20
+ msgIDFormat = struct.Struct("!IIQ")
21
+
22
+ __msgIndex = 1024
23
+
24
+
25
+ def nextMsgID(clientID, sessionID=0):
26
+ global __msgIndex
27
+ __msgIndex += 1
28
+ return msgIDFormat.pack(clientID, sessionID, __msgIndex)
29
+
30
+
31
+ def parseMsgID(msgID):
32
+ """
33
+ return: (clientID, sessionID, msgIndex)
34
+ """
35
+ return msgIDFormat.unpack(msgID)
36
+
37
+
38
+ # message type
39
+
40
+ RPC_REQUEST = b'\x01'
41
+ RPC_RESPONSE = b'\x02'
42
+ RPC_PING = b'\x03'
43
+ RPC_PONG = b'\x04'
44
+ RPC_CANCEL = b'\x05'
45
+ RPC_SHUTDOWN = b'\x06'
46
+ RPC_CONNECT = b'\x07'
47
+ RPC_WELCOME = b'\x08'
48
+
49
+ RPC_MSGIDSIZE = msgIDFormat.size
50
+
51
+
52
+ class RPCMixin(ABC):
53
+ info = {}
54
+
55
+ def start(self):
56
+ pass
57
+
58
+ def stop(self):
59
+ pass
60
+
61
+ def close(self):
62
+ pass
63
+
64
+ @property
65
+ @abstractmethod
66
+ def loop(self):
67
+ """
68
+ Event loop.
69
+ """
70
+
71
+ @abstractmethod
72
+ async def sendto(self, data, address):
73
+ """
74
+ Send message to address.
75
+ """
76
+
77
+ __rpc_handlers = {
78
+ RPC_CONNECT: 'on_connect',
79
+ RPC_WELCOME: 'on_welcome',
80
+ RPC_PING: 'on_ping',
81
+ RPC_PONG: 'on_pong',
82
+ RPC_REQUEST: 'on_request',
83
+ RPC_RESPONSE: 'on_response',
84
+ RPC_CANCEL: 'on_cancel',
85
+ RPC_SHUTDOWN: 'on_shutdown',
86
+ }
87
+
88
+ def parseData(self, data):
89
+ msg_type, msg = data[:1], data[1:]
90
+ if msg_type in [RPC_PING, RPC_PONG, RPC_CONNECT, RPC_WELCOME]:
91
+ return msg_type, msg
92
+ elif msg_type in [RPC_REQUEST, RPC_RESPONSE, RPC_CANCEL, RPC_SHUTDOWN]:
93
+ msgID, msg = msg[:RPC_MSGIDSIZE], msg[RPC_MSGIDSIZE:]
94
+ return msg_type, msgID, msg
95
+ # elif msg_type in [RPC_LONGREQUEST, RPC_LONGRESPONSE]:
96
+ # msgID, sessionID, msg = msg[:20], msg[20:40], msg[40:]
97
+ # return msg_type, msgID, sessionID, msg
98
+ else:
99
+ raise RPCError(f'Unkown message type {msg_type}.')
100
+
101
+ def handle(self, source, data):
102
+ """
103
+ Handle received data.
104
+
105
+ Should be called whenever received data from outside.
106
+ """
107
+ msg_type, *args = self.parseData(data)
108
+ log.debug(f'received request {msg_type} from {source}')
109
+ handler = self.__rpc_handlers.get(msg_type, None)
110
+ if handler is not None:
111
+ getattr(self, handler)(source, *args)
112
+ else:
113
+ log.error(f'No handler found for request {msg_type} from {source}')
114
+
115
+ async def pong(self, addr):
116
+ await self.sendto(RPC_PONG, addr)
117
+
118
+ def on_ping(self, source, msg):
119
+ log.debug(f"received ping from {source}")
120
+ asyncio.ensure_future(self.pong(source), loop=self.loop)
121
+
122
+
123
+ class RPCClientMixin(RPCMixin):
124
+ _client_defualt_timeout = 10
125
+ __pending = None
126
+ __clientID = 1
127
+
128
+ @property
129
+ def pending(self):
130
+ if self.__pending is None:
131
+ self.__pending = {}
132
+ return self.__pending
133
+
134
+ @property
135
+ def clientID(self):
136
+ return self.__clientID
137
+
138
+ def createPending(self, addr, msgID, timeout=1, cancelRemote=True):
139
+ """
140
+ Create a future for request, wait response before timeout.
141
+ """
142
+ fut = self.loop.create_future()
143
+ self.pending[msgID] = (fut,
144
+ self.loop.call_later(timeout,
145
+ self.cancelPending, addr,
146
+ msgID, cancelRemote))
147
+
148
+ def clean(fut, msgID=msgID):
149
+ if msgID in self.pending:
150
+ del self.pending[msgID]
151
+
152
+ fut.add_done_callback(clean)
153
+
154
+ return fut
155
+
156
+ def cancelPending(self, addr, msgID, cancelRemote):
157
+ """
158
+ Give up when request timeout and try to cancel remote task.
159
+ """
160
+ if msgID in self.pending:
161
+ fut, timeout = self.pending[msgID]
162
+ if cancelRemote:
163
+ self.cancelRemoteTask(addr, msgID)
164
+ if not fut.done():
165
+ fut.set_exception(
166
+ RPCTimeout(
167
+ f'Node({self.info}): Wait response from {addr} timeout.'
168
+ ))
169
+
170
+ def cancelRemoteTask(self, addr, msgID):
171
+ """
172
+ Try to cancel remote task.
173
+ """
174
+ asyncio.ensure_future(self.sendto(RPC_CANCEL + msgID, addr),
175
+ loop=self.loop)
176
+
177
+ def close(self):
178
+ self.stop()
179
+ for fut, timeout in list(self.pending.values()):
180
+ fut.cancel()
181
+ timeout.cancel()
182
+ self.pending.clear()
183
+
184
+ def setTimeout(self, timeout=10):
185
+ self._client_defualt_timeout = timeout
186
+
187
+ def remoteCall(self, addr, methodNane, sessionID=0, args=(), kw={}):
188
+ if 'timeout' in kw:
189
+ timeout = kw['timeout']
190
+ else:
191
+ timeout = self._client_defualt_timeout
192
+ msg = pack((methodNane, args, kw))
193
+ msgID = nextMsgID(self.clientID, sessionID)
194
+ asyncio.ensure_future(self.request(addr, msgID, msg), loop=self.loop)
195
+ return self.createPending(addr, msgID, timeout)
196
+
197
+ async def connect(self, addr, authkey=b"", timeout=None):
198
+ if timeout is None:
199
+ timeout = self._client_defualt_timeout
200
+ await self.sendto(RPC_CONNECT + authkey, addr)
201
+ fut = self.createPending(addr, addr, timeout, False)
202
+ msgID = await fut
203
+ clientID, *_ = parseMsgID(msgID)
204
+ if clientID < 1024:
205
+ raise RPCError(f'Connect {addr} fail')
206
+ self.__clientID = clientID
207
+
208
+ async def ping(self, addr, timeout=1):
209
+ await self.sendto(RPC_PING, addr)
210
+ fut = self.createPending(addr, addr, timeout, False)
211
+ try:
212
+ return await fut
213
+ except RPCTimeout:
214
+ return False
215
+
216
+ async def request(self, address, msgID, msg):
217
+ log.debug(f'send request {address}, {msgID.hex()}, {msg}')
218
+ await self.sendto(RPC_REQUEST + msgID + msg, address)
219
+
220
+ async def shutdown(self, address, roleAuth):
221
+ await self.sendto(RPC_SHUTDOWN + nextMsgID(self.clientID) + roleAuth,
222
+ address)
223
+
224
+ def on_welcome(self, source, msg):
225
+ if source in self.pending:
226
+ fut, timeout = self.pending[source]
227
+ timeout.cancel()
228
+ if not fut.done():
229
+ fut.set_result(msg)
230
+
231
+ def on_pong(self, source, msg):
232
+ log.debug(f"received pong from {source}")
233
+ if source in self.pending:
234
+ fut, timeout = self.pending[source]
235
+ timeout.cancel()
236
+ if not fut.done():
237
+ fut.set_result(True)
238
+
239
+ def on_response(self, source, msgID, msg):
240
+ """
241
+ Client side.
242
+ """
243
+ if msgID not in self.pending:
244
+ return
245
+ fut, timeout = self.pending[msgID]
246
+ timeout.cancel()
247
+ try:
248
+ result = unpack(msg)
249
+ except Exception as e:
250
+ fut.set_exception(e)
251
+ return
252
+ if not fut.done():
253
+ if isinstance(result, Exception):
254
+ fut.set_exception(result)
255
+ else:
256
+ fut.set_result(result)
257
+
258
+
259
+ class RPCServerMixin(RPCMixin):
260
+ __tasks = None
261
+ __sessions = None
262
+ __nextClientID = 1024
263
+ __nextSessionID = 1024
264
+
265
+ def info(self):
266
+ return {'version': __version__}
267
+
268
+ @property
269
+ def nextClientID(self):
270
+ self.__nextClientID += 1
271
+ return self.__nextClientID
272
+
273
+ @property
274
+ def nextSessionID(self):
275
+ self.__nextSessionID += 1
276
+ return self.__nextSessionID
277
+
278
+ @property
279
+ def tasks(self):
280
+ if self.__tasks is None:
281
+ self.__tasks = {}
282
+ return self.__tasks
283
+
284
+ @property
285
+ def sessions(self):
286
+ if self.__sessions is None:
287
+ self.__sessions = {}
288
+ return self.__sessions
289
+
290
+ def createTask(self, msgID, coro, timeout=0):
291
+ """
292
+ Create a new task for msgID.
293
+ """
294
+ if timeout > 0:
295
+ coro = asyncio.wait_for(coro, timeout)
296
+ task = asyncio.ensure_future(coro, loop=self.loop)
297
+ self.tasks[msgID] = task
298
+
299
+ def clean(fut, msgID=msgID):
300
+ if msgID in self.tasks:
301
+ del self.tasks[msgID]
302
+
303
+ task.add_done_callback(clean)
304
+
305
+ def cancelTask(self, msgID):
306
+ """
307
+ Cancel the task for msgID.
308
+ """
309
+ if msgID in self.tasks:
310
+ self.tasks[msgID].cancel()
311
+
312
+ def createSession(self, clientID, obj):
313
+ sessionID = self.nextSessionID
314
+ self.sessions[(clientID, sessionID)] = obj
315
+ return sessionID
316
+
317
+ def removeSession(self, clientID, sessionID):
318
+ del self.sessions[(clientID, sessionID)]
319
+
320
+ def close(self):
321
+ self.stop()
322
+ for task in list(self.tasks.values()):
323
+ task.cancel()
324
+ self.tasks.clear()
325
+
326
+ def _unpack_request(self, msg):
327
+ try:
328
+ method, args, kw = unpack(msg)
329
+ except:
330
+ raise RPCError("Could not read packet: %r" % msg)
331
+ return method, args, kw
332
+
333
+ @property
334
+ def executor(self):
335
+ return None
336
+
337
+ @abstractmethod
338
+ def getRequestHandler(self, methodNane, source, msgID, args=(), kw={}):
339
+ """
340
+ Get suitable handler for request.
341
+
342
+ You should implement this method yourself.
343
+ """
344
+
345
+ def processResult(self, result, method, msgID):
346
+ return result
347
+
348
+ async def handle_request(self, source, msgID, method, args, kw):
349
+ """
350
+ Handle a request from source.
351
+ """
352
+ try:
353
+ func = self.getRequestHandler(method, source=source, msgID=msgID)
354
+ result = await self.callMethod(func, *args, **kw)
355
+ result = self.processResult(result, method, msgID)
356
+ except (RPCError, RPCServerError) as e:
357
+ result = e
358
+ except Exception as e:
359
+ result = RPCServerError.make(e)
360
+ msg = pack(result)
361
+ await self.response(source, msgID, msg)
362
+
363
+ async def callMethod(self, func, *args, **kw):
364
+ if 'timeout' in kw and not acceptArg(func, 'timeout'):
365
+ del kw['timeout']
366
+ if inspect.iscoroutinefunction(func):
367
+ result = await func(*args, **kw)
368
+ else:
369
+ result = await self.loop.run_in_executor(
370
+ self.executor, functools.partial(func, *args, **kw))
371
+ if isinstance(result, Awaitable):
372
+ result = await result
373
+ return result
374
+
375
+ async def response(self, address, msgID, msg):
376
+ log.debug(f'send response {address}, {msgID.hex()}, {msg}')
377
+ await self.sendto(RPC_RESPONSE + msgID + msg, address)
378
+
379
+ def on_connect(self, source, msg):
380
+ log.debug(f"connect from {source}")
381
+ if self.auth(source, msg):
382
+ clientID = self.nextClientID
383
+ else:
384
+ clientID = 0
385
+ msg = pack(self.info())
386
+ asyncio.ensure_future(
387
+ self.sendto(
388
+ RPC_WELCOME + nextMsgID(clientID), # + msg,
389
+ source),
390
+ loop=self.loop)
391
+
392
+ def on_request(self, source, msgID, msg):
393
+ """
394
+ Received a request from source.
395
+ """
396
+ method, args, kw = self._unpack_request(msg)
397
+ self.createTask(msgID,
398
+ self.handle_request(source, msgID, method, args, kw),
399
+ timeout=kw.get('timeout', 0))
400
+
401
+ def on_shutdown(self, source, msgID, roleAuth):
402
+ if self.is_admin(source, roleAuth):
403
+ raise SystemExit(0)
404
+
405
+ def auth(self, source, authkey):
406
+ return True
407
+
408
+ def is_admin(self, source, roleAuth):
409
+ return True
410
+
411
+ def on_cancel(self, source, msgID, msg):
412
+ self.cancelTask(msgID)
@@ -0,0 +1,139 @@
1
+ import pickle
2
+ import zlib
3
+ from typing import Any, Callable, TypeVar
4
+
5
+ import msgpack
6
+
7
+ __index = 0
8
+ __pack_handlers = {}
9
+ __unpack_handlers = {}
10
+ __compress_level = zlib.Z_NO_COMPRESSION
11
+
12
+ cls = TypeVar('cls')
13
+
14
+
15
+ def compress_level(level: int) -> None:
16
+ """
17
+ Set compress level
18
+
19
+ Args:
20
+ level: int
21
+ An integer from 0 to 9 controlling the level of compression;
22
+ 1 is fastest and produces the least compression, 9 is slowest
23
+ and produces the most. 0 is no compression. The default value
24
+ is 0.
25
+ """
26
+ global __compress_level
27
+ __compress_level = level
28
+
29
+
30
+ def register(cls: type,
31
+ encode: Callable[[cls], bytes] = pickle.dumps,
32
+ decode: Callable[[bytes], cls] = pickle.loads) -> None:
33
+ """
34
+ Register a serializable type
35
+
36
+ Args:
37
+ cls: type
38
+ encode: Callable
39
+ translate an object of type `cls` into `bytes`
40
+ default: pickle.dumps
41
+ decode: Callable
42
+ translate `bytes` to an object of type `cls`
43
+ default: pickle.loads
44
+ """
45
+ global __index
46
+ __index += 1
47
+ t = __index
48
+ __pack_handlers[cls] = (t, encode)
49
+ __unpack_handlers[t] = decode
50
+
51
+
52
+ def default(obj: Any) -> msgpack.ExtType:
53
+ for cls, (t, encode) in __pack_handlers.items():
54
+ if isinstance(obj, cls):
55
+ return msgpack.ExtType(t, encode(obj))
56
+ else:
57
+ raise TypeError("Unknown type: %r" % (obj, ))
58
+
59
+
60
+ def ext_hook(code: int, data: bytes) -> msgpack.ExtType:
61
+ for c, decode in __unpack_handlers.items():
62
+ if code == c:
63
+ return decode(data)
64
+ else:
65
+ return msgpack.ExtType(code, data)
66
+
67
+
68
+ def pack(obj: Any) -> bytes:
69
+ """
70
+ Serialize
71
+ """
72
+ return msgpack.packb(obj, default=default, use_bin_type=True)
73
+
74
+
75
+ def unpack(buff: bytes) -> Any:
76
+ """
77
+ Unserialize
78
+ """
79
+ return msgpack.unpackb(buff, ext_hook=ext_hook, raw=False)
80
+
81
+
82
+ def packz(obj: Any) -> bytes:
83
+ """
84
+ Serialize and compress.
85
+ """
86
+ return zlib.compress(pack(obj), level=__compress_level)
87
+
88
+
89
+ def unpackz(buff: bytes) -> Any:
90
+ """
91
+ Decompress and unserialize.
92
+ """
93
+ return unpack(zlib.decompress(buff))
94
+
95
+
96
+ def encode_excepion(e: Exception) -> bytes:
97
+ e.__traceback__ = None
98
+ return pickle.dumps(e)
99
+
100
+
101
+ register(Exception, encode_excepion)
102
+
103
+
104
+ try:
105
+ import numpy as np
106
+
107
+ dtypes = [
108
+ np.bool_, np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16,
109
+ np.uint32, np.uint64, np.float16, np.float32, np.float64, np.complex64,
110
+ np.complex128
111
+ ]
112
+
113
+ _dtype_map1 = {t: i for i, t in enumerate(dtypes)}
114
+ _dtype_map2 = {i: t for i, t in enumerate(dtypes)}
115
+
116
+ def encode_ndarray(x: np.ndarray) -> bytes:
117
+ dtype = x.dtype
118
+ if isinstance(dtype, np.dtype):
119
+ dtype = dtype.type
120
+ if x.flags['F_CONTIGUOUS']:
121
+ x = x.T
122
+ T = True
123
+ else:
124
+ T = False
125
+ return pack((_dtype_map1[dtype], x.shape, T, x.data))
126
+
127
+ def decode_ndarray(buff: bytes) -> np.ndarray:
128
+ t, shape, T, buff = unpack(buff)
129
+ x = np.ndarray(shape, dtype=_dtype_map2[t], buffer=buff, order='C')
130
+ if T:
131
+ x = x.T
132
+ return x
133
+
134
+ register(np.ndarray, encode_ndarray, decode_ndarray)
135
+
136
+ except:
137
+ pass
138
+
139
+ __all__ = ['compress_level', 'register', 'pack', 'unpack', 'packz', 'unpackz']
@@ -0,0 +1,29 @@
1
+ from ..net.kcp import listen
2
+
3
+
4
+ class Peer:
5
+
6
+ def __init__(self, addr, handler, **kwargs):
7
+ self.addr = addr
8
+ self.handler = handler
9
+ self.kwargs = kwargs
10
+
11
+ async def run(self):
12
+ conv = self.kwargs.get('conv', 0)
13
+ ttl = self.kwargs.get('ttl', 60)
14
+ async with listen(self.addr, conv, ttl, **self.kwargs) as server:
15
+ async for conn in server:
16
+ await self.handler(conn)
17
+
18
+ def __call__(self, handler):
19
+ self.handler = handler
20
+ return self
21
+
22
+ def __await__(self):
23
+ return self.run().__await__()
24
+
25
+ def __aenter__(self):
26
+ return self
27
+
28
+ async def __aexit__(self, exc_type, exc_value, traceback):
29
+ pass
@@ -0,0 +1,29 @@
1
+ from ..net.kcp import listen
2
+
3
+
4
+ class Peer:
5
+
6
+ def __init__(self, addr, handler, **kwargs):
7
+ self.addr = addr
8
+ self.handler = handler
9
+ self.kwargs = kwargs
10
+
11
+ async def run(self):
12
+ conv = self.kwargs.get('conv', 0)
13
+ ttl = self.kwargs.get('ttl', 60)
14
+ async with listen(self.addr, conv, ttl, **self.kwargs) as server:
15
+ async for conn in server:
16
+ await self.handler(conn)
17
+
18
+ def __call__(self, handler):
19
+ self.handler = handler
20
+ return self
21
+
22
+ def __await__(self):
23
+ return self.run().__await__()
24
+
25
+ def __aenter__(self):
26
+ return self
27
+
28
+ async def __aexit__(self, exc_type, exc_value, traceback):
29
+ pass
qulab/sys/rpc/utils.py ADDED
@@ -0,0 +1,25 @@
1
+ import inspect
2
+ import os
3
+ from hashlib import sha1
4
+
5
+
6
+ def acceptArg(f, name, keyword=True):
7
+ """
8
+ Test if argument is acceptable by function.
9
+
10
+ Args:
11
+ f: callable
12
+ function
13
+ name: str
14
+ argument name
15
+ """
16
+ sig = inspect.signature(f)
17
+ for param in sig.parameters.values():
18
+ if param.name == name and param.kind != param.VAR_POSITIONAL:
19
+ return True
20
+ elif param.kind == param.VAR_KEYWORD:
21
+ return True
22
+ elif param.kind == param.VAR_POSITIONAL and not keyword:
23
+ return True
24
+ return False
25
+