Mercurial > libervia-web
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") |