44from typing import TYPE_CHECKING
55
66import logistro
7+ import orjson
78
89from . import _devtools_utils as _dtools
910from . import _js_logger
1920
2021
2122_TEXT_FORMATS = ("svg" , "json" ) # eps
23+ _CHUNK_SIZE = 10 * 1024 * 1024 # 10 MB
2224
2325_logger = logistro .getLogger (__name__ )
2426
2527
28+ def _orjson_default (obj ):
29+ """Fallback for types orjson can't handle natively (e.g. NumPy string arrays)."""
30+ if hasattr (obj , "tolist" ):
31+ return obj .tolist ()
32+ raise TypeError (f"Type is not JSON serializable: { type (obj ).__name__ } " )
33+
34+
2635def _subscribe_new (tab : choreo .Tab , event : str ) -> asyncio .Future :
2736 """Create subscription to tab clearing old ones first: helper function."""
2837 new_future = tab .subscribe_once (event )
@@ -117,22 +126,38 @@ async def _calc_fig(
117126 render_prof ,
118127 stepper ,
119128 ) -> bytes :
120- # js script
121- kaleido_js_fn = (
122- r"function(spec, ...args)"
123- r"{"
124- r"return kaleido_scopes.plotly(spec, ...args).then(JSON.stringify);"
125- r"}"
126- )
127- render_prof .profile_log .tick ("sending javascript" )
128- result = await _dtools .exec_js_fn (
129- self .tab ,
130- self ._current_js_id ,
131- kaleido_js_fn ,
129+ render_prof .profile_log .tick ("serializing spec" )
130+ spec_str = orjson .dumps (
132131 spec ,
133- topojson ,
134- stepper ,
135- )
132+ default = _orjson_default ,
133+ option = orjson .OPT_SERIALIZE_NUMPY ,
134+ ).decode ()
135+ render_prof .profile_log .tick ("spec serialized" )
136+
137+ render_prof .profile_log .tick ("sending javascript" )
138+ if len (spec_str ) <= _CHUNK_SIZE :
139+ kaleido_js_fn = (
140+ r"function(specStr, ...args)"
141+ r"{"
142+ r"return kaleido_scopes"
143+ r".plotly(JSON.parse(specStr), ...args)"
144+ r".then(JSON.stringify);"
145+ r"}"
146+ )
147+ result = await _dtools .exec_js_fn (
148+ self .tab ,
149+ self ._current_js_id ,
150+ kaleido_js_fn ,
151+ spec_str ,
152+ topojson ,
153+ stepper ,
154+ )
155+ else :
156+ result = await self ._calc_fig_chunked (
157+ spec_str ,
158+ topojson = topojson ,
159+ stepper = stepper ,
160+ )
136161 _raise_error (result )
137162 render_prof .profile_log .tick ("javascript sent" )
138163
@@ -154,3 +179,41 @@ async def _calc_fig(
154179 render_prof .data_out_size = len (res )
155180 render_prof .js_log = self .js_logger .log
156181 return res
182+
183+ async def _calc_fig_chunked (
184+ self ,
185+ spec_str : str ,
186+ * ,
187+ topojson : str | None ,
188+ stepper ,
189+ ):
190+ await _dtools .exec_js_fn (
191+ self .tab ,
192+ self ._current_js_id ,
193+ r"function() { window.__kaleido_chunks = []; }" ,
194+ )
195+
196+ for i in range (0 , len (spec_str ), _CHUNK_SIZE ):
197+ chunk = spec_str [i : i + _CHUNK_SIZE ]
198+ await _dtools .exec_js_fn (
199+ self .tab ,
200+ self ._current_js_id ,
201+ r"function(c) { window.__kaleido_chunks.push(c); }" ,
202+ chunk ,
203+ )
204+
205+ kaleido_js_fn = (
206+ r"function(...args)"
207+ r"{"
208+ r"var spec = JSON.parse(window.__kaleido_chunks.join(''));"
209+ r"delete window.__kaleido_chunks;"
210+ r"return kaleido_scopes.plotly(spec, ...args).then(JSON.stringify);"
211+ r"}"
212+ )
213+ return await _dtools .exec_js_fn (
214+ self .tab ,
215+ self ._current_js_id ,
216+ kaleido_js_fn ,
217+ topojson ,
218+ stepper ,
219+ )
0 commit comments