def _isolated_tk_test(success_count, func=None): """ A decorator to run *func* in a subprocess and assert that it prints "success" *success_count* times and nothing on stderr. TkAgg tests seem to have interactions between tests, so isolate each test in a subprocess. See GH#18261 """ if func is None: return functools.partial(_isolated_tk_test, success_count) if "MPL_TEST_ESCAPE_HATCH" in os.environ: # set in subprocess_run_helper() below return func @pytest.mark.skipif( not importlib.util.find_spec('tkinter'), reason="missing tkinter" ) @pytest.mark.skipif( sys.platform == "linux" and not _c_internal_utils.display_is_valid(), reason="$DISPLAY and $WAYLAND_DISPLAY are unset" ) @pytest.mark.xfail( # GitHub issue #23094 sys.platform == 'darwin', reason="Tk version mismatch on OSX CI" ) @functools.wraps(func) def test_func(): # even if the package exists, may not actually be importable this can # be the case on some CI systems. pytest.importorskip('tkinter') try: proc = subprocess_run_helper( func, timeout=_test_timeout, extra_env=dict( MPLBACKEND="TkAgg", MPL_TEST_ESCAPE_HATCH="1")) except subprocess.TimeoutExpired: pytest.fail("Subprocess timed out") except subprocess.CalledProcessError as e: pytest.fail("Subprocess failed to test intended behavior\n" + str(e.stderr)) else: # macOS may actually emit irrelevant errors about Accelerated # OpenGL vs. software OpenGL, so suppress them. # Asserting stderr first (and printing it on failure) should be # more helpful for debugging that printing a failed success count. assert not [line for line in proc.stderr.splitlines() if "OpenGL" not in line] assert proc.stdout.count("success") == success_count return test_func
def _get_testable_interactive_backends(): envs = [] for deps, env in [ *[([qt_api], { "MPLBACKEND": "qtagg", "QT_API": qt_api }) for qt_api in ["PyQt6", "PySide6", "PyQt5", "PySide2"]], *[([qt_api, "cairocffi"], { "MPLBACKEND": "qtcairo", "QT_API": qt_api }) for qt_api in ["PyQt6", "PySide6", "PyQt5", "PySide2"]], *[(["cairo", "gi"], { "MPLBACKEND": f"gtk{version}{renderer}" }) for version in [3, 4] for renderer in ["agg", "cairo"]], (["tkinter"], { "MPLBACKEND": "tkagg" }), (["wx"], { "MPLBACKEND": "wx" }), (["wx"], { "MPLBACKEND": "wxagg" }), (["matplotlib.backends._macosx"], { "MPLBACKEND": "macosx" }), ]: reason = None missing = [dep for dep in deps if not importlib.util.find_spec(dep)] if (sys.platform == "linux" and not _c_internal_utils.display_is_valid()): reason = "$DISPLAY and $WAYLAND_DISPLAY are unset" elif missing: reason = "{} cannot be imported".format(", ".join(missing)) elif env["MPLBACKEND"] == 'macosx' and os.environ.get('TF_BUILD'): reason = "macosx backend fails on Azure" elif env["MPLBACKEND"].startswith('gtk'): import gi version = env["MPLBACKEND"][3] repo = gi.Repository.get_default() if f'{version}.0' not in repo.enumerate_versions('Gtk'): reason = "no usable GTK bindings" marks = [] if reason: marks.append( pytest.mark.skip(reason=f"Skipping {env} because {reason}")) elif env["MPLBACKEND"].startswith('wx') and sys.platform == 'darwin': # ignore on OSX because that's currently broken (github #16849) marks.append(pytest.mark.xfail(reason='github #16849')) envs.append(pytest.param(env, marks=marks, id=str(env))) return envs
def _get_testable_interactive_backends(): try: from matplotlib.backends.qt_compat import QtGui # noqa have_qt5 = True except ImportError: have_qt5 = False backends = [] for deps, backend in [ (["cairo", "gi"], "gtk3agg"), (["cairo", "gi"], "gtk3cairo"), (["PyQt5"], "qt5agg"), (["PyQt5", "cairocffi"], "qt5cairo"), (["PySide2"], "qt5agg"), (["PySide2", "cairocffi"], "qt5cairo"), (["tkinter"], "tkagg"), (["wx"], "wx"), (["wx"], "wxagg"), (["matplotlib.backends._macosx"], "macosx"), ]: reason = None missing = [dep for dep in deps if not importlib.util.find_spec(dep)] if (sys.platform == "linux" and not _c_internal_utils.display_is_valid()): reason = "$DISPLAY and $WAYLAND_DISPLAY are unset" elif missing: reason = "{} cannot be imported".format(", ".join(missing)) elif backend == 'macosx' and os.environ.get('TF_BUILD'): reason = "macosx backend fails on Azure" elif 'qt5' in backend and not have_qt5: reason = "no usable Qt5 bindings" if reason: backend = pytest.param( backend, marks=pytest.mark.skip( reason=f"Skipping {backend} because {reason}")) elif backend.startswith('wx') and sys.platform == 'darwin': # ignore on OSX because that's currently broken (github #16849) backend = pytest.param( backend, marks=pytest.mark.xfail(reason='github #16849')) backends.append(backend) return backends
def _get_testable_qt_backends(): envs = [] for deps, env in [([qt_api], { "MPLBACKEND": "qtagg", "QT_API": qt_api }) for qt_api in ["PyQt6", "PySide6", "PyQt5", "PySide2"]]: reason = None missing = [dep for dep in deps if not importlib.util.find_spec(dep)] if (sys.platform == "linux" and not _c_internal_utils.display_is_valid()): reason = "$DISPLAY and $WAYLAND_DISPLAY are unset" elif missing: reason = "{} cannot be imported".format(", ".join(missing)) elif env["MPLBACKEND"] == 'macosx' and os.environ.get('TF_BUILD'): reason = "macosx backend fails on Azure" marks = [] if reason: marks.append( pytest.mark.skip(reason=f"Skipping {env} because {reason}")) envs.append(pytest.param(env, marks=marks, id=str(env))) return envs
"WAYLAND_DISPLAY": "", "MPLBACKEND": "", "MPLCONFIGDIR": str(tmpdir) } with pytest.raises(subprocess.CalledProcessError): subprocess.run([ sys.executable, "-c", "import matplotlib;" "matplotlib.use('tkagg');" "import matplotlib.pyplot" ], env=env, check=True, stderr=subprocess.DEVNULL) @pytest.mark.skipif(sys.platform == "linux" and not _c_internal_utils.display_is_valid(), reason="headless") def test_backend_fallback_headful(tmpdir): pytest.importorskip("tkinter") env = {**os.environ, "MPLBACKEND": "", "MPLCONFIGDIR": str(tmpdir)} backend = subprocess.check_output([ sys.executable, "-c", "import matplotlib.pyplot; print(matplotlib.get_backend())" ], env=env, universal_newlines=True) # The actual backend will depend on what's installed, but at least tkagg is # present. assert backend.strip().lower() != "agg"
env = {**os.environ, "DISPLAY": "", "WAYLAND_DISPLAY": "", "MPLBACKEND": "", "MPLCONFIGDIR": str(tmpdir)} with pytest.raises(subprocess.CalledProcessError): subprocess.run( [sys.executable, "-c", "import matplotlib;" "matplotlib.use('tkagg');" "import matplotlib.pyplot;" "matplotlib.pyplot.plot(42);" ], env=env, check=True, stderr=subprocess.DEVNULL) @pytest.mark.skipif( sys.platform == "linux" and not _c_internal_utils.display_is_valid(), reason="headless") def test_backend_fallback_headful(tmpdir): pytest.importorskip("tkinter") env = {**os.environ, "MPLBACKEND": "", "MPLCONFIGDIR": str(tmpdir)} backend = subprocess.check_output( [sys.executable, "-c", "import matplotlib as mpl; " "sentinel = mpl.rcsetup._auto_backend_sentinel; " # Check that access on another instance does not resolve the sentinel. "assert mpl.RcParams({'backend': sentinel})['backend'] == sentinel; " "assert dict.__getitem__(mpl.rcParams, 'backend') == sentinel; " "import matplotlib.pyplot; " "print(matplotlib.get_backend())"], env=env, universal_newlines=True) # The actual backend will depend on what's installed, but at least tkagg is