Build serverless web application with HoloViz Panel
Panel is a Python library for building web applications, mainly serves as
a bridge between Bokeh and HoloViews. (If you don't use HoloViews
directly or indirectly, you may want to skip this topic and search for some
alternative libraries.)
Panel runs on both CPython and Pyodide. For installation, please refer to the
tutorial. Beyond that, in Mahoraga we provide proxy for Panel/Bokeh
frontend assets, eliminating direct requests to upstream servers completely.
The following example is for Pyodide, but can be adapted to CPython as well.
Note
Mahoraga serves on http://127.0.0.1:3450 by default. Replace it with the
actual URL exposed to your clients.
Prerequisites
Simple slider panel
The following example combines the Mahoraga, Panel and Pyodide tutorials.
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="http://127.0.0.1:3450/pyodide/v0.29.4/full/pyodide.js"></script>
<script type="text/javascript" src="http://127.0.0.1:3450/npm/@bokeh/bokehjs@3.9.1/build/js/bokeh.min.js"></script>
<script type="text/javascript" src="http://127.0.0.1:3450/npm/@bokeh/bokehjs@3.9.1/build/js/bokeh-widgets.min.js"></script>
<script type="text/javascript" src="http://127.0.0.1:3450/npm/@bokeh/bokehjs@3.9.1/build/js/bokeh-tables.min.js"></script>
<script type="text/javascript" src="http://127.0.0.1:3450/npm/@holoviz/panel@1.9.3/dist/panel.min.js"></script>
</head>
<body>
<div id="simple_app"></div>
<script type="text/javascript">
async function main() {
let pyodide = await loadPyodide();
await pyodide.loadPackage("micropip");
pyodide.runPython(`
import micropip
class _Transaction(micropip.transaction.Transaction):
def __post_init__(self):
super().__post_init__()
self.search_pyodide_lock_first = True
micropip.package_manager.Transaction = _Transaction
`);
const micropip = pyodide.pyimport("micropip");
micropip.set_index_urls("http://127.0.0.1:3450/pypi/simple/{package_name}/?micropip=1");
await micropip.install(["bokeh ==3.9.1", "panel ==1.9.3"]);
pyodide.runPython(`
import os
os.environ["PANEL_CDN_ROOT"] = "http://127.0.0.1:3450/npm/@holoviz/panel@"
import panel as pn
pn.extension(sizing_mode="stretch_width")
slider = pn.widgets.FloatSlider(start=0, end=10, name="Amplitude")
def callback(new):
return f"Amplitude is: {new}"
pn.Row(slider, pn.bind(callback, slider)).servable(target="simple_app")
`);
}
main();
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="http://127.0.0.1:3450/npm/@bokeh/bokehjs@3.9.1/build/js/bokeh.min.js"></script>
<script type="text/javascript" src="http://127.0.0.1:3450/npm/@bokeh/bokehjs@3.9.1/build/js/bokeh-widgets.min.js"></script>
<script type="text/javascript" src="http://127.0.0.1:3450/npm/@bokeh/bokehjs@3.9.1/build/js/bokeh-tables.min.js"></script>
<script type="text/javascript" src="http://127.0.0.1:3450/npm/@holoviz/panel@1.9.3/dist/panel.min.js"></script>
<script type="module" src="http://127.0.0.1:3450/npm/@pyscript/core@0/dist/core.js"></script>
<link rel="stylesheet" href="http://127.0.0.1:3450/npm/@pyscript/core@0/dist/core.css">
</head>
<body>
<div id="simple_app"></div>
<script type="py" config='{
"interpreter": "http://127.0.0.1:3450/pyodide/v0.29.4/full/pyodide.mjs",
"packages": ["micropip"]
}'>
import micropip
class _Transaction(micropip.transaction.Transaction):
def __post_init__(self):
super().__post_init__()
self.search_pyodide_lock_first = True
micropip.package_manager.Transaction = _Transaction
micropip.set_index_urls("http://127.0.0.1:3450/pypi/simple/{package_name}/?micropip=1")
await micropip.install(["bokeh ==3.9.1", "panel ==1.9.3"])
import os
os.environ["PANEL_CDN_ROOT"] = "http://127.0.0.1:3450/npm/@holoviz/panel@"
import panel as pn
pn.extension(sizing_mode="stretch_width")
slider = pn.widgets.FloatSlider(start=0, end=10, name="Amplitude")
def callback(new):
return f"Amplitude is: {new}"
pn.Row(slider, pn.bind(callback, slider)).servable(target="simple_app")
</script>
</body>
</html>