Description
Bug report
Bug summary
Calling matplotlib.use('tkagg')
doesn't work in headless mode. That's fine and to be expected.
What is bizarre is that this only results in an error when this is called after matplotlib.pyplot
is imported. This results in a surprising and challenge to debug situation if matplotlib.use('tkagg')
is called multiple times (e.g., in separate files).
Code for reproduction
Here's a script that I'm running with matplotlib 3.0.2:
import os
# pretend to be in "headless" mode
os.environ.pop("DISPLAY", None)
# first module
import matplotlib
matplotlib.use('TkAgg') # silently fails?
import matplotlib.pyplot
# in another imported module
import matplotlib
matplotlib.use('TkAgg') # this is when matplotlib crashes!
import matplotlib.pyplot
Actual outcome
Traceback (most recent call last):
File "/usr/local/google/home/shoyer/Downloads/use_tkagg_test.py", line 13, in <module>
matplotlib.use('TkAgg') # this is when matplotlib crashes!
File "/usr/local/google/home/shoyer/miniconda3/envs/xarray-py37/lib/python3.7/site-packages/matplotlib/__init__.py", line 1391, in use
switch_backend(name)
File "/usr/local/google/home/shoyer/miniconda3/envs/xarray-py37/lib/python3.7/site-packages/matplotlib/pyplot.py", line 222, in switch_backend
newbackend, required_framework, current_framework))
ImportError: Cannot load backend 'TkAgg' which requires the 'tk' interactive framework, as 'headless' is currently running
Expected outcome
It would be nice if matplotlib did any of these:
- Gave a clear error message when
matplotlib.use()
was called the first time. - Gave a clear error message when
matplotlib.pyplot
is imported and an invalid backend is selected. - Silently allowed setting an invalid backend with
matplotlib.use()
even after pyplot is imported.
Matplotlib version
- Operating system:
- Matplotlib version: 3.0.2
- Matplotlib backend (
print(matplotlib.get_backend())
): NA - Python version: 3.7
- Jupyter version (if applicable):
- Other libraries:
As far as I can tell, the source of the problem is that matplotlib only actually validates backends if matplotlib.pyplot
is imported:
https://github.com/matplotlib/matplotlib/blob/d9030efd40fe4300e775db22ba55845399f6668f/lib/matplotlib/__init__.py#L1281=L1283
It seems like one good way to avoid this would be to use a consistent codepath, whether or not matplotlib.pyplot
has already been imported, e.g., by always calling matplotlib.pyplot.switch_backend()
. Or maybe matplotlib.use()
could do some basic validation, e.g., to verify that the backend is a valid choice.