importScripts("https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"); function sendPatch(patch, buffers, msg_id) { self.postMessage({ type: 'patch', patch: patch, buffers: buffers }) } async function startApplication() { console.log("Loading pyodide!"); self.postMessage({type: 'status', msg: 'Loading pyodide'}) self.pyodide = await loadPyodide(); self.pyodide.globals.set("sendPatch", sendPatch); console.log("Loaded!"); await self.pyodide.loadPackage("micropip"); const env_spec = ['https://cdn.holoviz.org/panel/wheels/bokeh-3.3.2-py3-none-any.whl', 'https://cdn.holoviz.org/panel/1.3.6/dist/wheels/panel-1.3.6-py3-none-any.whl', 'pyodide-http==0.2.1', 'hvplot', 'numpy', 'pandas'] for (const pkg of env_spec) { let pkg_name; if (pkg.endsWith('.whl')) { pkg_name = pkg.split('/').slice(-1)[0].split('-')[0] } else { pkg_name = pkg } self.postMessage({type: 'status', msg: `Installing ${pkg_name}`}) try { await self.pyodide.runPythonAsync(` import micropip await micropip.install('${pkg}'); `); } catch(e) { console.log(e) self.postMessage({ type: 'status', msg: `Error while installing ${pkg_name}` }); } } console.log("Packages loaded!"); self.postMessage({type: 'status', msg: 'Executing code'}) const code = ` import asyncio from panel.io.pyodide import init_doc, write_doc init_doc() """ # Caching Example See https://awesome-panel.org/resources/caching_example """ import time import hvplot.pandas # pylint: disable=unused-import import numpy as np import pandas as pd import panel as pn pn.extension(design="material") ACCENT_COLOR = "#1f77b4" np.random.seed([3, 1415]) PERIODS = 1 * 24 * 60 # minutes. I.e. 1 days DATA = pd.DataFrame( { "time": pd.date_range("2020-01-01", periods=PERIODS, freq="T"), "price": np.random.randn(PERIODS) + 98, } ) def _load_data(frac=0.1): time.sleep(0.5 + frac * 0.5) return DATA.sample(frac=frac) def _plot_data(frac=0.1): time.sleep(0.5) data = _load_data(frac) return data.hvplot(x="time", y="price") @pn.cache(per_session=True, ttl=60*60*24) def _plot_data_cached(frac): return _plot_data(frac) # Create Widgets fraction = pn.widgets.FloatSlider(value=0.1, start=0.1, end=1.0, step=0.1, name="Fraction of data") duration = pn.widgets.StaticText(value="", name="Time to create plot") use_cache = pn.widgets.Checkbox(value=False, name="Use Cache") preload_cache = pn.widgets.Button(name="Preload Cache", button_type="primary", disabled=True) clear_cache = pn.widgets.Button(name="Clear Cache", disabled=True) preload_progress = pn.widgets.Progress( name="Progress", active=False, value=0, max=100, sizing_mode="stretch_width", disabled=True ) plot_panel = pn.pane.HoloViews(min_height=500, sizing_mode="stretch_both") # Setup interactivity def _clear_cache(*_): _plot_data_cached.clear() clear_cache.on_click(_clear_cache) def _preload_cache(*_): for index in range(0, 11, 1): frac_ = round(index / 10, 1) preload_progress.value = int(frac_ * 100) _plot_data_cached(frac_) preload_progress.value = 0 preload_cache.on_click(_preload_cache) @pn.depends(frac=fraction, watch=True) def _update_plot(frac): start_counter = time.perf_counter() frac = round(frac, 1) if use_cache.value: plot = _plot_data_cached(frac) else: plot = _plot_data(frac) end_counter = time.perf_counter() duration.value = str(round(end_counter - start_counter, 4)) + " seconds" # Please note DiskCache does not cache the options plot.opts(color=ACCENT_COLOR, responsive=True) plot_panel.object = plot @pn.depends(use_cache=use_cache, watch=True) def _update_cache_widgets(use_cache): # pylint: disable=redefined-outer-name disabled = not use_cache preload_cache.disabled = disabled clear_cache.disabled = disabled preload_progress.disabled = disabled # Layout the app pn.Column( pn.pane.Markdown( "# Speed up slow functions with caching", sizing_mode="stretch_width" ), fraction, duration, use_cache, plot_panel, pn.Row(preload_cache, clear_cache,), preload_progress, ).servable() pn.state.onload(lambda: fraction.param.trigger("value")) await write_doc() ` try { const [docs_json, render_items, root_ids] = await self.pyodide.runPythonAsync(code) self.postMessage({ type: 'render', docs_json: docs_json, render_items: render_items, root_ids: root_ids }) } catch(e) { const traceback = `${e}` const tblines = traceback.split('\n') self.postMessage({ type: 'status', msg: tblines[tblines.length-2] }); throw e } } self.onmessage = async (event) => { const msg = event.data if (msg.type === 'rendered') { self.pyodide.runPythonAsync(` from panel.io.state import state from panel.io.pyodide import _link_docs_worker _link_docs_worker(state.curdoc, sendPatch, setter='js') `) } else if (msg.type === 'patch') { self.pyodide.globals.set('patch', msg.patch) self.pyodide.runPythonAsync(` state.curdoc.apply_json_patch(patch.to_py(), setter='js') `) self.postMessage({type: 'idle'}) } else if (msg.type === 'location') { self.pyodide.globals.set('location', msg.location) self.pyodide.runPythonAsync(` import json from panel.io.state import state from panel.util import edit_readonly if state.location: loc_data = json.loads(location) with edit_readonly(state.location): state.location.param.update({ k: v for k, v in loc_data.items() if k in state.location.param }) `) } } startApplication()