Coverage for src/sensai/util/jscode.py: 0%
98 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-13 22:17 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-13 22:17 +0000
1"""
2Utility classes and functions for JavaScript code generation
3"""
4import json
5from abc import abstractmethod, ABC
6from typing import Union, Any
8import numpy as np
10from .string import list_string
13PythonType = Union[str, int, bool, float]
16class JsCode(ABC):
17 def __str__(self):
18 return self.get_js_code()
20 @abstractmethod
21 def get_js_code(self):
22 pass
25class JsCodeLiteral(JsCode):
26 def __init__(self, js_code: str):
27 self.js_code = js_code
29 def get_js_code(self):
30 return self.js_code
33class JsValue(JsCode, ABC):
34 @classmethod
35 def from_python(cls, value: PythonType):
36 t = type(value)
37 if t == str:
38 return cls.string_value(value)
39 elif t == int:
40 return cls.int_value(value)
41 elif value is None:
42 return cls.undefined()
43 elif t == bool:
44 return cls.bool_value(value)
45 elif t in (float, np.float64, np.float):
46 return cls.float_value(value)
47 else:
48 raise ValueError(f"Unsupported value of type {type(value)}: {value}")
50 @classmethod
51 def from_value(cls, value: Union["JsValue", PythonType]):
52 if isinstance(value, JsValue):
53 return value
54 else:
55 return cls.from_python(value)
57 def is_undefined(self):
58 return self.get_js_code() == "undefined"
60 @staticmethod
61 def string_value(s: str):
62 s = s.replace('"', r'\"')
63 return JsValueLiteral(f'"{s}"')
65 @staticmethod
66 def int_value(value: int):
67 return JsValueLiteral(str(int(value)))
69 @staticmethod
70 def float_value(value: Union[float, int]):
71 return JsValueLiteral(str(float(value)))
73 @staticmethod
74 def bool_value(value: bool):
75 b = bool(value)
76 return JsValueLiteral("true" if b else "false")
78 @staticmethod
79 def undefined():
80 return JsValueLiteral("undefined")
82 @staticmethod
83 def null():
84 return JsValueLiteral("null")
87class JsValueLiteral(JsValue):
88 def __init__(self, js_code: str):
89 self.js_code = js_code
91 def get_js_code(self):
92 return self.js_code
95def js_value(value: Union[JsValue, PythonType]) -> JsValue:
96 return JsValue.from_value(value)
99def js_arg_list(*args: Union[JsValue, PythonType], drop_trailing_undefined=True) -> JsCode:
100 """
101 :param args: arguments that are either JsValue instances or (supported) Python values
102 :param drop_trailing_undefined: whether to drop trailing arguments that are undefined/None
103 :return: the JsCode
104 """
105 args = [js_value(a) for a in args]
106 last_index_to_include = len(args) - 1
107 if drop_trailing_undefined:
108 while last_index_to_include >= 0 and args[last_index_to_include].is_undefined():
109 last_index_to_include -= 1
110 args = args[:last_index_to_include+1]
111 return JsCodeLiteral(", ".join(map(str, args)))
114class JsObject(JsValue):
115 def __init__(self):
116 self.data = {}
118 def add(self, key: str, value: Union[JsValue, PythonType]):
119 self.data[key] = js_value(value)
121 def add_string(self, key: str, value: str):
122 self.data[key] = JsValue.string_value(value)
124 def add_code_literal(self, key: str, value: str):
125 self.data[key] = value
127 def add_float(self, key: str, value: Union[float, int]):
128 self.data[key] = JsValue.float_value(value)
130 def add_json(self, key: str, value: Any):
131 """
132 :param key: key within the object
133 :param value: any Python object which can be converted to JSON
134 """
135 self.add_code_literal(key, json.dumps(value))
137 def get_js_code(self):
138 return "{" + ", ".join(f'"{k}": {v}' for k, v in self.data.items()) + "}"
140 def __len__(self):
141 return len(self.data)
144class JsClassInstance(JsValueLiteral):
145 def __init__(self, class_name, *args: Union[JsValue, PythonType]):
146 arg_list = js_arg_list(*args, drop_trailing_undefined=False)
147 super().__init__(f"new {class_name}({arg_list})")
150class JsList(JsValueLiteral):
151 def __init__(self, *values: Union[JsValue, PythonType]):
152 super().__init__(list_string([js_value(x) for x in values]))