diff --git a/.travis.yml b/.travis.yml index 6585252ed0..0995382a34 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,10 +27,10 @@ install: - conda update -q conda # Useful for debugging any issues with conda - conda info -a - - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION scipy=0.18.1 numpy freetype nose pandas=0.19.2 jupyter ipython=4.2.0 param pyqt=4 matplotlib=1.5.1 xarray + - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION flake8 scipy=1.0.0 numpy freetype nose pandas=0.22.0 jupyter ipython=4.2.0 param matplotlib=2.1.2 xarray - source activate test-environment - - conda install -c conda-forge iris sip=4.18 plotly flexx - - conda install -c bokeh datashader dask=0.13 bokeh=0.12.10 selenium + - conda install -c conda-forge iris plotly flexx --quiet + - conda install -c bokeh datashader dask=0.16.1 bokeh=0.12.14 selenium - if [[ "$TRAVIS_PYTHON_VERSION" == "3.4" ]]; then conda install python=3.4.3; fi @@ -68,8 +68,9 @@ before-script: script: - export HOLOVIEWSRC=`pwd`'/holoviews.rc' - echo 'import holoviews as hv;hv.config(style_17=True);hv.config.warn_options_call=True' > holoviews.rc - - nosetests --with-doctest --with-coverage --cover-package=holoviews - - cd doc/nbpublisher; chmod +x test_notebooks.py; BOKEH_DEV=True ./test_notebooks.py + - flake8 --ignore=E,W,F999,F405 holoviews tests + - nosetests --with-doctest --with-coverage --cover-package=holoviews -v + - cd doc/nbpublisher; chmod +x test_notebooks.py; QT_QPA_PLATFORM='offscreen' BOKEH_DEV=True ./test_notebooks.py - chmod +x concat_html.py; ./concat_html.py ../test_data ../test_html - cd ../../; mv doc/Tutorials/.coverage ./.coverage.notebooks - coverage combine --append diff --git a/CHANGELOG.md b/CHANGELOG.md index e774b541fc..4ea1a14173 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +Version 1.9.4 +============= + +This release contains a small number of important bug fixes: + +- Compatibility with recent versions of dask and pandas + ([\#2329](https://github.com/ioam/holoviews/pull/2329)) +- Fixed bug referencing columns containing non-alphanumeric characters + in bokeh Tables ([\#2336](https://github.com/ioam/holoviews/pull/2336)) +- Fixed issue in regrid operation + ([2337](https://github.com/ioam/holoviews/pull/2337)) +- Fixed issue when using datetimes with datashader when processing + ranges ([\#2344](https://github.com/ioam/holoviews/pull/2344)) + + Version 1.9.3 ============= diff --git a/holoviews/__init__.py b/holoviews/__init__.py index 9c40343151..a2e037c31d 100644 --- a/holoviews/__init__.py +++ b/holoviews/__init__.py @@ -5,7 +5,7 @@ import numpy as np # noqa (API import) import param -__version__ = param.Version(release=(1,9,3), fpath=__file__, +__version__ = param.Version(release=(1,9,4), fpath=__file__, commit="$Format:%h$", reponame='holoviews') from .core import archive, config # noqa (API import) diff --git a/holoviews/core/data/dask.py b/holoviews/core/data/dask.py index 458e72c8c9..69f6bf3803 100644 --- a/holoviews/core/data/dask.py +++ b/holoviews/core/data/dask.py @@ -109,7 +109,7 @@ def select_mask(cls, dataset, selection): else: masks.append(series == k) for mask in masks: - if select_mask: + if select_mask is not None: select_mask &= mask else: select_mask = mask @@ -145,7 +145,10 @@ def groupby(cls, columns, dimensions, container_type, group_type, **kwargs): if len(group_by) == 1: column = columns.data[group_by[0]] if column.dtype.name == 'category': - indices = ((ind,) for ind in column.cat.categories) + try: + indices = ((ind,) for ind in column.cat.categories) + except NotImplementedError: + indices = ((ind,) for ind in column.unique().compute()) else: indices = ((ind,) for ind in column.unique().compute()) else: diff --git a/holoviews/core/data/dictionary.py b/holoviews/core/data/dictionary.py index c904560c19..39a7065154 100644 --- a/holoviews/core/data/dictionary.py +++ b/holoviews/core/data/dictionary.py @@ -214,7 +214,7 @@ def values(cls, dataset, dim, expanded=True, flat=True): else: if not expanded: return util.unique_array(values) - values = np.array(values) + values = np.asarray(values) return values diff --git a/holoviews/core/traversal.py b/holoviews/core/traversal.py index e320601d17..3b97e2410d 100644 --- a/holoviews/core/traversal.py +++ b/holoviews/core/traversal.py @@ -72,7 +72,7 @@ def unique_dimkeys(obj, default_dim='Frame'): for d, k in zip(dims, key): dim_keys[d.name] = k if dim_keys: - keys = [tuple(dim_keys[dim.name] for dim in dimensions)] + keys = [tuple(dim_keys.get(dim.name) for dim in dimensions)] else: keys = [] return merge_dimensions(dim_groups), keys diff --git a/holoviews/core/util.py b/holoviews/core/util.py index 603f19648b..18949960ce 100644 --- a/holoviews/core/util.py +++ b/holoviews/core/util.py @@ -180,7 +180,7 @@ def run(self): self.counter += 1 try: self.callback(self.counter) - except Exception as e: + except Exception: self.stop() if self.timeout is not None: @@ -213,7 +213,7 @@ def deephash(obj): basestring = basestring unicode = unicode from itertools import izip - generator_types = (izip, xrange, types.GeneratorType) + generator_types = (izip, xrange, types.GeneratorType) # noqa @@ -1555,8 +1555,14 @@ def dt_to_int(value, time_unit='us'): """ Converts a datetime type to an integer with the supplied time unit. """ - if time_unit == 'ns': - tscale = 1./np.timedelta64(1, time_unit).tolist() + if isinstance(value, np.datetime64): + value = np.datetime64(value, 'ns') + if time_unit == 'ns': + tscale = 1 + else: + tscale = (np.timedelta64(1, time_unit)/np.timedelta64(1, 'ns')) * 1000. + elif time_unit == 'ns': + tscale = 1000. else: tscale = 1./np.timedelta64(1, time_unit).tolist().total_seconds() if pd and isinstance(value, pd.Timestamp): diff --git a/holoviews/element/stats.py b/holoviews/element/stats.py index 057792209f..e18a11c29c 100644 --- a/holoviews/element/stats.py +++ b/holoviews/element/stats.py @@ -75,7 +75,7 @@ def dframe(self, dimensions=None): dimensions = [self.get_dimension(d, strict=True) for d in dimensions if d in dimensions.kdims] else: - dimensions = dimensions.kdims + dimensions = self.kdims return self.interface.dframe(self, dimensions) diff --git a/holoviews/element/util.py b/holoviews/element/util.py index f9084be52b..8c7689535d 100644 --- a/holoviews/element/util.py +++ b/holoviews/element/util.py @@ -241,6 +241,8 @@ def circular_layout(nodes): Lay out nodes on a circle and add node index. """ N = len(nodes) + if not N: + return ([], [], []) circ = np.pi/N*np.arange(N)*2 x = np.cos(circ) y = np.sin(circ) @@ -269,7 +271,6 @@ def connect_edges_pd(graph): df = df.sort_values('graph_edge_index').drop(['graph_edge_index'], axis=1) edge_segments = [] - N = len(nodes) for i, edge in df.iterrows(): start = edge['src_x'], edge['src_y'] end = edge['dst_x'], edge['dst_y'] diff --git a/holoviews/operation/datashader.py b/holoviews/operation/datashader.py index acba9a91df..2f4e5a3d23 100644 --- a/holoviews/operation/datashader.py +++ b/holoviews/operation/datashader.py @@ -136,8 +136,8 @@ def _get_sampling(self, element, x, y): ytype = 'datetime' elif not np.isfinite(ystart) and not np.isfinite(yend): if element.get_dimension_type(y) in datetime_types: - xstart, xend = 0, 10000 - xtype = 'datetime' + ystart, yend = 0, 10000 + ytype = 'datetime' else: ystart, yend = 0, 1 elif ystart == yend: @@ -239,7 +239,7 @@ def get_agg_data(cls, obj, category=None): paths = [p.compute() if isinstance(p, dd.DataFrame) else p for p in paths] df = pd.concat(paths) else: - df = paths[0] + df = paths[0] if paths else pd.DataFrame([], columns=[x.name, y.name]) if category and df[category].dtype.name != 'category': df[category] = df[category].astype('category') @@ -338,6 +338,10 @@ def _process(self, element, key=None): xarray = xr.DataArray(np.full((height, width), np.NaN, dtype=np.float32), dims=['y', 'x'], coords={'x': xs, 'y': ys}) return self.p.element_type(xarray) + elif not len(data): + xarray = xr.DataArray(np.full((height, width), np.NaN, dtype=np.float32), + dims=[y.name, x.name], coords={x.name: xs, y.name: ys}) + return self.p.element_type(xarray) cvs = ds.Canvas(plot_width=width, plot_height=height, x_range=x_range, y_range=y_range) @@ -432,7 +436,7 @@ def _get_xarrays(self, element, coords, xtype, ytype): xarr = element.data[vd.name] if 'datetime' in (xtype, ytype): xarr = xarr.copy() - if dims != xarr.dims and not irregular: + if dims != xarr.dims: xarr = xarr.transpose(*dims) else: arr = element.dimension_values(vd, flat=False) @@ -492,6 +496,7 @@ def _process(self, element, key=None): datatype=['xarray']+element.datatype) + class shade(Operation): """ shade applies a normalization function followed by colormapping to diff --git a/holoviews/plotting/bokeh/callbacks.py b/holoviews/plotting/bokeh/callbacks.py index 5659820e81..e184a010fd 100644 --- a/holoviews/plotting/bokeh/callbacks.py +++ b/holoviews/plotting/bokeh/callbacks.py @@ -121,7 +121,8 @@ def _init_plot_handles(self): requested[h] = handles[h] elif h in self.extra_models: print("Warning %s could not find the %s model. " - "The corresponding stream may not work.") + "The corresponding stream may not work." + % (type(self).__name__, h)) self.handle_ids.update(self._get_stream_handle_ids(requested)) return requested diff --git a/holoviews/plotting/bokeh/chart.py b/holoviews/plotting/bokeh/chart.py index 5cf6b79307..022f915d64 100644 --- a/holoviews/plotting/bokeh/chart.py +++ b/holoviews/plotting/bokeh/chart.py @@ -3,20 +3,19 @@ import numpy as np import param from bokeh.models import (CategoricalColorMapper, CustomJS, HoverTool, - FactorRange, Whisker, Band, Range1d, Circle, - VBar, HBar) + FactorRange, Whisker, Range1d, Circle, VBar, + HBar) from bokeh.models.tools import BoxSelectTool from bokeh.transform import jitter -from ...core import Dataset, OrderedDict -from ...core.dimension import Dimension +from ...core import Dataset, OrderedDict, Dimension from ...core.util import (max_range, basestring, dimension_sanitizer, - wrap_tuple, unique_iterator) + unique_iterator, wrap_tuple) from ...element import Bars from ...operation import interpolate_curve from ..util import compute_sizes, get_min_distance, dim_axis_label -from .element import (ElementPlot, ColorbarPlot, LegendPlot, CompositeElementPlot, - line_properties, fill_properties) +from .element import (ElementPlot, CompositeElementPlot, ColorbarPlot, + LegendPlot, line_properties, fill_properties) from .util import expand_batched_style, categorize_array, rgb2hex, mpl_to_bokeh @@ -1078,6 +1077,7 @@ def get_data(self, element, ranges, style): # Compute statistics vals = g.dimension_values(g.vdims[0]) + vals = vals[np.isfinite(vals)] if len(vals): qmin, q1, q2, q3, qmax = (np.percentile(vals, q=q) for q in range(0,125,25)) iqr = q3 - q1 diff --git a/holoviews/plotting/bokeh/element.py b/holoviews/plotting/bokeh/element.py index 9a68ffc945..d75aace0c6 100644 --- a/holoviews/plotting/bokeh/element.py +++ b/holoviews/plotting/bokeh/element.py @@ -1,4 +1,3 @@ -from itertools import groupby import warnings import param @@ -21,7 +20,7 @@ from ...core import DynamicMap, CompositeOverlay, Element, Dimension from ...core.options import abbreviated_exception, SkipRendering from ...core import util -from ...streams import Stream, Buffer +from ...streams import Buffer from ..plot import GenericElementPlot, GenericOverlayPlot from ..util import dynamic_update, process_cmap from .plot import BokehPlot, TOOLS diff --git a/holoviews/plotting/bokeh/plot.py b/holoviews/plotting/bokeh/plot.py index e870cb79be..e2f9e0b3b1 100644 --- a/holoviews/plotting/bokeh/plot.py +++ b/holoviews/plotting/bokeh/plot.py @@ -7,11 +7,9 @@ from bokeh.models import (ColumnDataSource, Column, Row, Div) from bokeh.models.widgets import Panel, Tabs -from ...core import (OrderedDict, Store, GridMatrix, AdjointLayout, - NdLayout, Empty, GridSpace, HoloMap, Element, - DynamicMap) +from ...core import (OrderedDict, Store, AdjointLayout, NdLayout, + Empty, GridSpace, HoloMap, Element, DynamicMap) from ...core.util import basestring, wrap_tuple, unique_iterator -from ...element import Histogram from ...streams import Stream from ..plot import (DimensionedPlot, GenericCompositePlot, GenericLayoutPlot, GenericElementPlot, GenericOverlayPlot) diff --git a/holoviews/plotting/bokeh/raster.py b/holoviews/plotting/bokeh/raster.py index d9b29f57b5..535b712532 100644 --- a/holoviews/plotting/bokeh/raster.py +++ b/holoviews/plotting/bokeh/raster.py @@ -3,8 +3,8 @@ from bokeh.models import HoverTool from ...core.util import cartesian_product, is_nan, dimension_sanitizer +from ...core.options import SkipRendering from ...element import Raster -from ..renderer import SkipRendering from .element import ElementPlot, ColorbarPlot, line_properties, fill_properties diff --git a/holoviews/plotting/bokeh/tabular.py b/holoviews/plotting/bokeh/tabular.py index 56fdcc9ad0..617b886483 100644 --- a/holoviews/plotting/bokeh/tabular.py +++ b/holoviews/plotting/bokeh/tabular.py @@ -70,9 +70,23 @@ def initialize_plot(self, ranges=None, plot=None, plots=None, source=None): source = self._init_datasource(data) self.handles['source'] = source + columns = self._get_columns(element, data) + style['reorderable'] = False + table = DataTable(source=source, columns=columns, height=self.height, + width=self.width, **style) + self.handles['plot'] = table + self.handles['glyph_renderer'] = table + self._execute_hooks(element) + self.drawn = True + + for cb in self.callbacks: + cb.initialize() + + return table + + def _get_columns(self, element, data): columns = [] - dims = element.dimensions() - for d in dims: + for d in element.dimensions(): col = dimension_sanitizer(d.name) kind = data[col].dtype.kind if kind == 'i': @@ -89,21 +103,10 @@ def initialize_plot(self, ranges=None, plot=None, plots=None, source=None): else: formatter = StringFormatter() editor = StringEditor() - column = TableColumn(field=d.name, title=d.pprint_label, + column = TableColumn(field=dimension_sanitizer(d.name), title=d.pprint_label, editor=editor, formatter=formatter) columns.append(column) - style['reorderable'] = False - table = DataTable(source=source, columns=columns, height=self.height, - width=self.width, **style) - self.handles['plot'] = table - self.handles['glyph_renderer'] = table - self._execute_hooks(element) - self.drawn = True - - for cb in self.callbacks: - cb.initialize() - - return table + return columns def update_frame(self, key, ranges=None, plot=None): diff --git a/holoviews/plotting/mpl/__init__.py b/holoviews/plotting/mpl/__init__.py index 8bbdd1137c..57c1a800f6 100644 --- a/holoviews/plotting/mpl/__init__.py +++ b/holoviews/plotting/mpl/__init__.py @@ -26,8 +26,12 @@ mpl_ge_150 = LooseVersion(mpl.__version__) >= '1.5.0' if pd: - from pandas.tseries import converter - converter.register() + try: + from pandas.plotting import register_matplotlib_converters + register_matplotlib_converters() + except ImportError: + from pandas.tseries import converter + converter.register() def set_style(key): diff --git a/meta.yaml b/meta.yaml index 616d7941f1..56f13322c2 100644 --- a/meta.yaml +++ b/meta.yaml @@ -1,6 +1,6 @@ package: name: holoviews - version: 1.9.3 + version: 1.9.4 source: path: . diff --git a/setup.py b/setup.py index 057859a1be..6435b2a196 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ setup_args.update(dict( name='holoviews', - version="1.9.3", + version="1.9.4", install_requires=install_requires, extras_require=extras_require, description='Stop plotting your data - annotate your data and let it visualize itself.', diff --git a/tests/operation/testdatashader.py b/tests/operation/testdatashader.py index cf617ca517..fd19109c19 100644 --- a/tests/operation/testdatashader.py +++ b/tests/operation/testdatashader.py @@ -11,7 +11,7 @@ import datashader as ds from holoviews.operation.datashader import ( aggregate, regrid, ds_version, stack, directly_connect_edges, - shade, rasterize + shade ) except: ds_version = None @@ -77,6 +77,20 @@ def test_aggregate_curve_datetimes(self): datatype=['xarray'], bounds=bounds, vdims='Count') self.assertEqual(img, expected) + def test_aggregate_curve_datetimes_microsecond_timebase(self): + dates = pd.date_range(start="2016-01-01", end="2016-01-03", freq='1D') + xstart = np.datetime64('2015-12-31T23:59:59.723518000', 'us') + xend = np.datetime64('2016-01-03T00:00:00.276482000', 'us') + curve = Curve((dates, [1, 2, 3])) + img = aggregate(curve, width=2, height=2, x_range=(xstart, xend), dynamic=False) + bounds = (np.datetime64('2015-12-31T23:59:59.585277000'), 1.0, + np.datetime64('2016-01-03T00:00:00.414723000'), 3.0) + dates = [np.datetime64('2016-01-01T11:59:59.861759000',), + np.datetime64('2016-01-02T12:00:00.138241000')] + expected = Image((dates, [1.5, 2.5], [[1, 0], [0, 2]]), + datatype=['xarray'], bounds=bounds, vdims='Count') + self.assertEqual(img, expected) + def test_aggregate_ndoverlay(self): ds = Dataset([(0.2, 0.3, 0), (0.4, 0.7, 1), (0, 0.99, 2)], kdims=['x', 'y', 'z']) ndoverlay = ds.to(Points, ['x', 'y'], [], 'z').overlay() diff --git a/tests/plotting/bokeh/testserver.py b/tests/plotting/bokeh/testserver.py index 2c02af79f6..69aeffea1f 100644 --- a/tests/plotting/bokeh/testserver.py +++ b/tests/plotting/bokeh/testserver.py @@ -2,7 +2,7 @@ from holoviews.core.spaces import DynamicMap from holoviews.core.options import Store -from holoviews.element import Curve, Polygons, Path, Image, HLine +from holoviews.element import Curve, Polygons, Path, HLine from holoviews.element.comparison import ComparisonTestCase from holoviews.streams import RangeXY, PlotReset @@ -53,6 +53,7 @@ def test_set_up_linked_change_stream_on_server_doc(self): obj = Curve([]) stream = RangeXY(source=obj) server_doc = bokeh_renderer.server_doc(obj) + self.assertIsInstance(server_doc, Document) self.assertEqual(len(bokeh_renderer.last_plot.callbacks), 1) cb = bokeh_renderer.last_plot.callbacks[0] self.assertIsInstance(cb, RangeXYCallback) @@ -68,6 +69,7 @@ def test_set_up_linked_event_stream_on_server_doc(self): obj = Curve([]) stream = PlotReset(source=obj) server_doc = bokeh_renderer.server_doc(obj) + self.assertIsInstance(server_doc, Document) cb = bokeh_renderer.last_plot.callbacks[0] self.assertIsInstance(cb, ResetCallback) self.assertEqual(cb.streams, [stream]) diff --git a/tests/plotting/bokeh/testtabular.py b/tests/plotting/bokeh/testtabular.py index 7ec1ef3068..b3a8c2ba8c 100644 --- a/tests/plotting/bokeh/testtabular.py +++ b/tests/plotting/bokeh/testtabular.py @@ -1,14 +1,13 @@ from datetime import datetime as dt from unittest import SkipTest -from holoviews.core.spaces import DynamicMap from holoviews.core.options import Store from holoviews.element import Table from holoviews.element.comparison import ComparisonTestCase try: from bokeh.models.widgets import ( - DataTable, TableColumn, NumberEditor, NumberFormatter, DateFormatter, + NumberEditor, NumberFormatter, DateFormatter, DateEditor, StringFormatter, StringEditor, IntEditor ) from holoviews.plotting.bokeh.renderer import BokehRenderer @@ -40,6 +39,13 @@ def test_table_plot(self): self.assertIsInstance(column.formatter, fmt) self.assertIsInstance(column.editor, edit) + def test_table_plot_escaped_dimension(self): + table = Table([1, 2, 3], ['A Dimension']) + plot = bokeh_renderer.get_plot(table) + source = plot.handles['source'] + renderer = plot.handles['glyph_renderer'] + self.assertEqual(list(source.data.keys())[0], renderer.columns[0].field) + def test_table_plot_datetimes(self): table = Table([dt.now(), dt.now()], 'Date') plot = bokeh_renderer.get_plot(table) diff --git a/tests/plotting/testplotutils.py b/tests/plotting/testplotutils.py index 20d6de3ec0..351b6f918b 100644 --- a/tests/plotting/testplotutils.py +++ b/tests/plotting/testplotutils.py @@ -9,7 +9,7 @@ from holoviews.core.spaces import DynamicMap, HoloMap from holoviews.core.options import Store, Cycle from holoviews.element.comparison import ComparisonTestCase -from holoviews.element import (Image, Scatter, Curve, Text, Points, +from holoviews.element import (Image, Scatter, Curve, Points, Area, VectorField, HLine, Path) from holoviews.operation import operation from holoviews.plotting.util import ( @@ -419,7 +419,7 @@ def test_dmap_overlay_linked_operation_mul_dmap_ndoverlay(self): layers = [mapped, mapped, self.dmap_ndoverlay] self.assertEqual(split_dmap_overlay(test), layers) - def test_dmap_overlay_linked_operation_mul_dmap_ndoverlay(self): + def test_dmap_overlay_linked_operation_mul_dmap_element_ndoverlay(self): mapped = self.dmap_overlay.map(lambda x: x.get(0), Overlay) test = mapped * self.element * self.dmap_ndoverlay initialize_dynamic(test) @@ -456,11 +456,11 @@ def test_process_cmap_cycle(self): def test_process_cmap_invalid_str(self): with self.assertRaises(ValueError): - colors = process_cmap('NonexistentColorMap', 3) + process_cmap('NonexistentColorMap', 3) def test_process_cmap_invalid_type(self): with self.assertRaises(TypeError): - colors = process_cmap({'A', 'B', 'C'}, 3) + process_cmap({'A', 'B', 'C'}, 3) class TestPlotUtils(ComparisonTestCase): diff --git a/tests/testbokehcallbacks.py b/tests/testbokehcallbacks.py index 102918f1ca..1dda34e739 100644 --- a/tests/testbokehcallbacks.py +++ b/tests/testbokehcallbacks.py @@ -7,15 +7,13 @@ from holoviews.element.comparison import ComparisonTestCase from holoviews.streams import Selection1D -try: - from holoviews.plotting.bokeh.callbacks import Callback, Selection1DCallback - from holoviews.plotting.bokeh.util import bokeh_version +from holoviews.plotting.bokeh.callbacks import Callback, Selection1DCallback +from holoviews.plotting.bokeh.util import bokeh_version + +from bokeh.events import Tap +from bokeh.models import Range1d, Plot, ColumnDataSource +bokeh_renderer = Store.renderers['bokeh'] - from bokeh.events import Tap - from bokeh.models import Range1d, Plot, ColumnDataSource - bokeh_renderer = Store.renderers['bokeh'] -except: - bokeh_renderer = None @attr(optional=1) class TestBokehCustomJSCallbacks(ComparisonTestCase): @@ -61,7 +59,8 @@ def test_callback_on_ndoverlay_is_attached(self): plot = bokeh_renderer.get_plot(ndoverlay) self.assertEqual(len(plot.callbacks), 1) self.assertIsInstance(plot.callbacks[0], Selection1DCallback) - + self.assertIn(selection, plot.callbacks[0].streams) + @attr(optional=1) class TestBokehServerJSCallbacks(ComparisonTestCase): diff --git a/tests/testbokehgraphs.py b/tests/testbokehgraphs.py index b596e82e22..721a64fabb 100644 --- a/tests/testbokehgraphs.py +++ b/tests/testbokehgraphs.py @@ -7,10 +7,8 @@ from holoviews.core.options import Store from holoviews.element import Graph, circular_layout from holoviews.element.comparison import ComparisonTestCase -from holoviews.plotting import comms try: - from holoviews.plotting.bokeh.util import bokeh_version bokeh_renderer = Store.renderers['bokeh'] from bokeh.models import (NodesAndLinkedEdges, EdgesAndLinkedNodes) from bokeh.models.mappers import CategoricalColorMapper, LinearColorMapper @@ -98,7 +96,6 @@ def test_graph_inspection_policy_edges_non_default_names(self): def test_graph_inspection_policy_none(self): plot = bokeh_renderer.get_plot(self.graph.opts(plot=dict(inspection_policy=None))) renderer = plot.handles['glyph_renderer'] - hover = plot.handles['hover'] self.assertIs(renderer.inspection_policy, None) def test_graph_selection_policy_nodes(self): @@ -118,7 +115,6 @@ def test_graph_selection_policy_edges(self): def test_graph_selection_policy_none(self): plot = bokeh_renderer.get_plot(self.graph.opts(plot=dict(selection_policy=None))) renderer = plot.handles['glyph_renderer'] - hover = plot.handles['hover'] self.assertIs(renderer.selection_policy, None) def test_graph_nodes_categorical_colormapped(self): @@ -145,6 +141,8 @@ def test_graph_nodes_numerically_colormapped(self): self.assertEqual(glyph.fill_color, {'field': 'Weight', 'transform': cmapper}) def test_graph_edges_categorical_colormapped(self): + raise SkipTest('Temporarily disabled until array interface is simplified.') + g = self.graph3.opts(plot=dict(edge_color_index='start'), style=dict(edge_cmap=['#FFFFFF', '#000000'])) plot = bokeh_renderer.get_plot(g) @@ -156,8 +154,8 @@ def test_graph_edges_categorical_colormapped(self): self.assertEqual(cmapper.factors, factors) self.assertEqual(edge_source.data['start_str'], factors) self.assertEqual(glyph.line_color, {'field': 'start_str', 'transform': cmapper}) - - def test_graph_nodes_numerically_colormapped(self): + + def test_graph_edges_numerically_colormapped(self): g = self.graph4.opts(plot=dict(edge_color_index='Weight'), style=dict(edge_cmap=['#FFFFFF', '#000000'])) plot = bokeh_renderer.get_plot(g) diff --git a/tests/testcallable.py b/tests/testcallable.py index 80429bf671..ed3b5537e6 100644 --- a/tests/testcallable.py +++ b/tests/testcallable.py @@ -55,7 +55,7 @@ def test_partial_name(self): def test_generator_expression_name(self): if sys.version_info < (3,0): - cb = Generator((i for i in xrange(10))) + cb = Generator((i for i in xrange(10))) # noqa else: cb = Generator((i for i in range(10))) self.assertEqual(cb.name, '') diff --git a/tests/testcoreutils.py b/tests/testcoreutils.py index 5dfa222a0d..dec09af219 100644 --- a/tests/testcoreutils.py +++ b/tests/testcoreutils.py @@ -558,14 +558,30 @@ def test_datetime_to_us_int(self): dt = datetime.datetime(2017, 1, 1) self.assertEqual(dt_to_int(dt), 1483228800000000.0) - def test_datetime64_to_us_int(self): + def test_datetime64_s_to_ns_int(self): + dt = np.datetime64(datetime.datetime(2017, 1, 1), 's') + self.assertEqual(dt_to_int(dt, 'ns'), 1483228800000000000000.0) + + def test_datetime64_us_to_ns_int(self): + dt = np.datetime64(datetime.datetime(2017, 1, 1), 'us') + self.assertEqual(dt_to_int(dt, 'ns'), 1483228800000000000000.0) + + def test_datetime64_to_ns_int(self): dt = np.datetime64(datetime.datetime(2017, 1, 1)) + self.assertEqual(dt_to_int(dt, 'ns'), 1483228800000000000000.0) + + def test_datetime64_us_to_us_int(self): + dt = np.datetime64(datetime.datetime(2017, 1, 1), 'us') + self.assertEqual(dt_to_int(dt), 1483228800000000.0) + + def test_datetime64_s_to_us_int(self): + dt = np.datetime64(datetime.datetime(2017, 1, 1), 's') self.assertEqual(dt_to_int(dt), 1483228800000000.0) def test_timestamp_to_us_int(self): dt = pd.Timestamp(datetime.datetime(2017, 1, 1)) self.assertEqual(dt_to_int(dt), 1483228800000000.0) - + def test_datetime_to_s_int(self): dt = datetime.datetime(2017, 1, 1) self.assertEqual(dt_to_int(dt, 's'), 1483228800.0) @@ -574,6 +590,14 @@ def test_datetime64_to_s_int(self): dt = np.datetime64(datetime.datetime(2017, 1, 1)) self.assertEqual(dt_to_int(dt, 's'), 1483228800.0) + def test_datetime64_us_to_s_int(self): + dt = np.datetime64(datetime.datetime(2017, 1, 1), 'us') + self.assertEqual(dt_to_int(dt, 's'), 1483228800.0) + + def test_datetime64_s_to_s_int(self): + dt = np.datetime64(datetime.datetime(2017, 1, 1), 's') + self.assertEqual(dt_to_int(dt, 's'), 1483228800.0) + def test_timestamp_to_s_int(self): dt = pd.Timestamp(datetime.datetime(2017, 1, 1)) self.assertEqual(dt_to_int(dt, 's'), 1483228800.0) diff --git a/tests/testdataset.py b/tests/testdataset.py index 0f9028eca5..8b505b8e54 100644 --- a/tests/testdataset.py +++ b/tests/testdataset.py @@ -8,7 +8,6 @@ import numpy as np from holoviews import Dataset, HoloMap, Dimension, Image -from holoviews.core.data.interface import DataError from holoviews.element import Distribution, Points, Scatter from holoviews.element.comparison import ComparisonTestCase @@ -105,11 +104,11 @@ def test_dataset_empty_list_init(self): def test_dataset_dict_dim_not_found_raises_on_array(self): with self.assertRaises(ValueError): - dataset = Dataset({'x': np.zeros(5)}, kdims=['Test'], vdims=[]) + Dataset({'x': np.zeros(5)}, kdims=['Test'], vdims=[]) def test_dataset_dict_dim_not_found_raises_on_scalar(self): with self.assertRaises(ValueError): - dataset = Dataset({'x': 1}, kdims=['Test'], vdims=[]) + Dataset({'x': 1}, kdims=['Test'], vdims=[]) # Properties and information @@ -917,7 +916,7 @@ def test_dataset_extract_kdims_declare_no_vdims(self): self.assertEqual(ds.kdims, [Dimension('x'), Dimension('y')]) self.assertEqual(ds.vdims, []) - def test_dataset_extract_kdims_declare_no_vdims(self): + def test_dataset_extract_no_kdims_extract_only_vdims(self): df = pd.DataFrame({'x': [1, 2, 3], 'y': [1, 2, 3], 'z': [1, 2, 3]}, columns=['x', 'y', 'z']) ds = Dataset(df, kdims=[]) diff --git a/tests/testelementconstructors.py b/tests/testelementconstructors.py index 3bbafeb609..20dab71d20 100644 --- a/tests/testelementconstructors.py +++ b/tests/testelementconstructors.py @@ -173,5 +173,3 @@ def test_quadmesh_string_signature(self): qmesh = QuadMesh(([0, 1], [0, 1], np.array([[0, 1], [0, 1]])), ['a', 'b'], 'c') self.assertEqual(qmesh.kdims, [Dimension('a'), Dimension('b')]) self.assertEqual(qmesh.vdims, [Dimension('c')]) - - diff --git a/tests/testgraphelement.py b/tests/testgraphelement.py index 7547b9f123..54b0b2eb23 100644 --- a/tests/testgraphelement.py +++ b/tests/testgraphelement.py @@ -71,14 +71,13 @@ def test_graph_edge_segments_pd(self): def test_constructor_with_nodes_and_paths(self): paths = Graph(((self.source, self.target), self.nodes)).edgepaths graph = Graph(((self.source, self.target), self.nodes, paths.data)) - nodes = Nodes(self.nodes) self.assertEqual(graph._edgepaths, paths) def test_constructor_with_nodes_and_paths_dimension_mismatch(self): paths = Graph(((self.source, self.target), self.nodes)).edgepaths exception = 'Ensure that the first two key dimensions on Nodes and EdgePaths match: x != x2' with self.assertRaisesRegexp(ValueError, exception): - graph = Graph(((self.source, self.target), self.nodes, paths.redim(x='x2'))) + Graph(((self.source, self.target), self.nodes, paths.redim(x='x2'))) def test_graph_clone_static_plot_id(self): self.assertEqual(self.graph.clone()._plot_id, self.graph._plot_id) diff --git a/tests/testimageinterfaces.py b/tests/testimageinterfaces.py index 2d7886dd09..bc5f8363b9 100644 --- a/tests/testimageinterfaces.py +++ b/tests/testimageinterfaces.py @@ -32,7 +32,7 @@ def test_init_data_tuple(self): xs = np.arange(5) ys = np.arange(10) array = xs * ys[:, np.newaxis] - image = Image((xs, ys, array)) + Image((xs, ys, array)) def test_init_data_tuple_error(self): xs = np.arange(5) @@ -44,12 +44,12 @@ def test_init_data_tuple_error(self): def test_init_data_datetime_xaxis(self): start = np.datetime64(dt.datetime.today()) end = start+np.timedelta64(1, 's') - image = Image(np.flipud(self.array), bounds=(start, 0, end, 10)) + Image(np.flipud(self.array), bounds=(start, 0, end, 10)) def test_init_data_datetime_yaxis(self): start = np.datetime64(dt.datetime.today()) end = start+np.timedelta64(1, 's') - image = Image(np.flipud(self.array), bounds=(-10, start, 10, end)) + Image(np.flipud(self.array), bounds=(-10, start, 10, end)) def test_init_bounds(self): self.assertEqual(self.image.bounds.lbrt(), (-10, 0, 10, 10)) @@ -283,13 +283,13 @@ def test_init_data_datetime_xaxis(self): start = np.datetime64(dt.datetime.today()) end = start+np.timedelta64(1, 's') xs = date_range(start, end, 10) - image = Image((xs, self.ys, self.array)) + Image((xs, self.ys, self.array)) def test_init_data_datetime_yaxis(self): start = np.datetime64(dt.datetime.today()) end = start+np.timedelta64(1, 's') ys = date_range(start, end, 10) - image = Image((self.xs, ys, self.array)) + Image((self.xs, ys, self.array)) def test_init_bounds_datetime_xaxis(self): start = np.datetime64(dt.datetime.today()) @@ -321,7 +321,7 @@ def test_init_densities_datetime_yaxis(self): self.assertEqual(image.xdensity, 0.5) self.assertEqual(image.ydensity, 1e-5) - def test_sample_datetime_yaxis(self): + def test_sample_datetime_xaxis(self): start = np.datetime64(dt.datetime.today()) end = start+np.timedelta64(1, 's') xs = date_range(start, end, 10) @@ -429,6 +429,13 @@ class ImageXArrayInterfaceTest(ImageGridInterfaceTest): datatype = 'xarray' + def setUp(self): + try: + import xarray as xr # noqa + except: + raise SkipTest('Test requires xarray') + super(ImageXArrayInterfaceTest, self).setUp() + @attr(optional=1) class ImageIrisInterfaceTest(ImageGridInterfaceTest): diff --git a/tests/testmplgraphs.py b/tests/testmplgraphs.py index 8a27b67b4a..631864bb1b 100644 --- a/tests/testmplgraphs.py +++ b/tests/testmplgraphs.py @@ -13,7 +13,6 @@ try: from matplotlib import pyplot pyplot.switch_backend('agg') - from holoviews.plotting.mpl import OverlayPlot mpl_renderer = Store.renderers['matplotlib'] except: mpl_renderer = None diff --git a/tests/testmultiinterface.py b/tests/testmultiinterface.py index c19cfe0b06..c98c11b591 100644 --- a/tests/testmultiinterface.py +++ b/tests/testmultiinterface.py @@ -5,7 +5,6 @@ from unittest import SkipTest import numpy as np -from holoviews import Dataset from holoviews.core.data.interface import DataError from holoviews.element import Path from holoviews.element.comparison import ComparisonTestCase @@ -114,13 +113,13 @@ def test_multi_mixed_interface_raises(self): arrays = [np.random.rand(10, 2) if j else {'x': range(10), 'y': range(10)} for i in range(2) for j in range(2)] with self.assertRaises(DataError): - mds = Path(arrays, kdims=['x', 'y'], datatype=['multitabular']) + Path(arrays, kdims=['x', 'y'], datatype=['multitabular']) def test_multi_mixed_dims_raises(self): arrays = [{'x': range(10), 'y' if j else 'z': range(10)} for i in range(2) for j in range(2)] with self.assertRaises(DataError): - mds = Path(arrays, kdims=['x', 'y'], datatype=['multitabular']) + Path(arrays, kdims=['x', 'y'], datatype=['multitabular']) def test_multi_split(self): arrays = [np.column_stack([np.arange(i, i+2), np.arange(i, i+2)]) for i in range(2)] diff --git a/tests/testoperation.py b/tests/testoperation.py index 5a4b54da3a..5bf3f513a9 100644 --- a/tests/testoperation.py +++ b/tests/testoperation.py @@ -6,7 +6,7 @@ from holoviews.element.comparison import ComparisonTestCase from holoviews.operation.element import (operation, transform, threshold, gradient, contours, histogram, - interpolate_curve, operation) + interpolate_curve) class OperationTests(ComparisonTestCase): """ diff --git a/tests/testplotinstantiation.py b/tests/testplotinstantiation.py index 5610038516..bfebb9f699 100644 --- a/tests/testplotinstantiation.py +++ b/tests/testplotinstantiation.py @@ -792,12 +792,12 @@ def test_points_color_selection_nonselection(self): vdims=['a', 'b']).opts(style=opts) plot = bokeh_renderer.get_plot(points) glyph_renderer = plot.handles['glyph_renderer'] - self.assertEqual(glyph_renderer.glyph.fill_color, 'green') - self.assertEqual(glyph_renderer.glyph.line_color, 'green') - self.assertEqual(glyph_renderer.selection_glyph.fill_color, 'red') - self.assertEqual(glyph_renderer.selection_glyph.line_color, 'red') - self.assertEqual(glyph_renderer.nonselection_glyph.fill_color, 'blue') - self.assertEqual(glyph_renderer.nonselection_glyph.line_color, 'blue') + self.assertEqual(glyph_renderer.glyph.fill_color, '#008000') + self.assertEqual(glyph_renderer.glyph.line_color, '#008000') + self.assertEqual(glyph_renderer.selection_glyph.fill_color, '#FF0000') + self.assertEqual(glyph_renderer.selection_glyph.line_color, '#FF0000') + self.assertEqual(glyph_renderer.nonselection_glyph.fill_color, '#0000FF') + self.assertEqual(glyph_renderer.nonselection_glyph.line_color, '#0000FF') def test_points_alpha_selection_nonselection(self): opts = dict(alpha=0.8, selection_alpha=1.0, nonselection_alpha=0.2) @@ -1388,21 +1388,21 @@ def test_curve_fontsize_xlabel(self): curve = Curve(range(10)).opts(plot=dict(fontsize={'xlabel': '14pt'})) plot = bokeh_renderer.get_plot(curve) self.assertEqual(plot.handles['xaxis'].axis_label_text_font_size, - {'value': '14pt'}) + '14pt') def test_curve_fontsize_ylabel(self): curve = Curve(range(10)).opts(plot=dict(fontsize={'ylabel': '14pt'})) plot = bokeh_renderer.get_plot(curve) self.assertEqual(plot.handles['yaxis'].axis_label_text_font_size, - {'value': '14pt'}) + '14pt') def test_curve_fontsize_both_labels(self): curve = Curve(range(10)).opts(plot=dict(fontsize={'labels': '14pt'})) plot = bokeh_renderer.get_plot(curve) self.assertEqual(plot.handles['xaxis'].axis_label_text_font_size, - {'value': '14pt'}) + '14pt') self.assertEqual(plot.handles['yaxis'].axis_label_text_font_size, - {'value': '14pt'}) + '14pt') def test_curve_fontsize_xticks(self): curve = Curve(range(10)).opts(plot=dict(fontsize={'xticks': '14pt'})) diff --git a/tests/testrenderclass.py b/tests/testrenderclass.py index 7158941daf..d5104e2ad1 100644 --- a/tests/testrenderclass.py +++ b/tests/testrenderclass.py @@ -73,12 +73,12 @@ def test_get_size_single_plot(self): def test_get_size_row_plot(self): plot = self.renderer.get_plot(self.image1+self.image2) w, h = self.renderer.get_size(plot) - self.assertEqual((w, h), (576, 258)) + self.assertEqual((w, h), (576, 255)) def test_get_size_column_plot(self): plot = self.renderer.get_plot((self.image1+self.image2).cols(1)) w, h = self.renderer.get_size(plot) - self.assertEqual((w, h), (288, 510)) + self.assertEqual((w, h), (288, 505)) def test_get_size_grid_plot(self): grid = GridSpace({(i, j): self.image1 for i in range(3) for j in range(3)}) @@ -92,11 +92,6 @@ def test_get_size_table(self): w, h = self.renderer.get_size(plot) self.assertEqual((w, h), (288, 288)) - def test_get_size_tables_in_layout(self): - table = Table(range(10), kdims=['x']) - plot = self.renderer.get_plot(table+table) - w, h = self.renderer.get_size(plot) - self.assertEqual((w, h), (576, 231)) @attr(optional=1) class BokehRendererTest(ComparisonTestCase): diff --git a/tests/teststatsoperations.py b/tests/teststatsoperations.py index f98f3cfdff..f17831c66b 100644 --- a/tests/teststatsoperations.py +++ b/tests/teststatsoperations.py @@ -1,14 +1,13 @@ from unittest import SkipTest -from nose.plugins.attrib import attr try: - import scipy + import scipy # noqa except: raise SkipTest('SciPy not available') import numpy as np -from holoviews import Distribution, Bivariate, Dataset, Area, Image +from holoviews import Distribution, Bivariate, Area, Image from holoviews.element.comparison import ComparisonTestCase from holoviews.operation.stats import (univariate_kde, bivariate_kde) diff --git a/tests/teststreams.py b/tests/teststreams.py index 7cc6fc3cb8..3f07462c9d 100644 --- a/tests/teststreams.py +++ b/tests/teststreams.py @@ -237,12 +237,12 @@ def tearDown(self): def test_source_registry(self): points = Points([(0, 0)]) - positionX = PointerX(source=points) + PointerX(source=points) self.assertIn(id(points), Stream.registry) def test_source_registry_empty_element(self): points = Points([]) - positionX = PointerX(source=points) + PointerX(source=points) self.assertIn(id(points), Stream.registry) @@ -367,7 +367,7 @@ def test_init_buffer_array(self): def test_buffer_array_ndim_exception(self): error = "Only 2D array data may be streamed by Buffer." with self.assertRaisesRegexp(ValueError, error): - buff = Buffer(np.array([0, 1])) + Buffer(np.array([0, 1])) def test_buffer_array_send(self): buff = Buffer(np.array([[0, 1]])) @@ -415,14 +415,14 @@ def test_buffer_dict_send(self): buff.send({'x': np.array([1]), 'y': np.array([2])}) self.assertEqual(buff.data, {'x': np.array([0, 1]), 'y': np.array([1, 2])}) - def test_buffer_array_larger_than_length(self): + def test_buffer_dict_larger_than_length(self): data = {'x': np.array([0]), 'y': np.array([1])} buff = Buffer(data, length=1) chunk = {'x': np.array([1]), 'y': np.array([2])} buff.send(chunk) self.assertEqual(buff.data, chunk) - def test_buffer_array_patch_larger_than_length(self): + def test_buffer_dict_patch_larger_than_length(self): data = {'x': np.array([0]), 'y': np.array([1])} buff = Buffer(data, length=1) chunk = {'x': np.array([1, 2]), 'y': np.array([2, 3])}