|
1 | | -from typing import Dict, List, Any |
| 1 | +from typing import Dict, List, Any, Optional, Callable |
2 | 2 | import os |
3 | 3 | import json |
4 | 4 |
|
5 | | -try: |
6 | | - from quickjs import Function |
7 | | -except ImportError as exc: |
8 | | - raise ImportError("`quickjs` is not installed, please install it first. refer it: `pip install quickjs`.") from exc |
9 | | - |
10 | 5 | from pygwalker._constants import ROOT_DIR |
11 | 6 | from .randoms import rand_str |
12 | 7 |
|
| 8 | +_dsl_to_workflow_js = None # type: Optional[Callable] |
| 9 | +_vega_to_dsl_js = None # type: Optional[Callable] |
| 10 | + |
| 11 | +_INSTALL_MSG = ( |
| 12 | + "Static HTML chart export requires a JavaScript runtime.\n" |
| 13 | + "Install with: pip install pygwalker[export]\n" |
| 14 | + "Or manually: pip install mini-racer" |
| 15 | +) |
| 16 | + |
| 17 | + |
| 18 | +def _make_js_callable(func_name, js_code): |
| 19 | + """Create a callable that executes a named JS function via mini-racer (V8).""" |
| 20 | + from py_mini_racer import MiniRacer |
| 21 | + |
| 22 | + ctx = MiniRacer() |
| 23 | + ctx.eval(js_code) |
| 24 | + |
| 25 | + def call(*args): |
| 26 | + if not args: |
| 27 | + return ctx.eval("{}()".format(func_name)) |
| 28 | + args_json = json.dumps(args) |
| 29 | + return ctx.eval("{}(...{})".format(func_name, args_json)) |
| 30 | + |
| 31 | + return call |
| 32 | + |
| 33 | + |
| 34 | +def _ensure_js_runtime(): |
| 35 | + """Lazily initialize JS runtime on first actual use.""" |
| 36 | + global _dsl_to_workflow_js, _vega_to_dsl_js |
| 37 | + if _dsl_to_workflow_js is not None: |
| 38 | + return |
| 39 | + |
| 40 | + try: |
| 41 | + dsl_js_path = os.path.join(ROOT_DIR, 'templates', 'dist', 'dsl-to-workflow.umd.js') |
| 42 | + vega_js_path = os.path.join(ROOT_DIR, 'templates', 'dist', 'vega-to-dsl.umd.js') |
13 | 43 |
|
14 | | -with open(os.path.join(ROOT_DIR, 'templates', 'dist', 'dsl-to-workflow.umd.js'), 'r', encoding='utf8') as f: |
15 | | - _dsl_to_workflow_js = Function('main', f.read()) |
| 44 | + with open(dsl_js_path, 'r', encoding='utf8') as f: |
| 45 | + _dsl_to_workflow_js = _make_js_callable('main', f.read()) |
16 | 46 |
|
17 | | -with open(os.path.join(ROOT_DIR, 'templates', 'dist', 'vega-to-dsl.umd.js'), 'r', encoding='utf8') as f: |
18 | | - _vega_to_dsl_js = Function('main', f.read()) |
| 47 | + with open(vega_js_path, 'r', encoding='utf8') as f: |
| 48 | + _vega_to_dsl_js = _make_js_callable('main', f.read()) |
| 49 | + except ImportError: |
| 50 | + raise ImportError(_INSTALL_MSG) |
19 | 51 |
|
20 | 52 |
|
21 | 53 | def dsl_to_workflow(dsl: Dict[str, Any]) -> Dict[str, Any]: |
| 54 | + _ensure_js_runtime() |
22 | 55 | return json.loads(_dsl_to_workflow_js(json.dumps(dsl))) |
23 | 56 |
|
24 | 57 |
|
25 | 58 | def vega_to_dsl(vega_config: Dict[str, Any], fields: List[Dict[str, Any]]) -> Dict[str, Any]: |
| 59 | + _ensure_js_runtime() |
26 | 60 | return json.loads(_vega_to_dsl_js(json.dumps({ |
27 | 61 | "vl": vega_config, |
28 | 62 | "allFields": fields, |
|
0 commit comments