From b94f5c05624e8a413f1cb97808abe77194b25758 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 25 Jun 2020 23:11:07 -0400 Subject: [PATCH] FIX: be more careful about not importing pyplot early In matplotlib.use we import pyplot to use `switch_backend`, however if the user calls `mpl.use(...)` before importing pyplot then during the initial import of pyplot, before we set the selected backend we try to set the backend set via rcParams. This change only imports pyplot if it is already imported, otherwise it is safe to just set the rcParams and not go through the full `plt.switch_backend` path. closes #17763 --- lib/matplotlib/__init__.py | 30 +++++++++++++++++++++------ lib/matplotlib/tests/test_rcparams.py | 5 ++++- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index e19ceabf9b0f..c425fe818f58 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1136,16 +1136,34 @@ def use(backend, *, force=True): matplotlib.get_backend """ name = validate_backend(backend) + # we need to use the base-class method here to avoid (prematurely) + # resolving the "auto" backend setting if dict.__getitem__(rcParams, 'backend') == name: # Nothing to do if the requested backend is already set pass else: - try: - from matplotlib import pyplot as plt - plt.switch_backend(name) - except ImportError: - if force: - raise + # if pyplot is not already imported, do not import it. Doing + # so may trigger a `plt.switch_backend` to the _default_ backend + # before we get a chance to change to the one the user just requested + plt = sys.modules.get('matplotlib.pyplot') + # if pyplot is imported, then try to change backends + if plt is not None: + try: + # we need this import check here to re-raise if the + # user does not have the libraries to support their + # chosen backend installed. + plt.switch_backend(name) + except ImportError: + if force: + raise + # if we have not imported pyplot, then we can set the rcParam + # value which will be respected when the user finally imports + # pyplot + else: + rcParams['backend'] = backend + # if the user has asked for a given backend, do not helpfully + # fallback + rcParams['backend_fallback'] = False if os.environ.get('MPLBACKEND'): diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index 424296374015..b34577a3ee93 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -473,7 +473,10 @@ def test_backend_fallback_headless(tmpdir): with pytest.raises(subprocess.CalledProcessError): subprocess.run( [sys.executable, "-c", - "import matplotlib; matplotlib.use('tkagg')"], + ("import matplotlib;" + + "matplotlib.use('tkagg');" + + "import matplotlib.pyplot") + ], env=env, check=True)