Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed bokeh FuncTickFormater #1010

Merged
merged 3 commits into from
Dec 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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

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)