Dropping a patch warnings when panning in streamed plot

I am using version 3.7.3 of Bokeh.

I know that the “Dropping a patch” warnings are just warnings, but they show up in the terminal and I don’t want the customers of the script I wrote have to see those over and over again. Here is an MRE that shows what is happening. When I run this, and use the Pan tool, I get many of those warnings. For example, this warning.

WARNING:root:Dropping a patch because it contains a previously known reference (id=‘p1005’). Most of the time this is harmless and usually a result of updating a model on one side of a communications channel while it was being removed on the other end.

The line that causes the warnings is

self.plot_figure.x_range = Range1d(0, self._iteration)

What am I doing wrong?

Thanks in advance!


from bokeh.models import ColumnDataSource, Range1d
from bokeh.models.tools import PanTool
from bokeh.plotting import figure
from bokeh.server.server import Server
from bokeh.application.application import Application
from bokeh.application.handlers import FunctionHandler

_time_between_callbacks_in_ms = 200

class _RealTimeOptimizerPlot(object):
 
    def __init__(self, callback_period, doc):
 
        self.plot_figure = figure(
            tools=[PanTool(),],
            title="MRE for dropped patches when panning",
        )

        self._source_stream_dict = {
            'iteration': [],
            'y': [],
        }
        self._source = ColumnDataSource(self._source_stream_dict)

        self._iteration = 0
        self._y = 0

        self.plot_figure.line(
            x="iteration",
            y="y",
            source=self._source,
        )

        doc.add_periodic_callback(self._update, callback_period)
        doc.title = "MRE for dropped patches when panning"
        doc.add_root(self.plot_figure)


    def _update(self):
        self._source_stream_dict = {
            "iteration": [self._iteration],
            "y": [self._y],
            }
        self._source.stream(self._source_stream_dict)
        
        # the following line causes the "Dropping a patch" warnings when panning
        self.plot_figure.x_range = Range1d(0, self._iteration)
        
        self._iteration += 1.
        self._y += 0.25


def _make_realtime_plot_doc(doc):
    _RealTimeOptimizerPlot(
        _time_between_callbacks_in_ms,
        doc=doc,
    )

try:
    server = Server(
        {"/": Application(FunctionHandler(_make_realtime_plot_doc))},
        port=5123,
    )
    server.start()
    server.io_loop.add_callback(server.show, "/")
    server.io_loop.start()
except KeyboardInterrupt as e:
    print(
        f"Real-time optimization plot server stopped due to keyboard interrupt: {e}"
    )
except Exception as e:
    print(f"Error starting real-time optimization plot server: {e}")
finally:
    print("Stopping real-time optimization plot server")
    if "server" in globals():
        server.stop()

I guess the first thing I’d note is that this was just reduced from warning to debug in a recently merged PR:

Lower log level for "Dropping a patch" message by TheoMathurin · Pull Request #14617 · bokeh/bokeh · GitHub

But that won’t land until version 3.9 is released. So if you need an immediate solution the quickest thing is probably just to raise the Bokeh log level to error or critical.

There is probably also a way to use Python’s built in log filtering APIs to suppress this, but the details of that are outside my expertise.

All that said, is there a reason you do this:

self.plot_figure.x_range = Range1d(0, self._iteration)

instead of this:

self.plot_figure.x_range.start = 0
self.plot_figure.x_range.end = self._iteration

? The latter kind of update (i.e. changing properties on existing objects instead of replacing entire objects wholesale) is always regarded as best-practice when possible. Replacing entire objects is a “heavyweight” operation that involves unhooking and hooking back up all kinds of property event listeners.

Alternatively, could a DataRange1d be used here to avoid setting the range manually altogether? Perhaps by configuring its renderers property if auto-ranging should only take some subset of glyphs into account.

@Bryan thanks for the quick response! My MRE was a little too minimal. I am actually plotting two lines and that’s why I was using the Range1d. To make the MRE I took out the second line to simplify things but still show the behavior.

But your suggestion to use DataRange1d could be my solution! I didn’t even know about it.

I used it in small scripts and it works great. I need to try it in my full, really complicated script now.

Thanks again.

1 Like

I guess I am misunderstanding something, but I’ll admit I don’t see why the number of lines should have any bearing on updating an existing range in place by setting start and end on it (preferred) vs replacing the entire range object with a new range object (to be avoided).

@Bryan thank you ! I wasn’t really aware of the importance of using start and end compared to making a new Range1d. That makes sense! Using start and end fixed my problem completely.

Thanks for the great help.

1 Like