Skip to content

Commit

Permalink
Merge pull request #1010 from ioam/functick_fix
Browse files Browse the repository at this point in the history
Fixed bokeh FuncTickFormater
  • Loading branch information
jlstevens committed Dec 11, 2016
2 parents d451899 + a1dd750 commit cb23e47
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 4 additions & 12 deletions holoviews/plotting/bokeh/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -456,17 +456,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

Expand Down
31 changes: 30 additions & 1 deletion holoviews/plotting/bokeh/util.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import itertools
import itertools, inspect, re
from distutils.version import LooseVersion
from collections import defaultdict

Expand All @@ -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
Expand Down Expand Up @@ -372,3 +373,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:
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('%', '%%')
param.main.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():]

42 changes: 42 additions & 0 deletions tests/testplotutils.py
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit cb23e47

Please sign in to comment.