lightweight-charts-python
lightweight-charts-python copied to clipboard
[BUG] Fix missing JS error handling
Expected Behavior
Exception error handling from js to python if show show_async() or show(block=False).
file log contains exception data. Very helpful if smth corrupted or wrong data (eg. try to update past completed candle and fix will give exact objects that don't match). Current patch (see below) can gen lost of logs!!!!.
-
app stopped
-
file log (chart_test.log)
2025-07-14 03:54:26,650 - ERROR -
\U0001f7e5 JS Script Error2025-07-14 03:54:26,672 - ERROR - \U0001f525 JavaScript error caught:
2025-07-14 03:54:26,672 - ERROR -
\U0001f7e5 JS Script Error:
Script: thisDoesNotExist()
Error: ReferenceError [?:?]
Message: thisDoesNotExist is not defined
- console log:
2025-07-14 03:54:26,650 - ERROR -
🟥 JS Script Error:
Script: thisDoesNotExist()
Error: ReferenceError [?:?]
Message: thisDoesNotExist is not defined
Traceback (most recent call last):
File "C:\Test\.venv\Lib\site-packages\lightweight_charts\chart.py", line 90, in loop
window.evaluate_js(arg)
File "C:\Test\.venv\Lib\site-packages\webview\window.py", line 50, in wrapper
return function(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Test\.venv\Lib\site-packages\webview\window.py", line 487, in evaluate_js
raise JavascriptException(result)
webview.errors.JavascriptException: {'name': 'ReferenceError', 'stack': 'ReferenceError: thisDoesNotExist is not defined\n at eval (eval at <anonymous> (:4:29), <anonymous>:1:1)\n at <anonymous>:4:29', 'message': 'thisDoesNotExist is not defined'}
✅ The error has been successfully caught.
Process finished with exit code 1
patch - abstract.py.patch.txt:
87c87,88
< self.return_queue.put(window.evaluate_js(arg[14:]))
---
> result = window.evaluate_js(arg[14:])
> self.return_queue.put(result)
90,91d90
< except KeyError as e:
< return
92a92,102
> import traceback
> import logging
>
> logger = logging.getLogger("lightweight_charts")
> if not logger.handlers:
> handler = logging.StreamHandler()
> formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
> handler.setFormatter(formatter)
> logger.addHandler(handler)
> logger.setLevel(logging.DEBUG)
>
95,96c105,110
< raise JavascriptException(
< f"\n\nscript -> '{arg}',\nerror -> {msg.get('name', 'Unknown')}[{msg.get('line', '?')}:{msg.get('column', '?')}]\n{msg.get('message', 'No message')}"
---
> error_msg = (
> f"\n\n🟥 JS Script Error:\n"
> f"Script: {arg}\n"
> f"Error: {msg.get('name', 'Unknown')} "
> f"[{msg.get('line', '?')}:{msg.get('column', '?')}]\n"
> f"Message: {msg.get('message', 'No message')}"
98,99c112,126
< except Exception as err:
< raise JavascriptException(f"\n\nscript -> '{arg}',\nerror -> Unexpected format: {e}")
---
> except Exception:
> error_msg = (
> f"\n\n🟥 JS Script Error (Unknown format):\n"
> f"Script: {arg}\nRaw: {e}"
> )
>
> logger.error(error_msg)
> traceback.print_exc()
> self.emit_queue.put(('__error__', error_msg))
>
> except Exception as e:
> logger = logging.getLogger("lightweight_charts")
> logger.error(f"Unhandled exception while executing JS:\nScript: {arg}\nException: {e}")
> traceback.print_exc()
> raise RuntimeError(f"Unhandled JS exception: {e}") from e
220c247,249
< if response == 'exit':
---
> if isinstance(response, tuple) and response[0] == '__error__':
> raise RuntimeError(response[1])
> elif response == 'exit':
Current Behaviour
No exception error handling from js to python if show show_async() or show(block=False).
- app running/hangs
- file log (chart_test.log) empty
- console log:
C:\Test\.venv\Scripts\python.exe C:\Test\test_patch.py
Exception in thread Thread-2 (loop):
Traceback (most recent call last):
File "C:\Test\.venv\Lib\site-packages\lightweight_charts\chart.py", line 89, in loop
window.evaluate_js(arg)
File "C:\Test\.venv\Lib\site-packages\webview\window.py", line 50, in wrapper
return function(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Test\.venv\Lib\site-packages\webview\window.py", line 487, in evaluate_js
raise JavascriptException(result)
webview.errors.JavascriptException: {'name': 'ReferenceError', 'stack': 'ReferenceError: thisDoesNotExist is not defined\n at eval (eval at <anonymous> (:4:29), <anonymous>:1:1)\n at <anonymous>:4:29', 'message': 'thisDoesNotExist is not defined'}
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Test\.venv\Lib\site-packages\lightweight_charts\chart.py", line 95, in loop
raise JavascriptException(
webview.errors.JavascriptException:
script -> 'thisDoesNotExist()',
error -> ReferenceError[?:?]
thisDoesNotExist is not defined
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\<user>\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 1073, in _bootstrap_inner
self.run()
File "C:\Users\<user>\AppData\Local\Programs\Python\Python312\Lib\threading.py", line 1010, in run
self._target(*self._args, **self._kwargs)
File "C:\Test\.venv\Lib\site-packages\lightweight_charts\chart.py", line 99, in loop
raise JavascriptException(f"\n\nscript -> '{arg}',\nerror -> Unexpected format: {e}")
webview.errors.JavascriptException:
script -> 'thisDoesNotExist()',
error -> Unexpected format: {'name': 'ReferenceError', 'stack': 'ReferenceError: thisDoesNotExist is not defined\n at eval (eval at <anonymous> (:4:29), <anonymous>:1:1)\n at <anonymous>:4:29', 'message': 'thisDoesNotExist is not defined'}
Reproducible Example
If incorrect data or error happens async no error is passed to main thread.
from lightweight_charts import Chart
import logging
import sys
import asyncio
logging.basicConfig(
level=logging.DEBUG,
filename="chart_test.log", # <-- лог в файл
filemode="w",
format="%(asctime)s - %(levelname)s - %(message)s"
)
def main():
chart = Chart(title="JS Error Trigger After Load")
async def run_after_load():
await asyncio.sleep(0.5)
chart.win.script_func("thisDoesNotExist()") # <--- Invalid JS call
async def async_main():
asyncio.create_task(run_after_load())
try:
await chart.show_async()
except RuntimeError as e:
logging.error("🔥 JavaScript error caught:")
logging.error(str(e))
print("✅ The error has been successfully caught.")
chart.exit()
sys.exit(1)
try:
asyncio.run(async_main())
except Exception as e:
logging.error("💥 Top-level error: %s", e)
sys.exit(1)
if __name__ == "__main__":
main()
Environment
- OS: any
- Library: 2.1