From 50c6aa6b2fef8d4866e3e88dad37d534c6de20f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Wed, 11 Oct 2023 12:41:46 +0200 Subject: [PATCH] Dropping support for Bokeh 2 (#5891) --- holoviews/plotting/bokeh/callbacks.py | 19 +---- holoviews/plotting/bokeh/element.py | 40 +++------- holoviews/plotting/bokeh/graphs.py | 3 - holoviews/plotting/bokeh/links.py | 14 +--- holoviews/plotting/bokeh/plot.py | 22 ++--- holoviews/plotting/bokeh/raster.py | 16 +--- holoviews/plotting/bokeh/util.py | 36 +++------ .../tests/plotting/bokeh/test_elementplot.py | 6 +- .../tests/plotting/bokeh/test_graphplot.py | 22 ++--- .../tests/plotting/bokeh/test_gridplot.py | 11 +-- .../tests/plotting/bokeh/test_layoutplot.py | 80 +++---------------- holoviews/tests/plotting/bokeh/test_plot.py | 4 - .../tests/plotting/bokeh/test_rasterplot.py | 46 +++-------- .../tests/plotting/bokeh/test_renderer.py | 6 +- setup.py | 11 +-- 15 files changed, 69 insertions(+), 267 deletions(-) diff --git a/holoviews/plotting/bokeh/callbacks.py b/holoviews/plotting/bokeh/callbacks.py index 2fdce4335e..c9abcf539d 100644 --- a/holoviews/plotting/bokeh/callbacks.py +++ b/holoviews/plotting/bokeh/callbacks.py @@ -25,7 +25,7 @@ BoxEdit, PointDraw, PolyDraw, PolyEdit, CDSStream, FreehandDraw, CurveEdit, SelectionXY, Lasso, SelectMode ) -from .util import bokeh3, convert_timestamp +from .util import convert_timestamp class Callback: @@ -617,23 +617,6 @@ class RangeXYCallback(Callback): 'y1': 'cb_obj.y1', } - _js_on_event = """ - if (this._updating) - return - const plot = this.origin - const plots = plot.x_range.plots.concat(plot.y_range.plots) - for (const p of plots) { - const event = new this.constructor(p.x_range.start, p.x_range.end, p.y_range.start, p.y_range.end) - event._updating = true - p.trigger_event(event) - } - """ - - def set_callback(self, handle): - super().set_callback(handle) - if not bokeh3: - handle.js_on_event('rangesupdate', CustomJS(code=self._js_on_event)) - def _process_msg(self, msg): if self.plot.state.x_range is not self.plot.handles['x_range']: x_range = self.plot.handles['x_range'] diff --git a/holoviews/plotting/bokeh/element.py b/holoviews/plotting/bokeh/element.py index c1d20faa20..267ea20fa3 100644 --- a/holoviews/plotting/bokeh/element.py +++ b/holoviews/plotting/bokeh/element.py @@ -49,7 +49,7 @@ ) from .tabular import TablePlot from .util import ( - TOOL_TYPES, bokeh_version, bokeh3, bokeh32, date_to_integer, + TOOL_TYPES, bokeh_version, bokeh32, date_to_integer, decode_bytes, get_tab_title, glyph_order, py2js_tickformatter, recursive_model_update, theme_attr_json, cds_column_replace, hold_policy, match_dim_specs, compute_layout_properties, @@ -58,12 +58,8 @@ get_scale, get_axis_class ) -if bokeh3: - from bokeh.models.formatters import CustomJSTickFormatter - from bokeh.models.layouts import TabPanel -else: - from bokeh.models.formatters import FuncTickFormatter as CustomJSTickFormatter - from bokeh.models.layouts import Panel as TabPanel +from bokeh.models.formatters import CustomJSTickFormatter +from bokeh.models.layouts import TabPanel try: TOOLS_MAP = Tool._known_aliases @@ -683,10 +679,7 @@ def _init_plot(self, key, element, plots, ranges=None): properties.update(**self._plot_properties(key, element)) - if bokeh3: - figure = bokeh.plotting.figure - else: - figure = bokeh.plotting.Figure + figure = bokeh.plotting.figure with warnings.catch_warnings(): # Bokeh raises warnings about duplicate tools but these @@ -1719,18 +1712,13 @@ def _update_glyph(self, renderer, properties, mapping, glyph, source, data): server = self.renderer.mode == 'server' with hold_policy(self.document, 'collect', server=server): empty_data = {c: [] for c in columns} - if bokeh3: - event = ModelChangedEvent( - document=self.document, - model=source, - attr='data', - new=empty_data, - setter='empty' - ) - else: - event = ModelChangedEvent( - self.document, source, 'data', source.data, empty_data, empty_data, setter='empty' - ) + event = ModelChangedEvent( + document=self.document, + model=source, + attr='data', + new=empty_data, + setter='empty' + ) self.document.callbacks._held_events.append(event) if legend is not None: @@ -2563,7 +2551,7 @@ def _process_legend(self, plot=None): or not self.show_legend): legend.items[:] = [] else: - if bokeh3 and self.legend_cols: + if self.legend_cols: plot.legend.nrows = self.legend_cols else: plot.legend.orientation = 'horizontal' if self.legend_cols else 'vertical' @@ -2666,8 +2654,6 @@ def _process_legend(self, overlay): options[k] = v pos = self.legend_position - if not bokeh3: - options['orientation'] = 'horizontal' if self.legend_cols else 'vertical' if pos in ['top', 'bottom'] and not self.legend_cols: options['orientation'] = 'horizontal' @@ -2677,7 +2663,7 @@ def _process_legend(self, overlay): options.update(self._fontsize('legend', 'label_text_font_size')) options.update(self._fontsize('legend_title', 'title_text_font_size')) - if bokeh3 and self.legend_cols: + if self.legend_cols: options.update({"ncols": self.legend_cols}) legend.update(**options) diff --git a/holoviews/plotting/bokeh/graphs.py b/holoviews/plotting/bokeh/graphs.py index 4f6924b07e..7f591ef8e1 100644 --- a/holoviews/plotting/bokeh/graphs.py +++ b/holoviews/plotting/bokeh/graphs.py @@ -19,7 +19,6 @@ base_properties, line_properties, fill_properties, text_properties, rgba_tuple ) -from .util import bokeh3 class GraphPlot(GraphMixin, CompositeElementPlot, ColorbarPlot, LegendPlot): @@ -185,8 +184,6 @@ def get_data(self, element, ranges, style): index = nodes.astype(np.int32) layout = {k: (y, x) if self.invert_axes else (x, y) for k, (x, y) in zip(index, node_positions)} - if not bokeh3: - layout = {str(k): v for k, v in layout.items()} point_data = {'index': index} diff --git a/holoviews/plotting/bokeh/links.py b/holoviews/plotting/bokeh/links.py index 55850eeb08..154cfb3974 100644 --- a/holoviews/plotting/bokeh/links.py +++ b/holoviews/plotting/bokeh/links.py @@ -2,8 +2,8 @@ from bokeh.models import CustomJS from bokeh.models.tools import RangeTool +from bokeh.models import Toolbar -from .util import bokeh3 from ...core.util import isscalar from ..links import ( Link, RectanglesTableLink, DataLink, RangeToolLink, @@ -11,11 +11,6 @@ ) from ..plot import GenericElementPlot, GenericOverlayPlot -if bokeh3: - from bokeh.models import Toolbar -else: - from bokeh.models import ToolbarBox as Toolbar # Not completely correct - class LinkCallback: @@ -158,11 +153,8 @@ def __init__(self, root_model, link, source_plot, target_plot): tool = RangeTool(**axes) source_plot.state.add_tools(tool) - if bokeh3 and toolbars: + if toolbars: toolbars[0].tools.append(tool) - elif toolbars: - toolbar = toolbars[0].toolbar - toolbar.tools.append(tool) class DataLinkCallback(LinkCallback): @@ -206,8 +198,6 @@ def __init__(self, root_model, link, source_plot, target_plot): renderer.update(data_source=src_cds) else: renderer.update(source=src_cds) - if not bokeh3 and hasattr(renderer, 'view'): - renderer.view.update(source=src_cds) target_plot.handles['source'] = src_cds target_plot.handles['cds'] = src_cds for callback in target_plot.callbacks: diff --git a/holoviews/plotting/bokeh/plot.py b/holoviews/plotting/bokeh/plot.py index 20d780d695..ccc01aa4f5 100644 --- a/holoviews/plotting/bokeh/plot.py +++ b/holoviews/plotting/bokeh/plot.py @@ -30,14 +30,11 @@ from ..util import attach_streams, displayable, collate from .links import LinkCallback from .util import ( - bokeh3, filter_toolboxes, make_axis, sync_legends, update_shared_sources, empty_plot, + filter_toolboxes, make_axis, sync_legends, update_shared_sources, empty_plot, decode_bytes, theme_attr_json, cds_column_replace, get_default, merge_tools, select_legends ) -if bokeh3: - from bokeh.models.layouts import TabPanel -else: - from bokeh.models.layouts import Panel as TabPanel +from bokeh.models.layouts import TabPanel class BokehPlot(DimensionedPlot, CallbackPlot): @@ -271,10 +268,9 @@ def _get_title_div(self, key, default_fontsize='15pt', width=450): if 'title' in self.handles: title_div = self.handles['title'] - elif bokeh3: - title_div = Div(width=width, styles={"white-space": "nowrap"}) # so it won't wrap long titles easily else: - title_div = Div(width=width, style={"white-space": "nowrap"}) # so it won't wrap long titles easily + # so it won't wrap long titles easily + title_div = Div(width=width, styles={"white-space": "nowrap"}) title_div.text = title_tags return title_div @@ -310,8 +306,6 @@ def sync_sources(self): renderer.update(data_source=new_source) else: renderer.update(source=new_source) - if not bokeh3 and hasattr(renderer, 'view'): - renderer.view.update(source=new_source) plot.handles['source'] = plot.handles['cds'] = new_source plots.append(plot) shared_sources.append(new_source) @@ -592,7 +586,7 @@ def initialize_plot(self, ranges=None, plots=[]): if self.sync_legends: sync_legends(plot) plot = self._make_axes(plot) - if bokeh3 and hasattr(plot, "toolbar") and self.merge_tools: + if hasattr(plot, "toolbar") and self.merge_tools: plot.toolbar = merge_tools(plots) title = self._get_title_div(self.keys[-1]) @@ -645,7 +639,7 @@ def _make_axes(self, plot): x_axis.margin = (0, 0, 0, 50) r1, r2 = r1[::-1], r2[::-1] plot = gridplot([r1, r2], merge_tools=False) - if bokeh3 and self.merge_tools: + if self.merge_tools: plot.toolbar = merge_tools([r1, r2]) elif y_axis: models = [y_axis, plot] @@ -952,7 +946,7 @@ def initialize_plot(self, plots=None, ranges=None): merge_tools=False, toolbar_location=self.toolbar, sizing_mode=sizing_mode) - if bokeh3 and self.merge_tools: + if self.merge_tools: grid.toolbar = merge_tools(children) tab_plots.append((title, grid)) continue @@ -994,7 +988,7 @@ def initialize_plot(self, plots=None, ranges=None): ) if self.sync_legends: sync_legends(layout_plot) - if bokeh3 and self.merge_tools: + if self.merge_tools: layout_plot.toolbar = merge_tools(plot_grid) title = self._get_title_div(self.keys[-1]) diff --git a/holoviews/plotting/bokeh/raster.py b/holoviews/plotting/bokeh/raster.py index d6d1d66f8a..6eda70d400 100644 --- a/holoviews/plotting/bokeh/raster.py +++ b/holoviews/plotting/bokeh/raster.py @@ -12,7 +12,7 @@ from .element import ColorbarPlot, LegendPlot from .selection import BokehOverlaySelectionDisplay from .styles import base_properties, fill_properties, line_properties, mpl_to_bokeh -from .util import bokeh3, colormesh +from .util import colormesh class RasterPlot(ColorbarPlot): @@ -102,10 +102,6 @@ def get_data(self, element, ranges, style): l, b, r, t = b, l, t, r dh, dw = t-b, r-l - if self.invert_xaxis and not bokeh3: - l, r = r, l - if self.invert_yaxis and not bokeh3: - b, t = t, b data = dict(x=[l], y=[b], dw=[dw], dh=[dh]) for i, vdim in enumerate(element.vdims, 2): @@ -118,10 +114,6 @@ def get_data(self, element, ranges, style): img = np.array([[np.NaN]]) if self.invert_axes ^ (type(element) is Raster): img = img.T - if self.invert_xaxis and not bokeh3: - img = img[:, ::-1] - if self.invert_yaxis and not bokeh3: - img = img[::-1] key = 'image' if i == 2 else dimension_sanitizer(vdim.name) data[key] = [img] @@ -212,12 +204,6 @@ def get_data(self, element, ranges, style): l, b, r, t = b, l, t, r dh, dw = t-b, r-l - if self.invert_xaxis and not bokeh3: - l, r = r, l - img = img[:, ::-1] - if self.invert_yaxis and not bokeh3: - img = img[::-1] - b, t = t, b if 0 in img.shape: img = np.zeros((1, 1), dtype=np.uint32) diff --git a/holoviews/plotting/bokeh/util.py b/holoviews/plotting/bokeh/util.py index 3d5557c9aa..3bfa425921 100644 --- a/holoviews/plotting/bokeh/util.py +++ b/holoviews/plotting/bokeh/util.py @@ -30,6 +30,10 @@ from bokeh.models.widgets import DataTable, Div from bokeh.themes.theme import Theme from bokeh.themes import built_in_themes +from bokeh.layouts import group_tools +from bokeh.models.formatters import CustomJSTickFormatter +from bokeh.models import Toolbar, Tabs, GridPlot, SaveTool, CopyTool, ExamineTool, FullscreenTool, LayoutDOM +from bokeh.plotting import figure from packaging.version import Version from ...core.layout import Layout @@ -44,25 +48,10 @@ from ..util import dim_axis_label from ...util.warnings import deprecated + bokeh_version = Version(bokeh.__version__) -bokeh3 = bokeh_version >= Version("3.0") bokeh32 = bokeh_version >= Version("3.2") -if bokeh3: - from bokeh.layouts import group_tools - from bokeh.models.formatters import CustomJSTickFormatter - from bokeh.models import Toolbar, Tabs, GridPlot, SaveTool, CopyTool, ExamineTool, FullscreenTool, LayoutDOM - from bokeh.plotting import figure - class WidgetBox: pass # Does not exist in Bokeh 3 - -else: - from bokeh.layouts import WidgetBox - from bokeh.models.formatters import FuncTickFormatter as CustomJSTickFormatter - from bokeh.models.widgets import Tabs - from bokeh.models import ToolbarBox as Toolbar # Not completely correct - from bokeh.plotting import Figure as figure - class GridPlot: pass # Does not exist in Bokeh 2 - TOOL_TYPES = { 'pan': tools.PanTool, 'xpan': tools.PanTool, @@ -175,7 +164,7 @@ def compute_plot_size(plot): elif isinstance(plot, (Div, Toolbar)): # Cannot compute size for Div or Toolbar return 0, 0 - elif isinstance(plot, (Row, Column, Tabs, WidgetBox)): + elif isinstance(plot, (Row, Column, Tabs)): if not plot.children: return 0, 0 if isinstance(plot, Row) or (isinstance(plot, Toolbar) and plot.toolbar_location not in ['right', 'left']): w_agg, h_agg = (np.sum, np.max) @@ -419,14 +408,12 @@ def merge(tool, group): def sync_legends(bokeh_layout): """This syncs the legends of all plots in a grid based on their name. - Only works for Bokeh 3 and above. - Parameters ---------- bokeh_layout : bokeh.models.{GridPlot, Row, Column} Gridplot to sync legends of. """ - if not bokeh3 or len(bokeh_layout.children) < 2: + if len(bokeh_layout.children) < 2: return # Collect all glyph with names @@ -664,7 +651,7 @@ def pad_width(model, table_padding=0.85, tabs_padding=1.2): elif isinstance(model, DataTable): width = model.width model.width = int(table_padding*width) - elif isinstance(model, (WidgetBox, Div)): + elif isinstance(model, Div): width = model.width elif model: width = model.width @@ -687,8 +674,7 @@ def pad_plots(plots): row_widths.append(width) widths.append(row_widths) - layout = Column if bokeh3 else WidgetBox - plots = [[layout(p, width=w) if isinstance(p, (DataTable, Tabs)) else p + plots = [[Column(p, width=w) if isinstance(p, (DataTable, Tabs)) else p for p, w in zip(row, ws)] for row, ws in zip(plots, widths)] return plots @@ -1170,8 +1156,6 @@ def wrap_formatter(formatter, axis): def property_to_dict(x): """ Convert Bokeh's property Field and Value to a dictionary - - Was added in bokeh 3.0 """ try: @@ -1191,8 +1175,6 @@ def dtype_fix_hook(plot, element): # https://github.com/holoviz/holoviews/issues/5726 # Should be fixed in Bokeh 3.2 - if not bokeh3: - return try: renderers = plot.handles["plot"].renderers for renderer in renderers: diff --git a/holoviews/tests/plotting/bokeh/test_elementplot.py b/holoviews/tests/plotting/bokeh/test_elementplot.py index 4ca0e0c835..4577a99c72 100644 --- a/holoviews/tests/plotting/bokeh/test_elementplot.py +++ b/holoviews/tests/plotting/bokeh/test_elementplot.py @@ -10,7 +10,6 @@ from holoviews.element import Curve, Image, Scatter, Labels, HeatMap from holoviews.streams import Stream, PointDraw from holoviews.plotting.util import process_cmap -from holoviews.plotting.bokeh.util import bokeh3 from holoviews.util import render from .test_plot import TestBokehPlot, bokeh_renderer @@ -24,10 +23,7 @@ NumeralTickFormatter, LogTicker, LinearColorMapper, LogColorMapper, EqHistColorMapper) -if bokeh3: - from bokeh.models.formatters import CustomJSTickFormatter -else: - from bokeh.models.formatters import FuncTickFormatter as CustomJSTickFormatter +from bokeh.models.formatters import CustomJSTickFormatter class TestElementPlot(LoggingComparisonTestCase, TestBokehPlot): diff --git a/holoviews/tests/plotting/bokeh/test_graphplot.py b/holoviews/tests/plotting/bokeh/test_graphplot.py index ec540936bf..f0a47df345 100644 --- a/holoviews/tests/plotting/bokeh/test_graphplot.py +++ b/holoviews/tests/plotting/bokeh/test_graphplot.py @@ -3,7 +3,7 @@ from holoviews.core.data import Dataset from holoviews.element import Graph, Nodes, TriMesh, Chord, VLine, circular_layout from holoviews.util.transform import dim -from holoviews.plotting.bokeh.util import property_to_dict, bokeh3 +from holoviews.plotting.bokeh.util import property_to_dict from bokeh.models import (NodesAndLinkedEdges, EdgesAndLinkedNodes, NodesOnly, Patches) from bokeh.models.mappers import CategoricalColorMapper, LinearColorMapper @@ -36,10 +36,7 @@ def test_plot_simple_graph(self): self.assertEqual(node_source.data['index'], self.source) self.assertEqual(edge_source.data['start'], self.source) self.assertEqual(edge_source.data['end'], self.target) - if bokeh3: - layout = {z: (x, y) for x, y, z in self.graph.nodes.array()} - else: - layout = {str(z): (x, y) for x, y, z in self.graph.nodes.array()} + layout = {z: (x, y) for x, y, z in self.graph.nodes.array()} self.assertEqual(layout_source.graph_layout, layout) @@ -64,10 +61,7 @@ def test_plot_graph_with_paths(self): edges = graph.edgepaths.split() self.assertEqual(edge_source.data['xs'], [path.dimension_values(0) for path in edges]) self.assertEqual(edge_source.data['ys'], [path.dimension_values(1) for path in edges]) - if bokeh3: - layout = {z: (x, y) for x, y, z in self.graph.nodes.array()} - else: - layout = {str(z): (x, y) for x, y, z in self.graph.nodes.array()} + layout = {z: (x, y) for x, y, z in self.graph.nodes.array()} self.assertEqual(layout_source.graph_layout, layout) def test_graph_inspection_policy_nodes(self): @@ -322,10 +316,7 @@ def test_plot_simple_trimesh(self): self.assertEqual(node_source.data['index'], np.arange(4)) self.assertEqual(edge_source.data['start'], np.arange(2)) self.assertEqual(edge_source.data['end'], np.arange(1, 3)) - if bokeh3: - layout = {z: (x, y) for x, y, z in self.trimesh.nodes.array()} - else: - layout = {str(z): (x, y) for x, y, z in self.trimesh.nodes.array()} + layout = {z: (x, y) for x, y, z in self.trimesh.nodes.array()} self.assertEqual(layout_source.graph_layout, layout) def test_plot_simple_trimesh_filled(self): @@ -337,10 +328,7 @@ def test_plot_simple_trimesh_filled(self): self.assertEqual(node_source.data['index'], np.arange(4)) self.assertEqual(edge_source.data['start'], np.arange(2)) self.assertEqual(edge_source.data['end'], np.arange(1, 3)) - if bokeh3: - layout = {z: (x, y) for x, y, z in self.trimesh.nodes.array()} - else: - layout = {str(z): (x, y) for x, y, z in self.trimesh.nodes.array()} + layout = {z: (x, y) for x, y, z in self.trimesh.nodes.array()} self.assertEqual(layout_source.graph_layout, layout) def test_trimesh_edges_categorical_colormapped(self): diff --git a/holoviews/tests/plotting/bokeh/test_gridplot.py b/holoviews/tests/plotting/bokeh/test_gridplot.py index b8f44359b2..7b1f93451c 100644 --- a/holoviews/tests/plotting/bokeh/test_gridplot.py +++ b/holoviews/tests/plotting/bokeh/test_gridplot.py @@ -5,17 +5,13 @@ from holoviews.element import Curve, Image, Points from holoviews.operation import gridmatrix from holoviews.streams import Stream -from holoviews.plotting.bokeh.util import bokeh3 from .test_plot import TestBokehPlot, bokeh_renderer from bokeh.layouts import Column from bokeh.models import Div -if bokeh3: - from bokeh.models import Toolbar -else: - from bokeh.models import ToolbarBox as Toolbar # Not completely correct +from bokeh.models import Toolbar @@ -111,10 +107,7 @@ def test_grid_set_toolbar_location(self): grid = GridSpace({0: Curve([]), 1: Points([])}, 'X').opts(toolbar='left') plot = bokeh_renderer.get_plot(grid) self.assertIsInstance(plot.state, Column) - if bokeh3: - self.assertIsInstance(plot.state.children[0].toolbar, Toolbar) - else: - self.assertIsInstance(plot.state.children[0].children[0], Toolbar) + self.assertIsInstance(plot.state.children[0].toolbar, Toolbar) def test_grid_disable_toolbar(self): diff --git a/holoviews/tests/plotting/bokeh/test_layoutplot.py b/holoviews/tests/plotting/bokeh/test_layoutplot.py index 275c96417c..4f7df4979a 100644 --- a/holoviews/tests/plotting/bokeh/test_layoutplot.py +++ b/holoviews/tests/plotting/bokeh/test_layoutplot.py @@ -1,7 +1,6 @@ import datetime as dt import re -import pytest import numpy as np from holoviews.core import (HoloMap, GridSpace, Layout, Empty, Dataset, @@ -10,23 +9,16 @@ from holoviews.streams import Stream from holoviews.util import render, opts from holoviews.util.transform import dim -from holoviews.plotting.bokeh.util import bokeh3 -from bokeh.models import Div, GlyphRenderer, Tabs, Spacer, Title, Row, Column +from bokeh.models import Div, GlyphRenderer, Tabs, Spacer, Title from ...utils import LoggingComparisonTestCase from .test_plot import TestBokehPlot, bokeh_renderer -if bokeh3: - from bokeh.models.layouts import TabPanel - from bokeh.plotting import figure - from bokeh.models import GridPlot - from bokeh.models import Toolbar -else: - from bokeh.models.layouts import Panel as TabPanel - from bokeh.plotting import Figure as figure - from bokeh.models import ToolbarBox as Toolbar # Not completely correct - from bokeh.models import GridBox as GridPlot # Not completely correct +from bokeh.models.layouts import TabPanel +from bokeh.plotting import figure +from bokeh.models import GridPlot +from bokeh.models import Toolbar class TestLayoutPlot(LoggingComparisonTestCase, TestBokehPlot): @@ -172,8 +164,7 @@ def test_layout_title_update(self): 'font-weight:bold;font-size:12pt">Default: 1') self.assertEqual(title.text, text) - @pytest.mark.skipif(not bokeh3, reason="Only work for Bokeh 3") - def test_layout_gridspaces_bokeh3(self): + def test_layout_gridspaces(self): layout = (GridSpace({(i, j): Curve(range(i+j)) for i in range(1, 3) for j in range(2,4)}) + GridSpace({(i, j): Curve(range(i+j)) for i in range(1, 3) @@ -204,45 +195,6 @@ def test_layout_gridspaces_bokeh3(self): for gfig, *_ in grid.children: self.assertIsInstance(gfig, figure) - @pytest.mark.skipif(bokeh3, reason="Only work for Bokeh 2") - def test_layout_gridspaces_bokeh2(self): - layout = (GridSpace({(i, j): Curve(range(i+j)) for i in range(1, 3) - for j in range(2,4)}) + - GridSpace({(i, j): Curve(range(i+j)) for i in range(1, 3) - for j in range(2,4)}) + - Curve(range(10))).cols(2) - layout_plot = bokeh_renderer.get_plot(layout) - plot = layout_plot.state - - # Unpack until getting down to two rows - self.assertIsInstance(plot, Column) - self.assertEqual(len(plot.children), 2) - toolbar, grid = plot.children - self.assertIsInstance(toolbar, Toolbar) - self.assertIsInstance(grid, GridPlot) - self.assertEqual(len(grid.children), 3) - (col1, *_), (col2, *_), _ = grid.children - self.assertIsInstance(col1, Column) - self.assertIsInstance(col2, Column) - grid1 = col1.children[0] - grid2 = col2.children[0] - - # Check the row of GridSpaces - self.assertEqual(len(grid1.children), 3) - _, (col1, *_), _ = grid1.children - self.assertIsInstance(col1, Column) - inner_grid1 = col1.children[0] - - self.assertEqual(len(grid2.children), 3) - _, (col2, *_), _ = grid2.children - self.assertIsInstance(col2, Column) - inner_grid2 = col2.children[0] - for grid in [inner_grid1, inner_grid2]: - self.assertEqual(len(grid.children), 4) - for gfig, *_ in grid.children: - self.assertIsInstance(gfig, figure) - - def test_layout_instantiate_subplots(self): layout = (Curve(range(10)) + Curve(range(10)) + Image(np.random.rand(10,10)) + Curve(range(10)) + Curve(range(10))) @@ -262,10 +214,7 @@ def test_empty_adjoint_plot(self): plot = bokeh_renderer.get_plot(adjoint) adjoint_plot = plot.subplots[(0, 0)] self.assertEqual(len(adjoint_plot.subplots), 3) - if bokeh3: - grid = plot.state - else: - grid = plot.state.children[1] + grid = plot.state (f1, *_), (f2, *_), (s1, *_) = grid.children self.assertIsInstance(grid, GridPlot) self.assertIsInstance(s1, Spacer) @@ -285,11 +234,8 @@ def test_empty_adjoint_plot_with_renderer(self): def test_layout_plot_with_adjoints(self): layout = (Curve([]) + Curve([]).hist()).cols(1) plot = bokeh_renderer.get_plot(layout) - if bokeh3: - grid = plot.state - toolbar = grid.toolbar - else: - toolbar, grid = plot.state.children + grid = plot.state + toolbar = grid.toolbar self.assertIsInstance(toolbar, Toolbar) self.assertIsInstance(grid, GridPlot) for (fig, _, _) in grid.children: @@ -372,12 +318,8 @@ def test_layout_empty_subplots(self): def test_layout_set_toolbar_location(self): layout = (Curve([]) + Points([])).opts(toolbar='left') plot = bokeh_renderer.get_plot(layout) - if bokeh3: - self.assertIsInstance(plot.state, GridPlot) - self.assertIsInstance(plot.state.toolbar, Toolbar) - else: - self.assertIsInstance(plot.state, Row) - self.assertIsInstance(plot.state.children[0], Toolbar) + self.assertIsInstance(plot.state, GridPlot) + self.assertIsInstance(plot.state.toolbar, Toolbar) def test_layout_disable_toolbar(self): layout = (Curve([]) + Points([])).opts(toolbar=None) diff --git a/holoviews/tests/plotting/bokeh/test_plot.py b/holoviews/tests/plotting/bokeh/test_plot.py index aa0e04fa84..0b4a1d5b98 100644 --- a/holoviews/tests/plotting/bokeh/test_plot.py +++ b/holoviews/tests/plotting/bokeh/test_plot.py @@ -1,5 +1,4 @@ import numpy as np -import pytest import pyviz_comms as comms from param import concrete_descendents @@ -14,7 +13,6 @@ ) from holoviews.plotting.bokeh.callbacks import Callback from holoviews.plotting.bokeh.element import ElementPlot -from holoviews.plotting.bokeh.util import bokeh3 bokeh_renderer = Store.renderers['bokeh'] @@ -83,7 +81,6 @@ def _test_hover_info(self, element, tooltips, line_policy='nearest', formatters= self.assertTrue(any(renderer in h.renderers for h in hover)) -@pytest.mark.skipif(not bokeh3, reason="Bokeh>=3.0 required") def test_sync_two_plots(): curve = lambda i: Curve(np.arange(10) * i, label="ABC"[i]) plot1 = curve(0) * curve(1) @@ -103,7 +100,6 @@ def test_sync_two_plots(): assert v[0].code == "dst.muted = src.muted" -@pytest.mark.skipif(not bokeh3, reason="Bokeh>=3.0 required") def test_sync_three_plots(): curve = lambda i: Curve(np.arange(10) * i, label="ABC"[i]) plot1 = curve(0) * curve(1) diff --git a/holoviews/tests/plotting/bokeh/test_rasterplot.py b/holoviews/tests/plotting/bokeh/test_rasterplot.py index 041ea2ccd7..872a916838 100644 --- a/holoviews/tests/plotting/bokeh/test_rasterplot.py +++ b/holoviews/tests/plotting/bokeh/test_rasterplot.py @@ -3,7 +3,6 @@ import numpy as np from holoviews.element import Raster, Image, RGB, ImageStack -from holoviews.plotting.bokeh.util import bokeh3 from holoviews.plotting.bokeh.raster import ImageStackPlot from .test_plot import TestBokehPlot, bokeh_renderer @@ -59,18 +58,11 @@ def test_raster_invert_axes(self): plot = bokeh_renderer.get_plot(raster) source = plot.handles["source"] - if bokeh3: - np.testing.assert_equal(source.data["image"][0], arr.T) - assert source.data["x"][0] == 0 - assert source.data["y"][0] == 0 - assert source.data["dw"][0] == 2 - assert source.data["dh"][0] == 3 - else: - np.testing.assert_equal(source.data["image"][0], np.rot90(arr)) - assert source.data["x"][0] == 0 - assert source.data["y"][0] == 3 - assert source.data["dw"][0] == 2 - assert source.data["dh"][0] == 3 + np.testing.assert_equal(source.data["image"][0], arr.T) + assert source.data["x"][0] == 0 + assert source.data["y"][0] == 0 + assert source.data["dw"][0] == 2 + assert source.data["dh"][0] == 3 def test_image_invert_axes(self): arr = np.array([[0, 1, 2], [3, 4, 5]]) @@ -95,12 +87,8 @@ def test_image_invert_xaxis(self): assert cdata["dh"] == [1.0] assert cdata["dw"] == [1.0] - if bokeh3: - assert cdata["x"] == [-0.5] - np.testing.assert_equal(cdata["image"][0], arr[::-1]) - else: - assert cdata["x"] == [0.5] - np.testing.assert_equal(cdata["image"][0], arr[::-1, ::-1]) + assert cdata["x"] == [-0.5] + np.testing.assert_equal(cdata["image"][0], arr[::-1]) def test_image_invert_yaxis(self): arr = np.random.rand(10, 10) @@ -114,12 +102,8 @@ def test_image_invert_yaxis(self): assert cdata["dh"] == [1.0] assert cdata["dw"] == [1.0] - if bokeh3: - assert cdata["y"] == [-0.5] - np.testing.assert_equal(cdata["image"][0], arr[::-1]) - else: - assert cdata["y"] == [0.5] - np.testing.assert_equal(cdata["image"][0], arr) + assert cdata["y"] == [-0.5] + np.testing.assert_equal(cdata["image"][0], arr[::-1]) def test_rgb_invert_xaxis(self): rgb = RGB(np.random.rand(10, 10, 3)).opts(invert_xaxis=True) @@ -131,11 +115,7 @@ def test_rgb_invert_xaxis(self): assert cdata["y"] == [-0.5] assert cdata["dh"] == [1.0] assert cdata["dw"] == [1.0] - - if bokeh3: - assert cdata["x"] == [-0.5] - else: - assert cdata["x"] == [0.5] + assert cdata["x"] == [-0.5] def test_rgb_invert_yaxis(self): rgb = RGB(np.random.rand(10, 10, 3)).opts(invert_yaxis=True) @@ -147,11 +127,7 @@ def test_rgb_invert_yaxis(self): assert cdata["x"] == [-0.5] assert cdata["dh"] == [1.0] assert cdata["dw"] == [1.0] - - if bokeh3: - assert cdata["y"] == [-0.5] - else: - assert cdata["y"] == [0.5] + assert cdata["y"] == [-0.5] def test_image_stack_tuple(self): x = np.arange(0, 3) diff --git a/holoviews/tests/plotting/bokeh/test_renderer.py b/holoviews/tests/plotting/bokeh/test_renderer.py index ddd6bf3844..202b655e64 100644 --- a/holoviews/tests/plotting/bokeh/test_renderer.py +++ b/holoviews/tests/plotting/bokeh/test_renderer.py @@ -8,7 +8,6 @@ from holoviews.streams import Stream from holoviews.plotting import Renderer from holoviews.element.comparison import ComparisonTestCase -from holoviews.plotting.bokeh.util import bokeh3 from pyviz_comms import CommManager @@ -81,10 +80,7 @@ def test_get_size_tables_in_layout(self): self.assertEqual((w, h), (800, 300)) def test_theme_rendering(self): - if bokeh3: - attrs = {'figure': {'outline_line_color': '#444444'}} - else: - attrs = {'Figure': {'outline_line_color': '#444444'}} + attrs = {'figure': {'outline_line_color': '#444444'}} theme = Theme(json={'attrs' : attrs}) self.renderer.theme = theme plot = self.renderer.get_plot(Curve([])) diff --git a/setup.py b/setup.py index 846c9ec028..c4a72abb4c 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ "param >=1.12.0,<3.0", "numpy >=1.0", "pyviz_comms >=0.7.4", - "panel >=0.13.1", + "panel >=1.0", "colorcet", "packaging", "pandas >=0.20.0", @@ -35,7 +35,7 @@ 'flaky', 'matplotlib >=3, <3.8', # 3.8 breaks tests 'nbconvert', - 'bokeh', + 'bokeh >=3.1', 'pillow', 'plotly >=4.0', 'dash >=1.16', @@ -77,7 +77,7 @@ # IPython Notebook + pandas + matplotlib + bokeh extras_require["recommended"] = extras_require["notebook"] + [ "matplotlib >=3", - "bokeh >=2.4.3", + "bokeh >=3.1", ] # Requirements to run all examples @@ -116,16 +116,13 @@ 'mpl_sample_data >=3.1.3', 'pscript', 'graphviz', - 'bokeh >2.2', + 'bokeh >=3.1', 'pooch', 'selenium', ] extras_require['all'] = sorted(set(sum(extras_require.values(), []))) -extras_require['bokeh2'] = ["panel ==0.14.4", "param ==1.13.0"] # Hard-pin to not pull in rc releases -extras_require['bokeh3'] = ["panel >=1.0.0"] - extras_require["build"] = [ "param >=1.7.0", "setuptools >=30.3.0",