Repository navigation
[Bug]: TimerAsyncio does not work with Python 3.14 #30917
Copy link
Copy link
Closed
Labels
Release criticalFor bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
Milestone
Description
Bug summary
The test_backends_interactive.py::test_webagg test is failing on Python 3.14, because the implementation of TimerAsyncio uses asyncio.ensure_future when there is no current event loop. asyncio.ensure_future calls asyncio.get_event_loop() when a loop is not given, and that raises a RuntimeError if there is no current event loop.
Code for reproduction
Run the matplotlib testsuite on Python 3.14
Actual outcome
=================================== FAILURES ===================================
_________________________________ test_webagg __________________________________
[gw28] linux -- Python 3.14.2 /build/python-matplotlib/src/matplotlib/test-env/bin/python
@pytest.mark.skipif('TF_BUILD' in os.environ,
reason="this test fails an azure for unknown reasons")
@pytest.mark.skipif(sys.platform == "win32", reason="Cannot send SIGINT on Windows.")
def test_webagg():
pytest.importorskip("tornado")
proc = subprocess.Popen(
[sys.executable, "-c",
inspect.getsource(_test_interactive_impl)
+ "\n_test_interactive_impl()", "{}"],
env={**os.environ, "MPLBACKEND": "webagg", "SOURCE_DATE_EPOCH": "0"})
url = f'http://{mpl.rcParams["webagg.address"]}:{mpl.rcParams["webagg.port"]}'
timeout = time.perf_counter() + _test_timeout
try:
while True:
try:
retcode = proc.poll()
# check that the subprocess for the server is not dead
> assert retcode is None
E assert 1 is None
test-env/lib/python3.14/site-packages/matplotlib/tests/test_backends_interactive.py:493: AssertionError
----------------------------- Captured stderr call -----------------------------
Traceback (most recent call last):
File "<string>", line 83, in <module>
_test_interactive_impl()
~~~~~~~~~~~~~~~~~~~~~~^^
File "<string>", line 69, in _test_interactive_impl
fig.savefig(result, format='png')
~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/figure.py", line 3490, in savefig
self.canvas.print_figure(fname, **kwargs)
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/backend_bases.py", line 2186, in print_figure
result = print_method(
filename,
...<3 lines>...
bbox_inches_restore=_bbox_inches_restore,
**kwargs)
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/backend_bases.py", line 2042, in <lambda>
print_method = functools.wraps(meth)(lambda *args, **kwargs: meth(
~~~~^
*args, **{k: v for k, v in kwargs.items() if k not in skip}))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/backends/backend_agg.py", line 481, in print_png
self._print_pil(filename_or_obj, "png", pil_kwargs, metadata)
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/backends/backend_agg.py", line 429, in _print_pil
FigureCanvasAgg.draw(self)
~~~~~~~~~~~~~~~~~~~~^^^^^^
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/backends/backend_agg.py", line 382, in draw
self.figure.draw(self.renderer)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/artist.py", line 94, in draw_wrapper
result = draw(artist, renderer, *args, **kwargs)
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/artist.py", line 71, in draw_wrapper
return draw(artist, renderer)
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/figure.py", line 3264, in draw
DrawEvent("draw_event", self.canvas, renderer)._process()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/backend_bases.py", line 1189, in _process
self.canvas.callbacks.process(self.name, self)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/cbook.py", line 366, in process
self.exception_handler(exc)
~~~~~~~~~~~~~~~~~~~~~~^^^^^
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/cbook.py", line 110, in _exception_printer
raise exc
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/cbook.py", line 361, in process
func(*args, **kwargs)
~~~~^^^^^^^^^^^^^^^^^
File "<string>", line 65, in <lambda>
fig.canvas.mpl_connect("draw_event", lambda event: timer.start())
~~~~~~~~~~~^^
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/_api/deprecation.py", line 382, in wrapper
return func(*inner_args, **inner_kwargs)
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/backend_bases.py", line 1069, in start
self._timer_start()
~~~~~~~~~~~~~~~~~^^
File "/build/python-matplotlib/src/matplotlib/test-env/lib/python3.14/site-packages/matplotlib/backends/backend_webagg_core.py", line 139, in _timer_start
self._task = asyncio.ensure_future(
~~~~~~~~~~~~~~~~~~~~~^
self._timer_task(max(self.interval / 1_000., 1e-6))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/usr/lib/python3.14/asyncio/tasks.py", line 730, in ensure_future
loop = events.get_event_loop()
File "/usr/lib/python3.14/asyncio/events.py", line 715, in get_event_loop
raise RuntimeError('There is no current event loop in thread %r.'
% threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'MainThread'.
<sys>:0: RuntimeWarning: coroutine 'TimerAsyncio._timer_task' was never awaited
=========================== short test summary info ============================
FAILED test-env/lib/python3.14/site-packages/matplotlib/tests/test_backends_interactive.py::test_webagg - assert 1 is None
Expected outcome
No test failure
Additional information
No response
Operating system
Arch
Matplotlib Version
3.10.8
Matplotlib Backend
No response
Python version
3.14.2
Jupyter version
No response
Installation
None
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Release criticalFor bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
Activity
lahwaacz commentedon Dec 31, 2025
The code which fails:
matplotlib/lib/matplotlib/backends/backend_webagg_core.py
Lines 139 to 141 in 776b249
tacaswell commentedon Dec 31, 2025
Ah, we don't have a 3.14 CI job yet.
TST: account for asyncio changes in py314
tacaswell commentedon Dec 31, 2025
The fix here is to create an event loop in the test code. I don't think we want to restore the behavior upstream removed (implicitly creating an EventLoop) which will likely cause some down-stream breakage but the fix is for user to track the upstream changes.