Skip to content

Commit 46614d3

Browse files
tacaswellMeeseeksDev[bot]
authored and
MeeseeksDev[bot]
committed
Backport PR #11896: Resolve backend in rcParams.__getitem__("backend").
1 parent 42f779e commit 46614d3

File tree

6 files changed

+49
-51
lines changed

6 files changed

+49
-51
lines changed

lib/matplotlib/__init__.py

+29-11
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@
138138

139139
# cbook must import matplotlib only within function
140140
# definitions, so it is safe to import from it here.
141-
from . import cbook
141+
from . import cbook, rcsetup
142142
from matplotlib.cbook import (
143143
MatplotlibDeprecationWarning, dedent, get_label, sanitize_sequence)
144144
from matplotlib.cbook import mplDeprecation # deprecated
@@ -853,6 +853,10 @@ def __setitem__(self, key, val):
853853
cbook.warn_deprecated(
854854
"3.0", "{} is deprecated; in the future, examples will be "
855855
"found relative to the 'datapath' directory.".format(key))
856+
elif key == 'backend':
857+
if val is rcsetup._auto_backend_sentinel:
858+
if 'backend' in self:
859+
return
856860
try:
857861
cval = self.validate[key](val)
858862
except ValueError as ve:
@@ -881,6 +885,12 @@ def __getitem__(self, key):
881885
"3.0", "{} is deprecated; in the future, examples will be "
882886
"found relative to the 'datapath' directory.".format(key))
883887

888+
elif key == "backend":
889+
val = dict.__getitem__(self, key)
890+
if val is rcsetup._auto_backend_sentinel:
891+
from matplotlib import pyplot as plt
892+
plt.switch_backend(rcsetup._auto_backend_sentinel)
893+
884894
return dict.__getitem__(self, key)
885895

886896
def __repr__(self):
@@ -1095,10 +1105,10 @@ def rc_params_from_file(fname, fail_on_error=False, use_default_template=True):
10951105
_fullpath = os.path.join(_basedir, rcParams['examples.directory'])
10961106
rcParams['examples.directory'] = _fullpath
10971107

1098-
rcParamsOrig = rcParams.copy()
10991108

