From da39de7797645d50d1d76117911979778eb6bb65 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Sun, 11 Dec 2016 21:12:26 +0000
Subject: [PATCH 1/4] Defined html and json mime types to fix py3 bytes issues
---
holoviews/plotting/renderer.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/holoviews/plotting/renderer.py b/holoviews/plotting/renderer.py
index a2eed14c2f..bdee63fec9 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 = """
From feefa735fa27af6325552bda22779b5b21aa6003 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Sun, 18 Dec 2016 20:14:58 +0000
Subject: [PATCH 2/4] Fixed Renderer.export_widgets ensuring HTML is encoded
---
holoviews/plotting/renderer.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/holoviews/plotting/renderer.py b/holoviews/plotting/renderer.py
index bdee63fec9..a42583eaef 100644
--- a/holoviews/plotting/renderer.py
+++ b/holoviews/plotting/renderer.py
@@ -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
From 01cc5b8ad5befafa1fefaa99cd2f1031da14ca49 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Sun, 18 Dec 2016 20:15:47 +0000
Subject: [PATCH 3/4] Added tests ensuring Renderers correctly encode HTML data
---
tests/testrenderclass.py | 33 +++++++++++++++++++++++++++++----
1 file changed, 29 insertions(+), 4 deletions(-)
diff --git a/tests/testrenderclass.py b/tests/testrenderclass.py
index 4988be1b56..2dffda9777 100644
--- a/tests/testrenderclass.py
+++ b/tests/testrenderclass.py
@@ -7,8 +7,7 @@
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.element.comparison import ComparisonTestCase
from nose.plugins.attrib import attr
@@ -18,9 +17,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()
@@ -36,7 +41,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'
@@ -104,9 +109,29 @@ def test_static_html_gif(self):
data = self.renderer.static_html(self.map1, fmt='gif')
self.assertEqual(digest_data(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')
From f28098d05172683598acbbf5aaa569a72703cd2d Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Sun, 18 Dec 2016 20:50:00 +0000
Subject: [PATCH 4/4] Fixes for utf encoding and added tests
---
holoviews/plotting/renderer.py | 4 ++--
tests/testrenderclass.py | 20 +++++++++++++++++++-
tests/testwidgets.py | 8 +++++++-
3 files changed, 28 insertions(+), 4 deletions(-)
diff --git a/holoviews/plotting/renderer.py b/holoviews/plotting/renderer.py
index a42583eaef..005abf5518 100644
--- a/holoviews/plotting/renderer.py
+++ b/holoviews/plotting/renderer.py
@@ -478,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 2dffda9777..5c35421b2a 100644
--- a/tests/testrenderclass.py
+++ b/tests/testrenderclass.py
@@ -2,13 +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 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
@@ -33,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):
"""
@@ -107,7 +125,7 @@ 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):
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):