From 61b50878c32c0b3d36acf0ce2f072f455af3dd1b Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Fri, 15 Aug 2014 15:24:32 -0700 Subject: [PATCH 1/5] subclass IPython.core.display HTML object for more flexibility. By defining `_repr_*_` functions, IPython will just call these functions when a non-HTML representation is required. --- plotly/tools.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 548d8f0a572..8b3a5d0cc0f 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -13,6 +13,7 @@ import os.path import warnings import six +import requests from plotly import utils from plotly import exceptions @@ -283,9 +284,37 @@ def embed(file_owner_or_url, file_id=None, width="100%", height=525): return html(s, hide=False) except: pass + if file_id: + url = "{plotly_domain}/~{un}/{fid}".format( + plotly_domain=get_config_file()['plotly_domain'], + un=file_owner_or_url, + fid=file_id) + else: + url = file_owner_or_url try: - from IPython.display import HTML, display - display(HTML(s)) + from IPython.core.display import HTML + class PlotlyFrame(HTML): + def __init__(self, url): + self.resource = url + self.embed_code = get_embed(url) + super(PlotlyFrame, self).__init__(data=self.embed_code) + def _repr_svg_(self): + url = self.resource + ".svg" + res = requests.get(url) + return res.content + def _repr_png_(self): + url = self.resource + ".png" + res = requests.get(url) + return res.content + def _repr_pdf_(self): + url = self.resource + ".pdf" + res = requests.get(url) + return res.content + def _repr_jpeg_(self): + url = self.resource + ".jpeg" + res = requests.get(url) + return res.content + return PlotlyFrame(url) except: pass From d8b9e0743c94a2d00eabf964df365eba8a0ad9b3 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Mon, 18 Aug 2014 19:43:11 -0700 Subject: [PATCH 2/5] protected class definition instead of dynamic class definition. --- plotly/tools.py | 89 +++++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 32 deletions(-) diff --git a/plotly/tools.py b/plotly/tools.py index 8b3a5d0cc0f..521f6a0b681 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -31,6 +31,12 @@ def warning_on_one_line(message, category, filename, lineno, file=None, line=Non except ImportError: _matplotlylib_imported = False +try: + import IPython + _ipython_imported = True +except ImportError: + _ipython_imported = False + PLOTLY_DIR = os.path.join(os.path.expanduser("~"), ".plotly") CREDENTIALS_FILE = os.path.join(PLOTLY_DIR, ".credentials") CONFIG_FILE = os.path.join(PLOTLY_DIR, ".config") @@ -284,39 +290,21 @@ def embed(file_owner_or_url, file_id=None, width="100%", height=525): return html(s, hide=False) except: pass - if file_id: - url = "{plotly_domain}/~{un}/{fid}".format( - plotly_domain=get_config_file()['plotly_domain'], - un=file_owner_or_url, - fid=file_id) + if _ipython_imported: + if file_id: + url = "{plotly_domain}/~{un}/{fid}".format( + plotly_domain=get_config_file()['plotly_domain'], + un=file_owner_or_url, + fid=file_id) + else: + url = file_owner_or_url + return PlotlyDisplay(url) else: - url = file_owner_or_url - try: - from IPython.core.display import HTML - class PlotlyFrame(HTML): - def __init__(self, url): - self.resource = url - self.embed_code = get_embed(url) - super(PlotlyFrame, self).__init__(data=self.embed_code) - def _repr_svg_(self): - url = self.resource + ".svg" - res = requests.get(url) - return res.content - def _repr_png_(self): - url = self.resource + ".png" - res = requests.get(url) - return res.content - def _repr_pdf_(self): - url = self.resource + ".pdf" - res = requests.get(url) - return res.content - def _repr_jpeg_(self): - url = self.resource + ".jpeg" - res = requests.get(url) - return res.content - return PlotlyFrame(url) - except: - pass + warnings.warn( + "Looks like you're not using IPython or Sage to embed this plot. " + "If you just want the *embed code*, try using `get_embed()` " + "instead." + "\nQuestions? support@plot.ly") ### mpl-related tools ### @@ -561,3 +549,40 @@ def _replace_newline(obj): return s else: return obj # we return the actual reference... but DON'T mutate. + + +if _ipython_imported: + class PlotlyDisplay(IPython.core.display.HTML): + """An IPython display object for use with plotly urls + + PlotlyDisplay objects should be instantiated with a url for a plot. + IPython will *choose* the proper display representation from any + Python object, and using provided methods if they exist. By defining + the following, if an HTML display is unusable, the PlotlyDisplay + object can provide alternate representations. + + """ + def __init__(self, url): + self.resource = url + self.embed_code = get_embed(url) + super(PlotlyDisplay, self).__init__(data=self.embed_code) + + def _repr_svg_(self): + url = self.resource + ".svg" + res = requests.get(url) + return res.content + + def _repr_png_(self): + url = self.resource + ".png" + res = requests.get(url) + return res.content + + def _repr_pdf_(self): + url = self.resource + ".pdf" + res = requests.get(url) + return res.content + + def _repr_jpeg_(self): + url = self.resource + ".jpeg" + res = requests.get(url) + return res.content From 4e3259053929025b42605d228e72dbe518ba5bf8 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Mon, 18 Aug 2014 21:52:30 -0700 Subject: [PATCH 3/5] add tests for each returned image type for ipython display --- .../test_optional/test_ipython/__init__.py | 0 .../test_optional/test_ipython/test_embed.py | 53 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 plotly/tests/test_optional/test_ipython/__init__.py create mode 100644 plotly/tests/test_optional/test_ipython/test_embed.py diff --git a/plotly/tests/test_optional/test_ipython/__init__.py b/plotly/tests/test_optional/test_ipython/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/plotly/tests/test_optional/test_ipython/test_embed.py b/plotly/tests/test_optional/test_ipython/test_embed.py new file mode 100644 index 00000000000..cc7388592e2 --- /dev/null +++ b/plotly/tests/test_optional/test_ipython/test_embed.py @@ -0,0 +1,53 @@ +from __future__ import absolute_import + +import plotly.tools as tls +import imghdr +import threading + + +def test_plotly_display(): + plot_info = {"un": "plotlyimagetest", "fid": "2"} + url = "https://plot.ly/~{un}/{fid}".format(**plot_info) + disp_obj = tls.embed(url) + format_to_func = { + "jpeg": jpeg_worker, + "png": png_worker, + "svg": svg_worker, + "pdf": pdf_worker + } + results = {} + threads = [] + for f_format, func in format_to_func.items(): + threads += [threading.Thread(target=func, args=(disp_obj, results))] + threads[-1].setDaemon(True) + threads[-1].start() + for thread in threads: + thread.join() + for f_format in format_to_func: + result = results.get(f_format, False) + print("{f_format}: {result}".format(f_format=f_format, result=result)) + assert results.get(f_format) + + +def jpeg_worker(display_obj, results): + img = display_obj._repr_jpeg_() + if imghdr.what('', img) == "jpeg": + results["jpeg"] = True + + +def png_worker(display_obj, results): + img = display_obj._repr_png_() + if imghdr.what('', img) == "png": + results["png"] = True + + +def svg_worker(display_obj, results): + img = display_obj._repr_svg_() + if img[:4] == ' Date: Mon, 18 Aug 2014 21:58:20 -0700 Subject: [PATCH 4/5] alright, changed editor preferences to auto-include EOF line feeds. sorry for the delay :) --- plotly/tests/test_optional/test_ipython/test_embed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/tests/test_optional/test_ipython/test_embed.py b/plotly/tests/test_optional/test_ipython/test_embed.py index cc7388592e2..bf92e55f881 100644 --- a/plotly/tests/test_optional/test_ipython/test_embed.py +++ b/plotly/tests/test_optional/test_ipython/test_embed.py @@ -50,4 +50,4 @@ def svg_worker(display_obj, results): def pdf_worker(display_obj, results): img = display_obj._repr_pdf_() if img[:4] == '%PDF': - results["pdf"] = True \ No newline at end of file + results["pdf"] = True From d6dd58851d7549aaa7ed920fc544d1688198dd8c Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Mon, 18 Aug 2014 23:45:33 -0700 Subject: [PATCH 5/5] trying to use more standard testing formats. --- .../test_optional/test_ipython/test_embed.py | 97 ++++++++++--------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/plotly/tests/test_optional/test_ipython/test_embed.py b/plotly/tests/test_optional/test_ipython/test_embed.py index bf92e55f881..c046c8bfa76 100644 --- a/plotly/tests/test_optional/test_ipython/test_embed.py +++ b/plotly/tests/test_optional/test_ipython/test_embed.py @@ -3,51 +3,52 @@ import plotly.tools as tls import imghdr import threading - - -def test_plotly_display(): - plot_info = {"un": "plotlyimagetest", "fid": "2"} - url = "https://plot.ly/~{un}/{fid}".format(**plot_info) - disp_obj = tls.embed(url) - format_to_func = { - "jpeg": jpeg_worker, - "png": png_worker, - "svg": svg_worker, - "pdf": pdf_worker - } - results = {} - threads = [] - for f_format, func in format_to_func.items(): - threads += [threading.Thread(target=func, args=(disp_obj, results))] - threads[-1].setDaemon(True) - threads[-1].start() - for thread in threads: - thread.join() - for f_format in format_to_func: - result = results.get(f_format, False) - print("{f_format}: {result}".format(f_format=f_format, result=result)) - assert results.get(f_format) - - -def jpeg_worker(display_obj, results): - img = display_obj._repr_jpeg_() - if imghdr.what('', img) == "jpeg": - results["jpeg"] = True - - -def png_worker(display_obj, results): - img = display_obj._repr_png_() - if imghdr.what('', img) == "png": - results["png"] = True - - -def svg_worker(display_obj, results): - img = display_obj._repr_svg_() - if img[:4] == '