From 35c4d07eb7060b505412c0ad83886176fe8409fe Mon Sep 17 00:00:00 2001 From: martinRenou Date: Sun, 3 Oct 2021 16:25:15 +0200 Subject: [PATCH] Add a new output filter that excludes widgets if there is no state (#1643) This allows showing a static mimetype instead of a blank widget --- nbconvert/exporters/html.py | 34 +++++---- nbconvert/filters/widgetsdatatypefilter.py | 69 +++++++++++++++++++ .../templates/base/display_priority.j2 | 3 - .../templates/latex/display_priority.j2 | 3 - 4 files changed, 88 insertions(+), 21 deletions(-) create mode 100644 nbconvert/filters/widgetsdatatypefilter.py diff --git a/nbconvert/exporters/html.py b/nbconvert/exporters/html.py index 65b93702e..9f02b774c 100644 --- a/nbconvert/exporters/html.py +++ b/nbconvert/exporters/html.py @@ -21,6 +21,7 @@ from jinja2.loaders import split_template_path from nbconvert.filters.highlight import Highlight2HTML +from nbconvert.filters.widgetsdatatypefilter import WidgetsDataTypeFilter from nbconvert.filters.markdown_mistune import IPythonRenderer, MarkdownWithMath from .templateexporter import TemplateExporter @@ -98,22 +99,22 @@ def _template_name_default(self): def default_config(self): c = Config({ 'NbConvertBase': { - 'display_data_priority' : ['application/vnd.jupyter.widget-state+json', - 'application/vnd.jupyter.widget-view+json', - 'application/javascript', - 'text/html', - 'text/markdown', - 'image/svg+xml', - 'text/latex', - 'image/png', - 'image/jpeg', - 'text/plain' - ] - }, + 'display_data_priority': [ + 'application/vnd.jupyter.widget-view+json', + 'application/javascript', + 'text/html', + 'text/markdown', + 'image/svg+xml', + 'text/latex', + 'image/png', + 'image/jpeg', + 'text/plain' + ] + }, 'HighlightMagicsPreprocessor': { - 'enabled':True - } - }) + 'enabled': True + } + }) c.merge(super().default_config) return c @@ -136,7 +137,10 @@ def from_notebook_node(self, nb, resources=None, **kw): langinfo = nb.metadata.get('language_info', {}) lexer = langinfo.get('pygments_lexer', langinfo.get('name', None)) highlight_code = self.filters.get('highlight_code', Highlight2HTML(pygments_lexer=lexer, parent=self)) + filter_data_type = WidgetsDataTypeFilter(notebook_metadata=nb.metadata, parent=self) + self.register_filter('highlight_code', highlight_code) + self.register_filter('filter_data_type', filter_data_type) return super().from_notebook_node(nb, resources, **kw) def _init_resources(self, resources): diff --git a/nbconvert/filters/widgetsdatatypefilter.py b/nbconvert/filters/widgetsdatatypefilter.py new file mode 100644 index 000000000..6d390b44e --- /dev/null +++ b/nbconvert/filters/widgetsdatatypefilter.py @@ -0,0 +1,69 @@ +"""Filter used to select the first preferred output format available, +excluding interactive widget format if the widget state is not available. + +The filter contained in the file allows the converter templates to select +the output format that is most valuable to the active export format. The +value of the different formats is set via +NbConvertBase.display_data_priority +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2013, the IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Classes and functions +#----------------------------------------------------------------------------- + +from warnings import warn + +from ..utils.base import NbConvertBase + +__all__ = ['WidgetsDataTypeFilter'] + + +WIDGET_VIEW_MIMETYPE = 'application/vnd.jupyter.widget-view+json' +WIDGET_STATE_MIMETYPE = 'application/vnd.jupyter.widget-state+json' + + +class WidgetsDataTypeFilter(NbConvertBase): + """ Returns the preferred display format, excluding the widget output if + there is no widget state available """ + + def __init__(self, notebook_metadata=None, **kwargs): + metadata = notebook_metadata or {} + + self.widgets_state = ( + metadata['widgets'][WIDGET_STATE_MIMETYPE]['state'] if + metadata.get('widgets') is not None else {} + ) + + super().__init__(**kwargs) + + def __call__(self, output): + """ Return the first available format in the priority. + + Produces a UserWarning if no compatible mimetype is found. + + `output` is dict with structure {mimetype-of-element: value-of-element} + + """ + for fmt in self.display_data_priority: + if fmt in output: + # If there is no widget state available, we skip this mimetype + if ( + fmt == WIDGET_VIEW_MIMETYPE and + output[WIDGET_VIEW_MIMETYPE]['model_id'] not in self.widgets_state + ): + continue + + return [fmt] + warn("Your element with mimetype(s) {mimetypes}" + " is not able to be represented.".format( + mimetypes=output.keys()) + ) + + return [] diff --git a/share/jupyter/nbconvert/templates/base/display_priority.j2 b/share/jupyter/nbconvert/templates/base/display_priority.j2 index 56ad7cde7..ee4987fc2 100644 --- a/share/jupyter/nbconvert/templates/base/display_priority.j2 +++ b/share/jupyter/nbconvert/templates/base/display_priority.j2 @@ -32,9 +32,6 @@ {%- elif type == 'application/javascript' -%} {%- block data_javascript -%} {%- endblock -%} - {%- elif type == 'application/vnd.jupyter.widget-state+json' -%} - {%- block data_widget_state -%} - {%- endblock -%} {%- elif type == 'application/vnd.jupyter.widget-view+json' -%} {%- block data_widget_view -%} {%- endblock -%} diff --git a/share/jupyter/nbconvert/templates/latex/display_priority.j2 b/share/jupyter/nbconvert/templates/latex/display_priority.j2 index 9f33a74e1..c97736ec8 100644 --- a/share/jupyter/nbconvert/templates/latex/display_priority.j2 +++ b/share/jupyter/nbconvert/templates/latex/display_priority.j2 @@ -36,9 +36,6 @@ ((*- elif type == 'application/javascript' -*)) ((*- block data_javascript -*)) ((*- endblock -*)) - ((*- elif type == 'application/vnd.jupyter.widget-state+json' -*)) - ((*- block data_widget_state -*)) - ((*- endblock -*)) ((*- elif type == 'application/vnd.jupyter.widget-view+json' -*)) ((*- block data_widget_view -*)) ((*- endblock -*))