Skip to content

Commit

Permalink
Ensure proper cleanup of stream subscribers (#6389)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr authored Oct 14, 2024
1 parent 4286d42 commit 14849d5
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 8 deletions.
11 changes: 6 additions & 5 deletions holoviews/plotting/bokeh/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,12 @@ def cleanup(self):
if plot.subplots:
plot.subplots.clear()

if isinstance(plot, GenericElementPlot):
for callback in plot.callbacks:
streams += callback.streams
callback.cleanup()
if not isinstance(plot, (GenericElementPlot, GenericOverlayPlot)):
continue

for callback in plot.callbacks:
streams += callback.streams
callback.cleanup()

for stream in set(streams):
stream._subscribers = [
Expand All @@ -261,7 +263,6 @@ def cleanup(self):
get_method_owner(subscriber) not in plots
]


def _fontsize(self, key, label='fontsize', common=True):
"""
Converts integer fontsizes to a string specifying
Expand Down
2 changes: 1 addition & 1 deletion holoviews/plotting/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def cleanup(self):
"""
plots = self.traverse(lambda x: x, [Plot])
for plot in plots:
if not isinstance(plot, (GenericCompositePlot, GenericElementPlot, GenericOverlayPlot)):
if not isinstance(plot, (GenericElementPlot, GenericOverlayPlot)):
continue
for stream in set(plot.streams):
stream._subscribers = [
Expand Down
53 changes: 52 additions & 1 deletion holoviews/tests/plotting/bokeh/test_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
from holoviews import Curve
from holoviews.core.element import Element
from holoviews.core.options import Store
from holoviews.core.spaces import DynamicMap
from holoviews.element.comparison import ComparisonTestCase
from holoviews.plotting.bokeh.callbacks import Callback
from holoviews.plotting.bokeh.element import ElementPlot
from holoviews.streams import Pipe

bokeh_renderer = Store.renderers['bokeh']

Expand Down Expand Up @@ -82,11 +84,60 @@ def _test_hover_info(self, element, tooltips, line_policy='nearest', formatters=
self.assertIn(lookup[2:-1], cds.data)

# Ensure all the glyph renderers have a hover tool
print(renderers, hover)
for renderer in renderers:
self.assertTrue(any(renderer in h.renderers for h in hover))


def test_element_plot_stream_cleanup():
stream = Pipe()

dmap = DynamicMap(Curve, streams=[stream])

plot = bokeh_renderer.get_plot(dmap)

assert len(stream._subscribers) == 1

plot.cleanup()

assert not stream._subscribers


def test_overlay_plot_stream_cleanup():
stream1 = Pipe()
stream2 = Pipe()

dmap1 = DynamicMap(Curve, streams=[stream1])
dmap2 = DynamicMap(Curve, streams=[stream2])

plot = bokeh_renderer.get_plot(dmap1 * dmap2)

assert len(stream1._subscribers) == 4
assert len(stream2._subscribers) == 4

plot.cleanup()

assert not stream1._subscribers
assert not stream2._subscribers


def test_layout_plot_stream_cleanup():
stream1 = Pipe()
stream2 = Pipe()

dmap1 = DynamicMap(Curve, streams=[stream1])
dmap2 = DynamicMap(Curve, streams=[stream2])

plot = bokeh_renderer.get_plot(dmap1 + dmap2)

assert len(stream1._subscribers) == 2
assert len(stream2._subscribers) == 2

plot.cleanup()

assert not stream1._subscribers
assert not stream2._subscribers


def test_sync_two_plots():
curve = lambda i: Curve(np.arange(10) * i, label="ABC"[i])
plot1 = curve(0) * curve(1)
Expand Down
54 changes: 53 additions & 1 deletion holoviews/tests/plotting/plotly/test_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,68 @@
import pyviz_comms as comms
from param import concrete_descendents

from holoviews.core import Store
from holoviews.core import DynamicMap, Store
from holoviews.element import Curve
from holoviews.element.comparison import ComparisonTestCase
from holoviews.plotting.plotly.element import ElementPlot
from holoviews.plotting.plotly.util import figure_grid
from holoviews.streams import Pipe

from .. import option_intersections

plotly_renderer = Store.renderers['plotly']


def test_element_plot_stream_cleanup():
stream = Pipe()

dmap = DynamicMap(Curve, streams=[stream])

plot = plotly_renderer.get_plot(dmap)

assert len(stream._subscribers) == 1

plot.cleanup()

assert not stream._subscribers


def test_overlay_plot_stream_cleanup():
stream1 = Pipe()
stream2 = Pipe()

dmap1 = DynamicMap(Curve, streams=[stream1])
dmap2 = DynamicMap(Curve, streams=[stream2])

plot = plotly_renderer.get_plot(dmap1 * dmap2)

assert len(stream1._subscribers) == 4
assert len(stream2._subscribers) == 4

plot.cleanup()

assert not stream1._subscribers
assert not stream2._subscribers


def test_layout_plot_stream_cleanup():
stream1 = Pipe()
stream2 = Pipe()

dmap1 = DynamicMap(Curve, streams=[stream1])
dmap2 = DynamicMap(Curve, streams=[stream2])

plot = plotly_renderer.get_plot(dmap1 + dmap2)

assert len(stream1._subscribers) == 2
assert len(stream2._subscribers) == 2

plot.cleanup()

assert not stream1._subscribers
assert not stream2._subscribers


class TestPlotlyPlot(ComparisonTestCase):

def setUp(self):
Expand Down

0 comments on commit 14849d5

Please sign in to comment.