Skip to content

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

  • A running Mahoraga server which we have set up in the previous tutorial.
  • CORS must be enabled.

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>