Skip to content

Avoid event accumulation in webagg backend. #27160

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
47 changes: 43 additions & 4 deletions lib/matplotlib/backends/backend_webagg_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@
self._current_image_mode = 'full'
# Track mouse events to fill in the x, y position of key events.
self._last_mouse_xy = (None, None)
# Keep track of the number of images that the frontend has confirmed
# to have received by sending an "ack" message.
# (init with None for backends that do not handle "ack" messages)
self._num_received_images = None
# Cache-dict to store events that occurred during image processing.
self._event_cache = dict()

def show(self):
# show the figure window
Expand Down Expand Up @@ -258,10 +264,35 @@
return png.getvalue()

def handle_event(self, event):
# Check if all pending images have been processed.
if self._num_received_images is not None:
pending_images = self.manager._num_sent_images > self._num_received_images

Check warning on line 269 in lib/matplotlib/backends/backend_webagg_core.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_webagg_core.py#L269

Added line #L269 was not covered by tests
if not pending_images: # Reset counters
self.manager._num_sent_images = 0
self._num_received_images = 0

Check warning on line 272 in lib/matplotlib/backends/backend_webagg_core.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_webagg_core.py#L271-L272

Added lines #L271 - L272 were not covered by tests
else:
# If no ack message was ever received, always process events.
pending_images = False

Check warning on line 275 in lib/matplotlib/backends/backend_webagg_core.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_webagg_core.py#L275

Added line #L275 was not covered by tests

# Handle events as follows:
# - Always process explicit "ack" and "draw" events.
# - Process all other events only if "ack count" equals "send count"
e_type = event['type']
handler = getattr(self, f'handle_{e_type}',
self.handle_unknown_event)
return handler(event)
if not pending_images or e_type in ["ack", "draw"]:
# Handle cached events of all types other than the current type.
self._event_cache.pop(e_type, None)

Check warning on line 283 in lib/matplotlib/backends/backend_webagg_core.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_webagg_core.py#L283

Added line #L283 was not covered by tests
for cache_event_type, cache_event in self._event_cache.items():
getattr(self, f'handle_{cache_event_type}',

Check warning on line 285 in lib/matplotlib/backends/backend_webagg_core.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_webagg_core.py#L285

Added line #L285 was not covered by tests
self.handle_unknown_event)(cache_event)
self._event_cache.clear()

Check warning on line 287 in lib/matplotlib/backends/backend_webagg_core.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_webagg_core.py#L287

Added line #L287 was not covered by tests

# Handle the current event.
handler = getattr(self, f'handle_{e_type}',

Check warning on line 290 in lib/matplotlib/backends/backend_webagg_core.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_webagg_core.py#L290

Added line #L290 was not covered by tests
self.handle_unknown_event)
return handler(event)

Check warning on line 292 in lib/matplotlib/backends/backend_webagg_core.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_webagg_core.py#L292

Added line #L292 was not covered by tests
else:
# Cache the last event of each type so we can process it later.
self._event_cache[event["type"]] = event

Check warning on line 295 in lib/matplotlib/backends/backend_webagg_core.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_webagg_core.py#L295

Added line #L295 was not covered by tests

def handle_unknown_event(self, event):
_log.warning('Unhandled message type %s. %s', event["type"], event)
Expand All @@ -273,7 +304,11 @@
# This could also be used as a simple sanity check in the
# future, but for now the performance increase is enough
# to justify it, even if the server does nothing with it.
pass

# Keep track of the number of received ack messages.
if self._num_received_images is None:
self._num_received_images = 0 # To support no "ack" messages.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't know what this comment means.

self._num_received_images += 1

Check warning on line 311 in lib/matplotlib/backends/backend_webagg_core.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/backends/backend_webagg_core.py#L310-L311

Added lines #L310 - L311 were not covered by tests

def handle_draw(self, event):
self.draw()
Expand Down Expand Up @@ -431,6 +466,8 @@
self.web_sockets = set()
super().__init__(canvas, num)

self._num_sent_images = 0

def show(self):
pass

Expand Down Expand Up @@ -470,6 +507,8 @@
for s in self.web_sockets:
s.send_binary(diff)

self._num_sent_images += 1 # Count number of sent images.

@classmethod
def get_javascript(cls, stream=None):
if stream is None:
Expand Down
Loading