From 6f94783075cbced341d2e312e7a86bedce3b1c0a Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 20 Oct 2021 23:42:06 -0400 Subject: [PATCH] Backport PR #20591: Webagg backend: get rid of tornado --- doc/devel/dependencies.rst | 2 +- lib/matplotlib/backends/backend_nbagg.py | 3 +- lib/matplotlib/backends/backend_webagg.py | 2 +- .../backends/backend_webagg_core.py | 45 +++++++++++++++++-- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/doc/devel/dependencies.rst b/doc/devel/dependencies.rst index 1007210b8ba9..5da29663ad7d 100644 --- a/doc/devel/dependencies.rst +++ b/doc/devel/dependencies.rst @@ -46,7 +46,7 @@ and the capabilities they provide. * wxPython_ (>= 4) [#]_: for the wx-based backends. * pycairo_ (>= 1.11.0) or cairocffi_ (>= 0.8): for the GTK and/or cairo-based backends. -* Tornado_: for the WebAgg backend. +* Tornado_ (>=5): for the WebAgg backend. .. _Tk: https://docs.python.org/3/library/tk.html .. _PyQt5: https://pypi.org/project/PyQt5/ diff --git a/lib/matplotlib/backends/backend_nbagg.py b/lib/matplotlib/backends/backend_nbagg.py index cd42c9ca0043..c9df2e8dbad7 100644 --- a/lib/matplotlib/backends/backend_nbagg.py +++ b/lib/matplotlib/backends/backend_nbagg.py @@ -22,7 +22,8 @@ from matplotlib.backend_bases import _Backend, NavigationToolbar2 from matplotlib.backends.backend_webagg_core import ( FigureCanvasWebAggCore, FigureManagerWebAgg, NavigationToolbar2WebAgg, - TimerTornado) + TimerTornado, TimerAsyncio +) def connection_info(): diff --git a/lib/matplotlib/backends/backend_webagg.py b/lib/matplotlib/backends/backend_webagg.py index 7f43187ac65f..bb18c993bde0 100644 --- a/lib/matplotlib/backends/backend_webagg.py +++ b/lib/matplotlib/backends/backend_webagg.py @@ -36,7 +36,7 @@ from matplotlib.backend_bases import _Backend from matplotlib._pylab_helpers import Gcf from . import backend_webagg_core as core -from .backend_webagg_core import TimerTornado +from .backend_webagg_core import TimerAsyncio, TimerTornado class ServerThread(threading.Thread): diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py index f75979defe97..06c6097bcacc 100644 --- a/lib/matplotlib/backends/backend_webagg_core.py +++ b/lib/matplotlib/backends/backend_webagg_core.py @@ -8,8 +8,9 @@ # way over a web socket. # # - `backend_webagg.py` contains a concrete implementation of a basic -# application, implemented with tornado. +# application, implemented with asyncio. +import asyncio import datetime from io import BytesIO, StringIO import json @@ -19,7 +20,6 @@ import numpy as np from PIL import Image -import tornado from matplotlib import _api, backend_bases, backend_tools from matplotlib.backends import backend_agg @@ -85,6 +85,8 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _timer_start(self): + import tornado + self._timer_stop() if self._single: ioloop = tornado.ioloop.IOLoop.instance() @@ -98,6 +100,8 @@ def _timer_start(self): self._timer.start() def _timer_stop(self): + import tornado + if self._timer is None: return elif self._single: @@ -114,8 +118,43 @@ def _timer_set_interval(self): self._timer_start() +class TimerAsyncio(backend_bases.TimerBase): + def __init__(self, *args, **kwargs): + self._task = None + super().__init__(*args, **kwargs) + + async def _timer_task(self, interval): + while True: + try: + await asyncio.sleep(interval) + self._on_timer() + + if self._single: + break + except asyncio.CancelledError: + break + + def _timer_start(self): + self._timer_stop() + + self._task = asyncio.ensure_future( + self._timer_task(max(self.interval / 1_000., 1e-6)) + ) + + def _timer_stop(self): + if self._task is not None: + self._task.cancel() + self._task = None + + def _timer_set_interval(self): + # Only stop and restart it if the timer has already been started + if self._task is not None: + self._timer_stop() + self._timer_start() + + class FigureCanvasWebAggCore(backend_agg.FigureCanvasAgg): - _timer_cls = TimerTornado + _timer_cls = TimerAsyncio # Webagg and friends having the right methods, but still # having bugs in practice. Do not advertise that it works until # we can debug this.