comparison libervia/pages/_browser/bridge.py @ 1510:5ea06e8b06ed

browser: make bridge API closer to the one use with other frontends: `bridge.AsyncBridge` is not used instead of `aio_bridge.Bridge`
author Goffi <goffi@goffi.org>
date Mon, 22 May 2023 11:57:44 +0200
parents 106bae41f5c8
children
comparison
equal deleted inserted replaced
1509:106bae41f5c8 1510:5ea06e8b06ed
1 from browser import window, timer, console as log 1 from browser import window, aio, timer, console as log
2 import time 2 import time
3 import random 3 import random
4 import json 4 import json
5 import dialog 5 import dialog
6 import javascript 6 import javascript
7 7
8 8
9 log.warning = log.warn 9 log.warning = log.warn
10 tab_id = random.randint(0, 2**64) 10 tab_id = random.randint(0, 2**64)
11 log.info(f"TAB ID is {tab_id}") 11 log.info(f"TAB ID is {tab_id}")
12
13
14 class BridgeException(Exception):
15 """An exception which has been raised from the backend and arrived to the frontend."""
16
17 def __init__(self, name, message="", condition=""):
18 """
19
20 @param name (str): full exception class name (with module)
21 @param message (str): error message
22 @param condition (str) : error condition
23 """
24 Exception.__init__(self)
25 self.fullname = str(name)
26 self.message = str(message)
27 self.condition = str(condition) if condition else ""
28 self.module, __, self.classname = str(self.fullname).rpartition(".")
29
30 def __str__(self):
31 return f"{self.classname}: {self.message or ''}"
32
33 def __eq__(self, other):
34 return self.classname == other
12 35
13 36
14 class WebSocket: 37 class WebSocket:
15 38
16 def __init__(self, broadcast_channel): 39 def __init__(self, broadcast_channel):
241 "id": tab_id, 264 "id": tab_id,
242 "connecting_tab": self.connecting_tab 265 "connecting_tab": self.connecting_tab
243 }) 266 })
244 267
245 268
246 class bridge: 269 class Bridge:
247 bc: BroadcastChannel | None = None 270 bc: BroadcastChannel | None = None
248 271
249 def __init__(self) -> None: 272 def __init__(self) -> None:
250 if bridge.bc is None: 273 if Bridge.bc is None:
251 bridge.bc = BroadcastChannel() 274 Bridge.bc = BroadcastChannel()
252 275
253 def __getattr__(self, attr): 276 def __getattr__(self, attr):
254 return lambda *args, **kwargs: self.call(attr, *args, **kwargs) 277 return lambda *args, **kwargs: self.call(attr, *args, **kwargs)
255 278
256 def on_load(self, xhr, ev, callback, errback): 279 def on_load(self, xhr, ev, callback, errback):
266 ret = javascript.JSON.parse(xhr.response) 289 ret = javascript.JSON.parse(xhr.response)
267 if errback is not None: 290 if errback is not None:
268 errback(ret) 291 errback(ret)
269 else: 292 else:
270 log.error( 293 log.error(
271 f"bridge called failed: code: {xhr.response}, text: {xhr.statusText}" 294 f"bridge call failed: code: {xhr.response}, text: {xhr.statusText}"
272 ) 295 )
273 if errback is not None: 296 if errback is not None:
274 errback({"fullname": "BridgeInternalError", "message": xhr.statusText}) 297 errback({"fullname": "BridgeInternalError", "message": xhr.statusText})
275 298
276 def call(self, method_name, *args, callback, errback, **kwargs): 299 def call(self, method_name, *args, callback, errback, **kwargs):
287 xhr.send(data) 310 xhr.send(data)
288 311
289 def register_signal(self, signal: str, handler, iface=None) -> None: 312 def register_signal(self, signal: str, handler, iface=None) -> None:
290 BroadcastChannel.handlers.setdefault(signal, []).append(handler) 313 BroadcastChannel.handlers.setdefault(signal, []).append(handler)
291 log.debug(f"signal {signal} has been registered") 314 log.debug(f"signal {signal} has been registered")
315
316
317 class AsyncBridge:
318
319 def __getattr__(self, attr):
320 return lambda *args, **kwargs: self.call(attr, *args, **kwargs)
321
322 async def call(self, method_name, *args, **kwargs):
323 print(f"calling {method_name}")
324 data = javascript.JSON.stringify({
325 "args": args,
326 "kwargs": kwargs,
327 })
328 url = f"/_bridge/{method_name}"
329 r = await aio.post(
330 url,
331 headers={
332 'X-Csrf-Token': window.csrf_token,
333 },
334 data=data,
335 )
336
337 if r.status == 200:
338 return javascript.JSON.parse(r.data)
339 elif r.status == 502:
340 ret = javascript.JSON.parse(r.data)
341 raise BridgeException(ret['fullname'], ret['message'], ret['condition'])
342 else:
343 print(f"bridge called failed: code: {r.status}, text: {r.statusText}")
344 raise BridgeException("InternalError", r.statusText)
345
346 def register_signal(self, signal: str, handler, iface=None) -> None:
347 BroadcastChannel.handlers.setdefault(signal, []).append(handler)
348 log.debug(f"signal {signal} has been registered")