Source code for holoviews.ipython.display_hooks

"""
Definition and registration of display hooks for the IPython Notebook.
"""
import sys
import traceback
from contextlib import contextmanager
from functools import wraps

import IPython
from IPython import get_ipython
from IPython.display import HTML

import holoviews as hv

from ..core import (
    AdjointLayout,
    CompositeOverlay,
    Dimensioned,
    DynamicMap,
    Empty,
    GridSpace,
    HoloMap,
    Layout,
    NdLayout,
    ViewableElement,
)
from ..core.io import FileArchive
from ..core.options import AbbreviatedException, SkipRendering, Store, StoreOptions
from ..core.traversal import unique_dimkeys
from ..core.util import mimebundle_to_html
from ..plotting import Plot
from ..plotting.renderer import MIME_TYPES
from ..util.settings import OutputSettings
from .magics import OptsMagic, OutputMagic

# To assist with debugging of display hooks
FULL_TRACEBACK = None
ABBREVIATE_TRACEBACKS = True

#==================#
# Helper functions #
#==================#


def max_frame_warning(max_frames):
    sys.stderr.write(
        f"Animation longer than the max_frames limit {max_frames};\n"
        "skipping rendering to avoid unexpected lengthy computations.\n"
        "If desired, the limit can be increased using:\n"
        "hv.output(max_frames=<insert number>)"
    )