11001109
with warnings.catch_warnings():
11011110
warnings.simplefilter("ignore", MatplotlibDeprecationWarning)
1111+
rcParamsOrig = RcParams(rcParams.copy())
11021112
rcParamsDefault = RcParams([(key, default) for key, (default, converter) in
11031113
defaultParams.items()
11041114
if key not in _all_deprecated])
@@ -1222,7 +1232,7 @@ def rc_file_defaults():
12221232
with warnings.catch_warnings():
12231233
warnings.simplefilter("ignore", mplDeprecation)
12241234
from .style.core import STYLE_BLACKLIST
1225-
rcParams.update({k: v for k, v in rcParamsOrig.items()
1235+
rcParams.update({k: rcParamsOrig[k] for k in rcParamsOrig
12261236
if k not in STYLE_BLACKLIST})
12271237

12281238

@@ -1238,7 +1248,8 @@ def rc_file(fname):
12381248
with warnings.catch_warnings():
12391249
warnings.simplefilter("ignore", mplDeprecation)
12401250
from .style.core import STYLE_BLACKLIST
1241-
rcParams.update({k: v for k, v in rc_params_from_file(fname).items()
1251+
rc_from_file = rc_params_from_file(fname)
1252+
rcParams.update({k: rc_from_file[k] for k in rc_from_file
12421253
if k not in STYLE_BLACKLIST})
12431254

12441255

@@ -1289,16 +1300,23 @@ def __init__(self, rc=None, fname=None):
12891300
if rc:
12901301
rcParams.update(rc)
12911302
except Exception:
1292-
# If anything goes wrong, revert to the original rcs.
1293-
dict.update(rcParams, self._orig)
1303+
self.__fallback()
12941304
raise
12951305

1306+
def __fallback(self):
1307+
# If anything goes wrong, revert to the original rcs.
1308+
updated_backend = self._orig['backend']
1309+
dict.update(rcParams, self._orig)
1310+
# except for the backend. If the context block triggered resloving
1311+
# the auto backend resolution keep that value around
1312+
if self._orig['backend'] is rcsetup._auto_backend_sentinel:
1313+
rcParams['backend'] = updated_backend
1314+
12961315
def __enter__(self):
12971316
return self
12981317

12991318
def __exit__(self, exc_type, exc_value, exc_tb):
1300-
# No need to revalidate the original values.
1301-
dict.update(rcParams, self._orig)
1319+
self.__fallback()
13021320

13031321

13041322
def use(arg, warn=True, force=False):
@@ -1324,14 +1342,14 @@ def use(arg, warn=True, force=False):
13241342
13251343
force : bool, optional
13261344
If True, attempt to switch the backend. This defaults to
1327-
false and using `.pyplot.switch_backend` is preferred.
1345+
False.
13281346
13291347
13301348
"""
13311349
name = validate_backend(arg)
13321350

13331351
# if setting back to the same thing, do nothing
1334-
if (rcParams['backend'] == name):
1352+
if (dict.__getitem__(rcParams, 'backend') == name):
13351353
pass
13361354

13371355
# Check if we have already imported pyplot and triggered
@@ -1361,7 +1379,7 @@ def use(arg, warn=True, force=False):
13611379

13621380

13631381
if os.environ.get('MPLBACKEND'):
1364-
use(os.environ['MPLBACKEND'])
1382+
rcParams['backend'] = os.environ.get('MPLBACKEND')
13651383

13661384

13671385
def get_backend():

lib/matplotlib/backends/__init__.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,9 @@
1010

1111
_log = logging.getLogger(__name__)
1212

13-
backend = matplotlib.get_backend()
14-
# FIXME: Remove.
15-
_backend_loading_tb = "".join(
16-
line for line in traceback.format_stack()
17-
# Filter out line noise from importlib line.
18-
if not line.startswith(' File "<frozen importlib._bootstrap'))
13+
14+
# NOTE: plt.switch_backend() (called at import time) will add a "backend"
15+
# attribute here for backcompat.
1916

2017

2118
def _get_running_interactive_framework():

lib/matplotlib/pyplot.py

+12-31
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
from matplotlib.backend_bases import FigureCanvasBase
4040
from matplotlib.figure import Figure, figaspect
4141
from matplotlib.gridspec import GridSpec
42-
from matplotlib import rcParams, rcParamsDefault, get_backend
42+
from matplotlib import rcParams, rcParamsDefault, get_backend, rcParamsOrig
4343
from matplotlib import rc_context
4444
from matplotlib.rcsetup import interactive_bk as _interactive_bk
4545
from matplotlib.artist import getp, get, Artist
@@ -67,7 +67,7 @@
6767
Locator, IndexLocator, FixedLocator, NullLocator,\
6868
LinearLocator, LogLocator, AutoLocator, MultipleLocator,\
6969
MaxNLocator
70-
from matplotlib.backends import pylab_setup
70+
from matplotlib.backends import pylab_setup, _get_running_interactive_framework
7171

7272
_log = logging.getLogger(__name__)
7373

@@ -78,35 +78,15 @@
7878
# FIXME: Deprecate.
7979
def _backend_selection():
8080
"""
81-
If rcParams['backend_fallback'] is true, check to see if the
82-
current backend is compatible with the current running event loop,
83-
and if not switches to a compatible one.
84-
"""
85-
backend = rcParams['backend']
86-
if not rcParams['backend_fallback'] or backend not in _interactive_bk:
87-
return
88-
is_agg_backend = rcParams['backend'].endswith('Agg')
89-
if 'wx' in sys.modules and backend not in ('WX', 'WXAgg'):
90-
import wx
91-
if wx.App.IsMainLoopRunning():
92-
rcParams['backend'] = 'wx' + 'Agg' * is_agg_backend
93-
elif 'PyQt4.QtCore' in sys.modules and not backend == 'Qt4Agg':
94-
import PyQt4.QtGui
95-
if not PyQt4.QtGui.qApp.startingUp():
96-
# The mainloop is running.
97-
rcParams['backend'] = 'qt4Agg'
98-
elif 'PyQt5.QtCore' in sys.modules and not backend == 'Qt5Agg':
99-
import PyQt5.QtWidgets
100-
if not PyQt5.QtWidgets.qApp.startingUp():
101-
# The mainloop is running.
102-
rcParams['backend'] = 'qt5Agg'
103-
elif 'gtk' in sys.modules and 'gi' in sys.modules:
104-
from gi.repository import GLib
105-
if GLib.MainLoop().is_running():
106-
rcParams['backend'] = 'GTK3Agg'
107-
elif 'Tkinter' in sys.modules and not backend == 'TkAgg':
108-
# import Tkinter
109-
pass # what if anything do we need to do for tkinter?
81+
If rcParams['backend_fallback'] is true, we will check (at backend
82+
load-time) to see if the current backend is compatible with the current
83+
running event loop, and if not switches to a compatible one.
84+
"""
85+
if rcParams["backend_fallback"]:
86+
if (dict.__getitem__(rcParams, "backend") in _interactive_bk
87+
and _get_running_interactive_framework()):
88+
dict.__setitem__(
89+
rcParams, "backend", rcsetup._auto_backend_sentinel)
11090

11191

11292
_backend_selection()
@@ -237,6 +217,7 @@ def switch_backend(newbackend):
237217
except ImportError:
238218
continue
239219
else:
220+
rcParamsOrig['backend'] = candidate
240221
return
241222

242223
backend_name = (

lib/matplotlib/testing/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def setup():
3636
"Could not set locale to English/United States. "
3737
"Some date-related tests may fail.")
3838

39-
mpl.use('Agg', warn=False) # use Agg backend for these tests
39+
mpl.use('Agg', force=True, warn=False) # use Agg backend for these tests
4040

4141
with warnings.catch_warnings():
4242
warnings.simplefilter("ignore", MatplotlibDeprecationWarning)

lib/matplotlib/testing/conftest.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010
def pytest_configure(config):
11-
matplotlib.use('agg')
11+
matplotlib.use('agg', force=True)
1212
matplotlib._called_from_pytest = True
1313
matplotlib._init_tests()
1414

@@ -53,6 +53,8 @@ def mpl_test_settings(request):
5353
if backend is not None:
5454
plt.switch_backend(prev_backend)
5555

56+
assert matplotlib.get_backend() == 'agg'
57+
5658

5759
@pytest.fixture
5860
def mpl_image_comparison_parameters(request, extension):

lib/matplotlib/tests/test_backend_svg.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def test_determinism(filename, usetex):
139139
[sys.executable, '-R', '-c',
140140
'import matplotlib; '
141141
'matplotlib._called_from_pytest = True; '
142-
'matplotlib.use("svg"); '
142+
'matplotlib.use("svg", force=True); '
143143
'from matplotlib.tests.test_backend_svg '
144144
'import _test_determinism_save;'
145145
'_test_determinism_save(%r, %r)' % (filename, usetex)],

0 commit comments

Comments
 (0)