From 1b9704122606466a1a396b7cd948633a3545ce5b Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Tue, 28 Apr 2020 15:26:17 +0200 Subject: [PATCH 1/8] added _repr_html_ to BaseFigure --- packages/python/plotly/plotly/basedatatypes.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/python/plotly/plotly/basedatatypes.py b/packages/python/plotly/plotly/basedatatypes.py index ee9ce457451..57e0f41c059 100644 --- a/packages/python/plotly/plotly/basedatatypes.py +++ b/packages/python/plotly/plotly/basedatatypes.py @@ -418,6 +418,14 @@ def __repr__(self): return repr_str + + def _repr_html_(self): + """ + Customizes html representation + """ + return self.to_html(full_html=False, include_plotlyjs='cdn') + + def _ipython_display_(self): """ Handle rich display of figures in ipython contexts From 33bc2bde395577f1b019bd7dd3abd80c8ff99be8 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Wed, 29 Apr 2020 18:17:57 +0200 Subject: [PATCH 2/8] added test for _repr_html_ --- .../plotly/tests/test_io/test_renderers.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/python/plotly/plotly/tests/test_io/test_renderers.py b/packages/python/plotly/plotly/tests/test_io/test_renderers.py index 692c4efc696..1964604f1c4 100644 --- a/packages/python/plotly/plotly/tests/test_io/test_renderers.py +++ b/packages/python/plotly/plotly/tests/test_io/test_renderers.py @@ -287,3 +287,29 @@ def test_reject_invalid_renderer(renderer): ) def test_accept_valid_renderer(renderer): pio.renderers.default = renderer + + +# _repr_html +# ---------- +# independent of renderer +def test_repr_html(): + fig = go.Figure() + fig.update_layout(template=None) + s = fig._repr_html_() + # id number of figure + id_figure = s.split('document.getElementById("')[1].split('")')[0] + id_pattern = "cd462b94-79ce-42a2-887f-2650a761a144" + template = ( + '
\n \n \n " + ' \n ' + '
\n \n
" + ) + assert s.replace(id_figure, id_pattern) == template From f5d675836a4a6856376bda880b25cfc318717d0e Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Wed, 29 Apr 2020 18:29:53 +0200 Subject: [PATCH 3/8] black --- packages/python/plotly/plotly/basedatatypes.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/python/plotly/plotly/basedatatypes.py b/packages/python/plotly/plotly/basedatatypes.py index 57e0f41c059..67ab7f98f0f 100644 --- a/packages/python/plotly/plotly/basedatatypes.py +++ b/packages/python/plotly/plotly/basedatatypes.py @@ -418,13 +418,11 @@ def __repr__(self): return repr_str - def _repr_html_(self): """ - Customizes html representation + Customize html representation """ - return self.to_html(full_html=False, include_plotlyjs='cdn') - + return self.to_html(full_html=False, include_plotlyjs="cdn", auto_play=False) def _ipython_display_(self): """ From 16c63fd810fb384c0812b204f5240689ae24f0d8 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Sun, 3 May 2020 17:11:30 +0200 Subject: [PATCH 4/8] use mimebundle for _repr_html_ and introduce new renderer for sphinx-gallery without orca --- .../python/plotly/plotly/basedatatypes.py | 21 +++++++++- .../plotly/plotly/io/_base_renderers.py | 40 ++++++++++++++++++- .../python/plotly/plotly/io/_renderers.py | 6 ++- .../python/plotly/plotly/io/_sg_scraper.py | 2 +- 4 files changed, 63 insertions(+), 6 deletions(-) diff --git a/packages/python/plotly/plotly/basedatatypes.py b/packages/python/plotly/plotly/basedatatypes.py index 67ab7f98f0f..6acdb1a7e04 100644 --- a/packages/python/plotly/plotly/basedatatypes.py +++ b/packages/python/plotly/plotly/basedatatypes.py @@ -422,7 +422,26 @@ def _repr_html_(self): """ Customize html representation """ - return self.to_html(full_html=False, include_plotlyjs="cdn", auto_play=False) + bundle = self._repr_mimebundle_() + if 'text/html' in bundle: + return bundle['text/html'] + else: + return '' + #return self.to_html(full_html=False, include_plotlyjs="cdn") + + def _repr_mimebundle_(self, include=None, exclude=None, validate=True, **kwargs): + """ + Return mimebundle corresponding to default renderer. + """ + import plotly.io as pio + renderer_str = pio.renderers.default + renderers = pio._renderers.renderers + from plotly.io._utils import validate_coerce_fig_to_dict + fig_dict = validate_coerce_fig_to_dict(self, validate) + # Mimetype renderers + renderer = renderers[renderer_str] + bundle = renderer.to_mimebundle(fig_dict) + return bundle def _ipython_display_(self): """ diff --git a/packages/python/plotly/plotly/io/_base_renderers.py b/packages/python/plotly/plotly/io/_base_renderers.py index f0642c7a3f5..623e1622749 100644 --- a/packages/python/plotly/plotly/io/_base_renderers.py +++ b/packages/python/plotly/plotly/io/_base_renderers.py @@ -796,7 +796,30 @@ def render(self, fig_dict): self.displayHTML(html) -class SphinxGalleryRenderer(ExternalRenderer): +class SphinxGalleryHtmlRenderer(HtmlRenderer): + + def __init__( + self, + connected=True, + config=None, + auto_play=False, + post_script=None, + animation_opts=None, + ): + super(SphinxGalleryHtmlRenderer, self).__init__( + connected=connected, + full_html=False, + requirejs=False, + global_init=False, + config=config, + auto_play=auto_play, + post_script=post_script, + animation_opts=animation_opts, + ) + + +class SphinxGalleryOrcaRenderer(ExternalRenderer): + def render(self, fig_dict): stack = inspect.stack() # Name of script from which plot function was called is retrieved @@ -809,4 +832,17 @@ def render(self, fig_dict): filename_png = filename_root + ".png" figure = return_figure_from_figure_or_data(fig_dict, True) _ = write_html(fig_dict, file=filename_html) - write_image(figure, filename_png) + try: + write_image(figure, filename_png) + except (ValueError, ImportError): + raise ImportError( + "orca and psutil are required to use the `sphinx-gallery-orca` renderer. " + "See https://plotly.com/python/static-image-export/ for instructions on" + "how to install orca. Alternatively, you can use the `sphinx-gallery`" + "renderer (note that png thumbnails can only be generated with" + "the `sphinx-gallery-orca` renderer)." + ) + + + + diff --git a/packages/python/plotly/plotly/io/_renderers.py b/packages/python/plotly/plotly/io/_renderers.py index 5277c9efec7..22b0f92c394 100644 --- a/packages/python/plotly/plotly/io/_renderers.py +++ b/packages/python/plotly/plotly/io/_renderers.py @@ -24,7 +24,8 @@ PdfRenderer, BrowserRenderer, IFrameRenderer, - SphinxGalleryRenderer, + SphinxGalleryHtmlRenderer, + SphinxGalleryOrcaRenderer, CoCalcRenderer, DatabricksRenderer, ) @@ -430,7 +431,8 @@ def show(fig, renderer=None, validate=True, **kwargs): renderers["chromium"] = BrowserRenderer(config=config, using="chromium") renderers["iframe"] = IFrameRenderer(config=config, include_plotlyjs=True) renderers["iframe_connected"] = IFrameRenderer(config=config, include_plotlyjs="cdn") -renderers["sphinx_gallery"] = SphinxGalleryRenderer() +renderers["sphinx_gallery"] = SphinxGalleryHtmlRenderer() +renderers["sphinx_gallery_png"] = SphinxGalleryOrcaRenderer() # Set default renderer # -------------------- diff --git a/packages/python/plotly/plotly/io/_sg_scraper.py b/packages/python/plotly/plotly/io/_sg_scraper.py index bec587d0ff6..18defd11671 100644 --- a/packages/python/plotly/plotly/io/_sg_scraper.py +++ b/packages/python/plotly/plotly/io/_sg_scraper.py @@ -7,7 +7,7 @@ from glob import glob import shutil -plotly.io.renderers.default = "sphinx_gallery" +plotly.io.renderers.default = "sphinx_gallery_png" def plotly_sg_scraper(block, block_vars, gallery_conf, **kwargs): From 55dd832d7a3336176a8c320889aa71ddc92fd6ad Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Sun, 3 May 2020 17:21:44 +0200 Subject: [PATCH 5/8] added to_mimebundle to remove postscript --- .../plotly/plotly/io/_base_renderers.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/packages/python/plotly/plotly/io/_base_renderers.py b/packages/python/plotly/plotly/io/_base_renderers.py index 623e1622749..f8e6e57e61c 100644 --- a/packages/python/plotly/plotly/io/_base_renderers.py +++ b/packages/python/plotly/plotly/io/_base_renderers.py @@ -817,6 +817,38 @@ def __init__( animation_opts=animation_opts, ) + def to_mimebundle(self, fig_dict): + + from plotly.io import to_html + + if self.requirejs: + include_plotlyjs = "require" + include_mathjax = False + elif self.connected: + include_plotlyjs = "cdn" + include_mathjax = "cdn" + else: + include_plotlyjs = True + include_mathjax = "cdn" + + + html = to_html( + fig_dict, + config=self.config, + auto_play=self.auto_play, + include_plotlyjs=include_plotlyjs, + include_mathjax=include_mathjax, + full_html=self.full_html, + animation_opts=self.animation_opts, + default_width="100%", + default_height=525, + validate=False, + ) + + return {"text/html": html} + + + class SphinxGalleryOrcaRenderer(ExternalRenderer): From 584180866b4c73b51be29d723366cce8e9730135 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Sun, 3 May 2020 21:49:43 +0200 Subject: [PATCH 6/8] added tests --- .../python/plotly/plotly/basedatatypes.py | 18 +++++--- .../plotly/plotly/io/_base_renderers.py | 19 +++----- .../plotly/tests/test_io/test_renderers.py | 45 ++++++++++++++++--- 3 files changed, 55 insertions(+), 27 deletions(-) diff --git a/packages/python/plotly/plotly/basedatatypes.py b/packages/python/plotly/plotly/basedatatypes.py index 6acdb1a7e04..dcdce077a8e 100644 --- a/packages/python/plotly/plotly/basedatatypes.py +++ b/packages/python/plotly/plotly/basedatatypes.py @@ -423,24 +423,30 @@ def _repr_html_(self): Customize html representation """ bundle = self._repr_mimebundle_() - if 'text/html' in bundle: - return bundle['text/html'] + if "text/html" in bundle: + return bundle["text/html"] else: - return '' - #return self.to_html(full_html=False, include_plotlyjs="cdn") + return self.to_html(full_html=False, include_plotlyjs="cdn") def _repr_mimebundle_(self, include=None, exclude=None, validate=True, **kwargs): """ Return mimebundle corresponding to default renderer. """ import plotly.io as pio + renderer_str = pio.renderers.default renderers = pio._renderers.renderers + renderer_names = renderers._validate_coerce_renderers(renderer_str) + renderers_list = [renderers[name] for name in renderer_names] from plotly.io._utils import validate_coerce_fig_to_dict + from plotly.io._renderers import MimetypeRenderer + fig_dict = validate_coerce_fig_to_dict(self, validate) # Mimetype renderers - renderer = renderers[renderer_str] - bundle = renderer.to_mimebundle(fig_dict) + bundle = {} + for renderer in renderers_list: + if isinstance(renderer, MimetypeRenderer): + bundle.update(renderer.to_mimebundle(fig_dict)) return bundle def _ipython_display_(self): diff --git a/packages/python/plotly/plotly/io/_base_renderers.py b/packages/python/plotly/plotly/io/_base_renderers.py index f8e6e57e61c..9cd7fbfb72b 100644 --- a/packages/python/plotly/plotly/io/_base_renderers.py +++ b/packages/python/plotly/plotly/io/_base_renderers.py @@ -797,7 +797,6 @@ def render(self, fig_dict): class SphinxGalleryHtmlRenderer(HtmlRenderer): - def __init__( self, connected=True, @@ -831,7 +830,6 @@ def to_mimebundle(self, fig_dict): include_plotlyjs = True include_mathjax = "cdn" - html = to_html( fig_dict, config=self.config, @@ -848,10 +846,7 @@ def to_mimebundle(self, fig_dict): return {"text/html": html} - - class SphinxGalleryOrcaRenderer(ExternalRenderer): - def render(self, fig_dict): stack = inspect.stack() # Name of script from which plot function was called is retrieved @@ -868,13 +863,9 @@ def render(self, fig_dict): write_image(figure, filename_png) except (ValueError, ImportError): raise ImportError( - "orca and psutil are required to use the `sphinx-gallery-orca` renderer. " - "See https://plotly.com/python/static-image-export/ for instructions on" - "how to install orca. Alternatively, you can use the `sphinx-gallery`" - "renderer (note that png thumbnails can only be generated with" - "the `sphinx-gallery-orca` renderer)." + "orca and psutil are required to use the `sphinx-gallery-orca` renderer. " + "See https://plotly.com/python/static-image-export/ for instructions on" + "how to install orca. Alternatively, you can use the `sphinx-gallery`" + "renderer (note that png thumbnails can only be generated with" + "the `sphinx-gallery-orca` renderer)." ) - - - - diff --git a/packages/python/plotly/plotly/tests/test_io/test_renderers.py b/packages/python/plotly/plotly/tests/test_io/test_renderers.py index 1964604f1c4..bd0ff1b3ac7 100644 --- a/packages/python/plotly/plotly/tests/test_io/test_renderers.py +++ b/packages/python/plotly/plotly/tests/test_io/test_renderers.py @@ -289,15 +289,19 @@ def test_accept_valid_renderer(renderer): pio.renderers.default = renderer -# _repr_html -# ---------- -# independent of renderer -def test_repr_html(): +@pytest.mark.parametrize( + "renderer", + plotly_mimetype_renderers + + ["notebook", "notebook_connected", "browser", "notebook+plotly_mimetype"], +) +def test_repr_html(renderer): + pio.renderers.default = renderer fig = go.Figure() fig.update_layout(template=None) - s = fig._repr_html_() + str_html = fig._repr_html_() + bundle = fig._repr_mimebundle_() # id number of figure - id_figure = s.split('document.getElementById("')[1].split('")')[0] + id_html = str_html.split('document.getElementById("')[1].split('")')[0] id_pattern = "cd462b94-79ce-42a2-887f-2650a761a144" template = ( '
\n \n \n
" ) - assert s.replace(id_figure, id_pattern) == template + if "text/html" in bundle: + str_bundle = bundle["text/html"] + id_bundle = str_bundle.split('document.getElementById("')[1].split('")')[0] + assert str_html.replace(id_html, "") == str_bundle.replace(id_bundle, "") + else: + assert str_html.replace(id_html, "") == template.replace(id_pattern, "") + + +@pytest.mark.parametrize("renderer_str", pio.renderers) +def test_repr_mimebundle(renderer_str): + pio.renderers.default = renderer_str + fig = go.Figure() + fig.update_layout(template=None) + bundle = fig._repr_mimebundle_() + renderer = pio.renderers[renderer_str] + from plotly.io._renderers import MimetypeRenderer + + if isinstance(renderer, MimetypeRenderer): + ref_bundle = renderer.to_mimebundle(fig.to_dict()) + for key in bundle: + if "getElementById" in bundle[key]: + id1 = bundle[key].split('document.getElementById("')[1].split('")')[0] + id2 = ( + ref_bundle[key].split('document.getElementById("')[1].split('")')[0] + ) + assert bundle[key].replace(id1, "") == ref_bundle[key].replace(id2, "") + else: + assert bundle == {} From fa703f956332ba4f8d3a7bb31a9687782ade0fc1 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Sun, 3 May 2020 22:01:17 +0200 Subject: [PATCH 7/8] more tests --- .../python/plotly/plotly/tests/test_io/test_renderers.py | 7 +++++++ .../plotly/plotly/tests/test_orca/test_sg_scraper.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/python/plotly/plotly/tests/test_io/test_renderers.py b/packages/python/plotly/plotly/tests/test_io/test_renderers.py index bd0ff1b3ac7..bf48bd96f6a 100644 --- a/packages/python/plotly/plotly/tests/test_io/test_renderers.py +++ b/packages/python/plotly/plotly/tests/test_io/test_renderers.py @@ -344,3 +344,10 @@ def test_repr_mimebundle(renderer_str): assert bundle[key].replace(id1, "") == ref_bundle[key].replace(id2, "") else: assert bundle == {} + + +def test_repr_mimebundle_mixed_renderer(fig1): + pio.renderers.default = "notebook+plotly_mimetype" + assert set(fig1._repr_mimebundle_().keys()) == set( + {"application/vnd.plotly.v1+json", "text/html"} + ) diff --git a/packages/python/plotly/plotly/tests/test_orca/test_sg_scraper.py b/packages/python/plotly/plotly/tests/test_orca/test_sg_scraper.py index 63eb1e2fc65..3b26d2ac01c 100644 --- a/packages/python/plotly/plotly/tests/test_orca/test_sg_scraper.py +++ b/packages/python/plotly/plotly/tests/test_orca/test_sg_scraper.py @@ -44,7 +44,7 @@ def test_scraper(): from plotly.io._sg_scraper import plotly_sg_scraper # test that monkey-patching worked ok - assert plotly.io.renderers.default == "sphinx_gallery" + assert plotly.io.renderers.default == "sphinx_gallery_png" # Use dummy values for arguments of plotly_sg_scraper block = "" # we don't need actually code import tempfile From 44abde9a5391a70ac47821489596ca26239c10fc Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Sun, 3 May 2020 22:12:48 +0200 Subject: [PATCH 8/8] removed orca renderers from test --- .../plotly/tests/test_io/test_renderers.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/python/plotly/plotly/tests/test_io/test_renderers.py b/packages/python/plotly/plotly/tests/test_io/test_renderers.py index bf48bd96f6a..2d51792721b 100644 --- a/packages/python/plotly/plotly/tests/test_io/test_renderers.py +++ b/packages/python/plotly/plotly/tests/test_io/test_renderers.py @@ -324,7 +324,30 @@ def test_repr_html(renderer): assert str_html.replace(id_html, "") == template.replace(id_pattern, "") -@pytest.mark.parametrize("renderer_str", pio.renderers) +all_renderers_without_orca = [ + "plotly_mimetype", + "jupyterlab", + "nteract", + "vscode", + "notebook", + "notebook_connected", + "kaggle", + "azure", + "colab", + "cocalc", + "databricks", + "json", + "browser", + "firefox", + "chrome", + "chromium", + "iframe", + "iframe_connected", + "sphinx_gallery", +] + + +@pytest.mark.parametrize("renderer_str", all_renderers_without_orca) def test_repr_mimebundle(renderer_str): pio.renderers.default = renderer_str fig = go.Figure()