[docs]def process_object(obj): "Hook to process the object currently being displayed." invalid_options = OptsMagic.process_element(obj) if invalid_options: return invalid_options OutputMagic.info(obj)
def render(obj, **kwargs): info = process_object(obj) if info: display(HTML(info)) return if render_anim is not None: return render_anim(obj) backend = Store.current_backend renderer = Store.renderers[backend] # Drop back to png if pdf selected, notebook PDF rendering is buggy if renderer.fig == 'pdf': renderer = renderer.instance(fig='png') return renderer.components(obj, **kwargs)
[docs]def single_frame_plot(obj): """ Returns plot, renderer and format for single frame export. """ obj = Layout(obj) if isinstance(obj, AdjointLayout) else obj backend = Store.current_backend renderer = Store.renderers[backend] plot_cls = renderer.plotting_class(obj) plot = plot_cls(obj, **renderer.plot_options(obj, renderer.size)) fmt = (renderer.param.objects('existing')['fig'].objects[0] if renderer.fig == 'auto' else renderer.fig) return plot, renderer, fmt
[docs]def first_frame(obj): "Only display the first frame of an animated plot" plot, renderer, fmt = single_frame_plot(obj) plot.update(0) return {'text/html': renderer.html(plot, fmt)}
[docs]def middle_frame(obj): "Only display the (approximately) middle frame of an animated plot" plot, renderer, fmt = single_frame_plot(obj) middle_frame = int(len(plot) / 2) plot.update(middle_frame) return {'text/html': renderer.html(plot, fmt)}
[docs]def last_frame(obj): "Only display the last frame of an animated plot" plot, renderer, fmt = single_frame_plot(obj) plot.update(len(plot)) return {'text/html': renderer.html(plot, fmt)}
#===============# # Display hooks # #===============# def dynamic_optstate(element, state=None): # Temporary fix to avoid issues with DynamicMap traversal DynamicMap._deep_indexable = False optstate = StoreOptions.state(element,state=state) DynamicMap._deep_indexable = True return optstate @contextmanager def option_state(element): optstate = dynamic_optstate(element) try: yield except Exception: dynamic_optstate(element, state=optstate) raise
[docs]def display_hook(fn): """ A decorator to wrap display hooks that return a MIME bundle or None. Additionally it handles adding output to the notebook archive, saves files specified with the output magic and handles tracebacks. """ @wraps(fn) def wrapped(element): global FULL_TRACEBACK # noqa: PLW0603 if Store.current_backend is None: return {}, {} try: max_frames = OutputSettings.options['max_frames'] mimebundle = fn(element, max_frames=max_frames) if mimebundle is None: return {}, {} mime_data, mime_metadata = mimebundle if MIME_TYPES['js'] in mime_data: mime_data['text/html'] = mimebundle_to_html(mime_data) del mime_data[MIME_TYPES['js']] # Only want to add to the archive for one display hook... disabled_suffixes = ['png_display', 'svg_display'] if not any(fn.__name__.endswith(suffix) for suffix in disabled_suffixes): if type(hv.archive) is not FileArchive and 'text/html' in mime_data: hv.archive.add(element, html=mime_data['text/html']) filename = OutputSettings.options['filename'] if filename: Store.renderers[Store.current_backend].save(element, filename) return mime_data, mime_metadata except SkipRendering as e: if e.warn: sys.stderr.write(str(e)) return {}, {} except AbbreviatedException as e: FULL_TRACEBACK = '\n'.join(traceback.format_exception(e.etype, e.value, e.traceback)) info = dict(name=e.etype.__name__, message=str(e.value).replace('\n','<br>')) msg = '<i> [Call holoviews.ipython.show_traceback() for details]</i>' return {'text/html': "<b>{name}</b>{msg}<br>{message}".format(msg=msg, **info)}, {} except Exception: raise return wrapped
@display_hook def element_display(element, max_frames): info = process_object(element) if info: display(HTML(info)) return None backend = Store.current_backend if type(element) not in Store.registry[backend]: return None return render(element) @display_hook def map_display(vmap, max_frames): if not isinstance(vmap, (HoloMap, DynamicMap)): return None if len(vmap) == 0 and not isinstance(vmap, DynamicMap): return None elif len(vmap) > max_frames: max_frame_warning(max_frames) return None return render(vmap) @display_hook def layout_display(layout, max_frames): if isinstance(layout, AdjointLayout): layout = Layout(layout).opts(layout.opts.get('plot')) if not isinstance(layout, (Layout, NdLayout)): return None nframes = len(unique_dimkeys(layout)[1]) if nframes > max_frames: max_frame_warning(max_frames) return None return render(layout) @display_hook def grid_display(grid, max_frames): if not isinstance(grid, GridSpace): return None nframes = len(unique_dimkeys(grid)[1]) if nframes > max_frames: max_frame_warning(max_frames) return None return render(grid)
[docs]def display(obj, raw_output=False, **kwargs): """ Renders any HoloViews object to HTML and displays it using the IPython display function. If raw is enabled the raw HTML is returned instead of displaying it directly. """ if not Store.loaded_backends() and isinstance(obj, Dimensioned): raise RuntimeError('To use display on a HoloViews object ensure ' 'a backend is loaded using the holoviews ' 'extension.') raw = True if isinstance(obj, GridSpace): with option_state(obj): output = grid_display(obj) elif isinstance(obj, (CompositeOverlay, ViewableElement)): with option_state(obj): output = element_display(obj) elif isinstance(obj, (Layout, NdLayout, AdjointLayout)): with option_state(obj): output = layout_display(obj) elif isinstance(obj, (HoloMap, DynamicMap)): with option_state(obj): output = map_display(obj) elif isinstance(obj, Plot): output = render(obj) elif isinstance(obj, Empty): output = ({}, {}) else: output = obj raw = kwargs.pop('raw', False) if raw_output: return output elif isinstance(output, tuple): data, metadata = output else: data, metadata = output, {} return IPython.display.display(data, raw=raw, metadata=metadata, **kwargs)
def pprint_display(obj): if 'html' not in Store.display_formats: return None # If pretty printing is off, return None (fallback to next display format) ip = get_ipython() if not ip.display_formatter.formatters['text/plain'].pprint: return None return display(obj, raw_output=True)
[docs]def image_display(element, max_frames, 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: return None info = process_object(element) if info: display(HTML(info)) return 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}, {}
[docs]@display_hook def png_display(element, max_frames): """ Used to render elements to PNG if requested in the display formats. """ return image_display(element, max_frames, fmt='png')
[docs]@display_hook def svg_display(element, max_frames): """ Used to render elements to SVG if requested in the display formats. """ return image_display(element, max_frames, fmt='svg')
# display_video output by default, but may be set to first_frame, # middle_frame or last_frame (e.g. for testing purposes) render_anim = None def plot_display(plot): return plot.renderer.components(plot)