caching_example / index.js
MarcSkovMadsen's picture
Upload 4 files
e01fd27
raw
history blame
5.79 kB
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()