From 448779ac5df3255c8e292caf7001b30633f68a1e Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 13 Aug 2020 16:18:01 -0400 Subject: [PATCH] MNT: do a better job guessing the GUI framework in use We currently check the GUI framework and reject changing to a different framework. This adds the ability to check the running framework before we start guessing frameworks to use. --- lib/matplotlib/pyplot.py | 18 +++++++++- .../tests/test_backends_interactive.py | 34 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 70389be97e39..c1fde0dcb023 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -232,10 +232,26 @@ def switch_backend(newbackend): close("all") if newbackend is rcsetup._auto_backend_sentinel: + current_framework = cbook._get_running_interactive_framework() + mapping = {'qt5': 'qt5agg', + 'qt4': 'qt4agg', + 'gtk3': 'gtk3agg', + 'wx': 'wxagg', + 'tk': 'tkagg', + 'macosx': 'macosx', + 'headless': 'agg'} + + best_guess = mapping.get(current_framework, None) + if best_guess is not None: + candidates = [best_guess] + else: + candidates = [] + candidates += ["macosx", "qt5agg", "gtk3agg", "tkagg", "wxagg"] + # Don't try to fallback on the cairo-based backends as they each have # an additional dependency (pycairo) over the agg-based backend, and # are of worse quality. - for candidate in ["macosx", "qt5agg", "gtk3agg", "tkagg", "wxagg"]: + for candidate in candidates: try: switch_backend(candidate) except ImportError: diff --git a/lib/matplotlib/tests/test_backends_interactive.py b/lib/matplotlib/tests/test_backends_interactive.py index fe5885e0ac9c..4494efa35437 100644 --- a/lib/matplotlib/tests/test_backends_interactive.py +++ b/lib/matplotlib/tests/test_backends_interactive.py @@ -228,3 +228,37 @@ def test_never_update(monkeypatch, capsys): # test framework doesn't see tkinter callback exceptions normally # see tkinter.Misc.report_callback_exception assert "Exception in Tkinter callback" not in capsys.readouterr().err + + +@pytest.mark.skipif(sys.platform != "linux", reason="this a linux-only test") +@pytest.mark.backend('Qt5Agg', skip_on_importerror=True) +def test_lazy_linux_headless(): + test_script = """ +import os +import sys + +# make it look headless +del os.environ['DISPLAY'] + +# we should fast-track to Agg +import matplotlib.pyplot as plt +plt.get_backend() == 'agg' +assert 'PyQt5' not in sys.modules + +# make sure we really have pyqt installed +import PyQt5 +assert 'PyQt5' in sys.modules + +# try to switch and make sure we fail with ImportError +try: + plt.switch_backend('qt5agg') +except ImportError: + ... +else: + sys.exit(1) + +""" + proc = subprocess.run([sys.executable, "-c", test_script]) + if proc.returncode: + pytest.fail("The subprocess returned with non-zero exit status " + f"{proc.returncode}.")