From 688671bab8598acd30e2c589e4493ea8c6c2591a Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Thu, 17 Oct 2024 10:26:14 +0200 Subject: [PATCH] Serde: improve js-python bridge --- pkgs/clan-app/clan_app/views/webview.py | 36 +++++++++++++++++++++---- pkgs/clan-cli/clan_cli/api/serde.py | 3 ++- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/pkgs/clan-app/clan_app/views/webview.py b/pkgs/clan-app/clan_app/views/webview.py index 2500b2391..04ef178ce 100644 --- a/pkgs/clan-app/clan_app/views/webview.py +++ b/pkgs/clan-app/clan_app/views/webview.py @@ -1,5 +1,6 @@ import json import logging +from pathlib import Path from typing import Any import gi @@ -9,7 +10,7 @@ from clan_app.api import GObjApi, GResult, ImplFunc from clan_app.api.file import open_file 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__) @@ -122,21 +123,46 @@ class WebExecutor(GObject.Object): def on_result(self, source: ImplFunc, data: GResult) -> None: 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}") # Use idle_add to queue the response call to js on the main GTK thread self.return_data_to_js(data.method_name, serialized) 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( - f""" - window.clan.{method_name}(`{serialized}`); - """, + js, -1, None, None, None, + on_js_evaluation_finished, ) return GLib.SOURCE_REMOVE diff --git a/pkgs/clan-cli/clan_cli/api/serde.py b/pkgs/clan-cli/clan_cli/api/serde.py index 61e4f1196..e3ef28be5 100644 --- a/pkgs/clan-cli/clan_cli/api/serde.py +++ b/pkgs/clan-cli/clan_cli/api/serde.py @@ -51,7 +51,8 @@ from clan_cli.errors import ClanError def sanitize_string(s: str) -> str: # Using the native string sanitizer to handle all edge cases # 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: