diff --git a/holoviews/plotting/renderer.py b/holoviews/plotting/renderer.py index a2eed14c2f..005abf5518 100644 --- a/holoviews/plotting/renderer.py +++ b/holoviews/plotting/renderer.py @@ -50,8 +50,8 @@ 'webm': 'video/webm', 'mp4': 'video/mp4', 'pdf': 'application/pdf', - 'html': None, - 'json': None + 'html': 'text/html', + 'json': 'text/json' } static_template = """ @@ -361,12 +361,13 @@ def export_widgets(self_or_cls, obj, filename, fmt=None, template=None, widget = obj html = self_or_cls.static_html(widget, fmt, template) + encoded = self_or_cls.encode((html, {'mime_type': 'text/html'})) if isinstance(filename, BytesIO): - filename.write(html) + filename.write(encoded) filename.seek(0) else: with open(filename, 'w') as f: - f.write(html) + f.write(encoded) @classmethod @@ -477,11 +478,11 @@ def save(self_or_cls, obj, basename, fmt='auto', key={}, info={}, options=None, rendered = self_or_cls(plot, fmt) if rendered is None: return (data, info) = rendered + encoded = self_or_cls.encode(rendered) if isinstance(basename, BytesIO): - basename.write(data) + basename.write(encoded) basename.seek(0) else: - encoded = self_or_cls.encode(rendered) filename ='%s.%s' % (basename, info['file-ext']) with open(filename, 'wb') as f: f.write(encoded) diff --git a/tests/testrenderclass.py b/tests/testrenderclass.py index 4988be1b56..5c35421b2a 100644 --- a/tests/testrenderclass.py +++ b/tests/testrenderclass.py @@ -2,14 +2,17 @@ """ Test cases for rendering exporters """ +from __future__ import unicode_literals + from io import BytesIO from hashlib import sha256 from unittest import SkipTest import numpy as np -from holoviews.plotting.mpl.renderer import MPLRenderer -from holoviews import HoloMap, Image, ItemTable +from holoviews import HoloMap, Image, ItemTable, Store +from holoviews.core.util import unicode from holoviews.element.comparison import ComparisonTestCase +from holoviews.plotting import Renderer from nose.plugins.attrib import attr @@ -18,9 +21,15 @@ try: # Standardize backend due to random inconsistencies from matplotlib import pyplot + from holoviews.plotting.mpl import MPLRenderer pyplot.switch_backend('agg') except: - pyplot = None + pass + +try: + from holoviews.plotting.bokeh import BokehRenderer +except: + pass def digest_data(data): hashfn = sha256() @@ -28,6 +37,20 @@ def digest_data(data): return hashfn.hexdigest() +class TestRenderer(ComparisonTestCase): + """ + Test the basic serializer and deserializer (i.e. using pickle), + including metadata access. + """ + + def test_renderer_encode_unicode_types(self): + mime_types = ['image/svg+xml', 'text/html', 'text/json'] + for mime in mime_types: + info = {'mime_type': mime} + encoded = Renderer.encode(('Testing «ταБЬℓσ»: 1<2 & 4+1>3', info)) + self.assertTrue(isinstance(encoded, bytes)) + + @attr(optional=1) class MPLRendererTest(ComparisonTestCase): """ @@ -36,7 +59,7 @@ class MPLRendererTest(ComparisonTestCase): """ def setUp(self): - if pyplot is None: + if 'matplotlib' not in Store.renderers: raise SkipTest("Matplotlib required to test widgets") self.basename = 'no-file' @@ -102,11 +125,31 @@ def test_static_html_widgets(self): def test_static_html_gif(self): data = self.renderer.static_html(self.map1, fmt='gif') - self.assertEqual(digest_data(data), + self.assertEqual(digest_data(normalize(data)), '9d43822e0f368f3c673b19aaf66d22252849947b7dc4a157306c610c42d319b5') + def test_export_widgets(self): bytesio = BytesIO() self.renderer.export_widgets(self.map1, bytesio, fmt='widgets') data = normalize(bytesio.read()) self.assertEqual(digest_data(data), '91bbc7b4efebd07b1ee595b902d9899b27f2c7e353dfc87c57c2dfd5d0404301') + + +class BokehRendererTest(ComparisonTestCase): + + def setUp(self): + if 'bokeh' not in Store.renderers: + raise SkipTest("Bokeh required to test widgets") + self.image1 = Image(np.array([[0,1],[2,3]]), label='Image1') + self.image2 = Image(np.array([[1,0],[4,-2]]), label='Image2') + self.map1 = HoloMap({1:self.image1, 2:self.image2}, label='TestMap') + self.renderer = BokehRenderer.instance() + + def test_save_html(self): + bytesio = BytesIO() + self.renderer.save(self.image1, bytesio) + + def test_export_widgets(self): + bytesio = BytesIO() + self.renderer.export_widgets(self.map1, bytesio, fmt='widgets') diff --git a/tests/testwidgets.py b/tests/testwidgets.py index 6be00a2f29..da7b526ac7 100644 --- a/tests/testwidgets.py +++ b/tests/testwidgets.py @@ -17,6 +17,7 @@ raise SkipTest("Matplotlib required to test widgets") from holoviews import Image, HoloMap +from holoviews.core.util import unicode from holoviews.plotting.mpl import RasterPlot def digest_data(data): @@ -32,10 +33,15 @@ def digest_data(data): filters += [re.compile('new [A-Za-z]*SelectionWidget\([a-z0-9_, "]+')] def normalize(data): + if isinstance(data, bytes): + data = data.decode('utf8') for f in filters: data = re.sub(f, '[CLEARED]', data) # Hack around inconsistencies in jinja between Python 2 and 3 - return data.replace('0.0', '0').replace('1.0', '1') + data = data.replace('0.0', '0').replace('1.0', '1') + if isinstance(data, unicode): + data = data.encode('utf8') + return data @attr(optional=1) class TestWidgets(IPTestCase):