Serde: improve js-python bridge

This commit is contained in:
Johannes Kirschbauer
2024-10-17 10:26:14 +02:00
parent 38b088e8aa
commit 688671bab8
2 changed files with 33 additions and 6 deletions

View File

@@ -1,5 +1,6 @@
import json import json
import logging import logging
from pathlib import Path
from typing import Any from typing import Any
import gi import gi
@@ -9,7 +10,7 @@ from clan_app.api import GObjApi, GResult, ImplFunc
from clan_app.api.file import open_file from clan_app.api.file import open_file
gi.require_version("WebKit", "6.0") gi.require_version("WebKit", "6.0")
from gi.repository import GLib, GObject, WebKit from gi.repository import Gio, GLib, GObject, WebKit
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -122,21 +123,46 @@ class WebExecutor(GObject.Object):
def on_result(self, source: ImplFunc, data: GResult) -> None: def on_result(self, source: ImplFunc, data: GResult) -> None:
result = dataclass_to_dict(data.result) result = dataclass_to_dict(data.result)
serialized = json.dumps(result, indent=4) # Important:
# 2. ensure_ascii = False. non-ASCII characters are correctly handled, instead of being escaped.
serialized = json.dumps(result, indent=4, ensure_ascii=False)
log.debug(f"Result for {data.method_name}: {serialized}") log.debug(f"Result for {data.method_name}: {serialized}")
# Use idle_add to queue the response call to js on the main GTK thread # Use idle_add to queue the response call to js on the main GTK thread
self.return_data_to_js(data.method_name, serialized) self.return_data_to_js(data.method_name, serialized)
def return_data_to_js(self, method_name: str, serialized: str) -> bool: def return_data_to_js(self, method_name: str, serialized: str) -> bool:
js = f"""
window.clan.{method_name}({serialized});
"""
def dump_failed_code() -> None:
tmp_file = Path("/tmp/clan-pyjs-bridge-error.js")
with tmp_file.open("w") as f:
f.write(js)
log.debug(f"Failed code dumped in JS file: {tmp_file}")
# Error handling if the JavaScript evaluation fails
def on_js_evaluation_finished(
webview: WebKit.WebView, task: Gio.AsyncResult
) -> None:
try:
# Get the result of the JavaScript evaluation
value = webview.evaluate_javascript_finish(task)
if not value:
log.exception("No value returned")
dump_failed_code()
except GLib.Error:
log.exception("Error evaluating JS")
dump_failed_code()
self.webview.evaluate_javascript( self.webview.evaluate_javascript(
f""" js,
window.clan.{method_name}(`{serialized}`);
""",
-1, -1,
None, None,
None, None,
None, None,
on_js_evaluation_finished,
) )
return GLib.SOURCE_REMOVE return GLib.SOURCE_REMOVE

View File

@@ -51,7 +51,8 @@ from clan_cli.errors import ClanError
def sanitize_string(s: str) -> str: def sanitize_string(s: str) -> str:
# Using the native string sanitizer to handle all edge cases # Using the native string sanitizer to handle all edge cases
# Remove the outer quotes '"string"' # Remove the outer quotes '"string"'
return json.dumps(s)[1:-1] # return json.dumps(s)[1:-1]
return s
def dataclass_to_dict(obj: Any, *, use_alias: bool = True) -> Any: def dataclass_to_dict(obj: Any, *, use_alias: bool = True) -> Any: