From 9a2d3e8afbe3cdb411caedf2540e5cad9dfaf1ec Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Sat, 10 Dec 2016 18:45:45 +0000 Subject: [PATCH 1/3] Fixed FuncTickFormater with py2js_tickformatter util and tests --- holoviews/plotting/bokeh/element.py | 16 +++-------- holoviews/plotting/bokeh/util.py | 30 ++++++++++++++++++++- tests/testplotutils.py | 42 +++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 13 deletions(-) create mode 100644 tests/testplotutils.py diff --git a/holoviews/plotting/bokeh/element.py b/holoviews/plotting/bokeh/element.py index f3f36165c1..4d2d2a2860 100644 --- a/holoviews/plotting/bokeh/element.py +++ b/holoviews/plotting/bokeh/element.py @@ -34,7 +34,7 @@ from ..util import dynamic_update, get_sources from .plot import BokehPlot from .util import (mpl_to_bokeh, convert_datetime, update_plot, - bokeh_version, mplcmap_to_palette) + bokeh_version, mplcmap_to_palette, py2js_tickformatter) if bokeh_version >= '0.12': from bokeh.models import FuncTickFormatter @@ -450,17 +450,9 @@ def _axis_properties(self, axis, key, plot, dimension, if formatter: msg = ('%s dimension formatter could not be ' 'converted to tick formatter. ' % dimension.name) - try: - formatter = FuncTickFormatter.from_py_func(formatter) - except RuntimeError: - self.warning(msg+'Ensure Flexx is installed ' - '("conda install -c bokeh flexx" or ' - '"pip install flexx")') - except Exception as e: - error = 'Pyscript raised an error: {0}'.format(e) - error = error.replace('%', '%%') - self.warning(msg+error) - else: + jsfunc = py2js_tickformatter(formatter, msg) + if jsfunc: + formatter = FuncTickFormatter(code=jsfunc) axis_props['formatter'] = formatter return axis_props diff --git a/holoviews/plotting/bokeh/util.py b/holoviews/plotting/bokeh/util.py index cad1752ffe..67b5b3bc6f 100644 --- a/holoviews/plotting/bokeh/util.py +++ b/holoviews/plotting/bokeh/util.py @@ -1,4 +1,4 @@ -import itertools +import itertools, inspect, re from distutils.version import LooseVersion from collections import defaultdict @@ -372,3 +372,31 @@ def pad_plots(plots, padding=0.85): for p, w in zip(row, ws)] for row, ws in zip(plots, widths)] total_width = np.max([np.sum(row) for row in widths]) return plots, total_width + + +def py2js_tickformatter(formatter, msg=''): + """ + Uses flexx.pyscript to compile a python tick formatter to JS code + """ + try: + from flexx.pyscript import py2js + except ImportError: + self.warning(msg+'Ensure Flexx is installed ' + '("conda install -c bokeh flexx" or ' + '"pip install flexx")') + return + try: + jscode = py2js(formatter, 'formatter') + except Exception as e: + error = 'Pyscript raised an error: {0}'.format(e) + error = error.replace('%', '%%') + self.warning(msg+error) + return + + args = inspect.getargspec(formatter).args + arg_define = 'var %s = tick;' % args[0] if args else '' + return_js = 'return formatter();\n' + jsfunc = '\n'.join([arg_define, jscode, return_js]) + match = re.search('(function \(.*\))', jsfunc ) + return jsfunc[:match.start()] + 'function ()' + jsfunc[match.end():] + diff --git a/tests/testplotutils.py b/tests/testplotutils.py new file mode 100644 index 0000000000..fe170ff489 --- /dev/null +++ b/tests/testplotutils.py @@ -0,0 +1,42 @@ +from unittest import SkipTest + +from holoviews.core.options import Store +from holoviews.element.comparison import ComparisonTestCase + +try: + from holoviews.plotting.bokeh import util + bokeh_renderer = Store.renderers['bokeh'] +except: + bokeh_renderer = None + + +class TestBokehUtils(ComparisonTestCase): + + def setUp(self): + if not bokeh_renderer: + raise SkipTest("Bokeh required to test bokeh plot utils.") + + + def test_py2js_funcformatter_single_arg(self): + def test(x): return '%s$' % x + jsfunc = util.py2js_tickformatter(test) + js_func = ('var x = tick;\nvar formatter;\nformatter = function () {\n' + ' return "" + x + "$";\n};\n\nreturn formatter();\n') + self.assertEqual(jsfunc, js_func) + + + def test_py2js_funcformatter_two_args(self): + def test(x, pos): return '%s$' % x + jsfunc = util.py2js_tickformatter(test) + js_func = ('var x = tick;\nvar formatter;\nformatter = function () {\n' + ' return "" + x + "$";\n};\n\nreturn formatter();\n') + self.assertEqual(jsfunc, js_func) + + + def test_py2js_funcformatter_arg_and_kwarg(self): + def test(x, pos=None): return '%s$' % x + jsfunc = util.py2js_tickformatter(test) + js_func = ('var x = tick;\nvar formatter;\nformatter = function () {\n' + ' pos = (pos === undefined) ? null: pos;\n return "" ' + '+ x + "$";\n};\n\nreturn formatter();\n') + self.assertEqual(jsfunc, js_func) From 30b0d254424588b2b00865f129a9a02a40191831 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Sat, 10 Dec 2016 19:06:43 +0000 Subject: [PATCH 2/3] Fixed warnings --- holoviews/plotting/bokeh/util.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/holoviews/plotting/bokeh/util.py b/holoviews/plotting/bokeh/util.py index 67b5b3bc6f..bfaad130a8 100644 --- a/holoviews/plotting/bokeh/util.py +++ b/holoviews/plotting/bokeh/util.py @@ -10,6 +10,7 @@ except ImportError: cm, colors = None, None +import param import bokeh bokeh_version = LooseVersion(bokeh.__version__) from bokeh.core.enums import Palette @@ -381,16 +382,16 @@ def py2js_tickformatter(formatter, msg=''): try: from flexx.pyscript import py2js except ImportError: - self.warning(msg+'Ensure Flexx is installed ' - '("conda install -c bokeh flexx" or ' - '"pip install flexx")') + param.main.warning(msg+'Ensure Flexx is installed ' + '("conda install -c bokeh flexx" or ' + '"pip install flexx")') return try: jscode = py2js(formatter, 'formatter') except Exception as e: error = 'Pyscript raised an error: {0}'.format(e) error = error.replace('%', '%%') - self.warning(msg+error) + param.main.warning(msg+error) return args = inspect.getargspec(formatter).args From a1dd750c4c72c02907f234a5c21fe00eba1e4ee4 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Sat, 10 Dec 2016 19:08:37 +0000 Subject: [PATCH 3/3] Install flexx on travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c7264fc11b..9e95612cc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ install: - conda info -a - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION scipy numpy freetype nose bokeh pandas jupyter ipython=4.2.0 param pyqt=4 matplotlib=1.5.1 xarray datashader - source activate test-environment - - conda install -c conda-forge -c scitools iris sip=4.18 plotly + - conda install -c conda-forge -c scitools iris sip=4.18 plotly flexx - if [[ "$TRAVIS_PYTHON_VERSION" == "3.4" ]]; then conda install python=3.4.3; fi