Skip to content

Commit

Permalink
Add support for rendering in pyodide/pyscript (#5338)
Browse files Browse the repository at this point in the history
* Add support for rendering in pyodide/pyscript

* Update repr code
  • Loading branch information
philippjfr committed Jun 26, 2022
1 parent d4a2de2 commit 6647ddc
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 1 deletion.
5 changes: 4 additions & 1 deletion holoviews/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import os, io
import io, os, sys

import numpy as np # noqa (API import)
import param
Expand Down Expand Up @@ -44,6 +44,9 @@ class notebook_extension(param.ParameterizedFunction):
def __call__(self, *args, **opts): # noqa (dummy signature)
raise Exception("IPython notebook not available: use hv.extension instead.")

if '_pyodide' in sys.modules:
from .pyodide import pyodide_extension as extension # noqa (API import)

# A single holoviews.rc file may be executed if found.
for rcfile in [os.environ.get("HOLOVIEWSRC", ''),
os.path.abspath(os.path.join(os.path.split(__file__)[0],
Expand Down
92 changes: 92 additions & 0 deletions holoviews/pyodide.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import asyncio
import sys

from js import document

from bokeh.embed.elements import script_for_render_items
from bokeh.embed.util import standalone_docs_json_and_render_items
from bokeh.embed.wrappers import wrap_in_script_tag
from panel.io.pyodide import _link_docs
from panel.pane import panel as as_panel

from ..core.dimension import LabelledData
from ..core.options import Store
from ..util import extension as _extension


#-----------------------------------------------------------------------------
# Private API
#-----------------------------------------------------------------------------

async def _link(ref, doc):
from js import Bokeh
rendered = Bokeh.index.object_keys()
if ref not in rendered:
await asyncio.sleep(0.1)
await _link(ref, doc)
return
views = Bokeh.index.object_values()
view = views[rendered.indexOf(ref)]
_link_docs(doc, view.model.document)

def render_html(obj):
if hasattr(sys.stdout, '_out'):
out = sys.stdout._out # type: ignore
else:
raise ValueError("Could not determine target node to write to.")
doc = Document()
as_panel(obj).server_doc(doc, location=False)
docs_json, [render_item,] = standalone_docs_json_and_render_items(
doc.roots, suppress_callback_warning=True
)
for root in doc.roots:
render_item.roots._roots[root] = target
document.getElementById(target).classList.add('bk-root')
script = script_for_render_items(docs_json, [render_item])
asyncio.create_task(_link(doc.roots[0].ref['id'], doc))
return {'text/html': wrap_in_script_tag(script)}, {}

def render_image(element, fmt):
"""
Used to render elements to an image format (svg or png) if requested
in the display formats.
"""
if fmt not in Store.display_formats:b
return None

backend = Store.current_backend
if type(element) not in Store.registry[backend]:
return None
renderer = Store.renderers[backend]
plot = renderer.get_plot(element)

# Current renderer does not support the image format
if fmt not in renderer.param.objects('existing')['fig'].objects:
return None

data, info = renderer(plot, fmt=fmt)
return {info['mime_type']: data}, {}

def render_png(obj):
return render_image(element, 'png')

def render_svg(obj):
return render_image(element, 'svg')


#-----------------------------------------------------------------------------
# Public API
#-----------------------------------------------------------------------------

class pyodide_extension(_extension):

_loaded = False

def __call__(self, *args, **params):
super().__call__(*args, **params)
if not self._loaded:
Store.output_settings.initialize(list(Store.renderers.keys()))
Store.set_display_hook('html+js', LabelledData, render_html)
Store.set_display_hook('png', LabelledData, render_png)
Store.set_display_hook('svg', LabelledData, render_svg)
pyodide_extension._loaded = True
2 changes: 2 additions & 0 deletions holoviews/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,8 @@ class extension(_pyviz_extension):
# Hooks run when a backend is loaded
_backend_hooks = defaultdict(list)

_loaded = False

def __call__(self, *args, **params):
# Get requested backends
config = params.pop('config', {})
Expand Down

0 comments on commit 6647ddc

Please sign in to comment.