Skip to content

Make sure that initial state gets reset if anything goes wrong in rc_context [backport to 1.4.x] #3752

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 3, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions lib/matplotlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1249,10 +1249,16 @@ def __init__(self, rc=None, fname=None):
self.rcdict = rc
self.fname = fname
self._rcparams = rcParams.copy()
if self.fname:
rc_file(self.fname)
if self.rcdict:
rcParams.update(self.rcdict)
try:
if self.fname:
rc_file(self.fname)
if self.rcdict:
rcParams.update(self.rcdict)
except:
# if anything goes wrong, revert rc parameters and re-raise
rcParams.clear()
rcParams.update(self._rcparams)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically speaking, wouldn't this still leave new entries in the dictionary? Of course, this might not matter if the set of entries is always the same. Still I would have hoped that there would be a context manager for something like this already. If there isn't, it feels like a hole in the standard library.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WeatherGod - ah yes, good catch, I've updated this to add a call to clear before the update, which I think will do the right thing.

raise

def __enter__(self):
return self
Expand Down
23 changes: 23 additions & 0 deletions lib/matplotlib/tests/test_rcparams.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from matplotlib.tests import assert_str_equal
from matplotlib.testing.decorators import cleanup, knownfailureif
from nose.tools import assert_true, assert_raises, assert_equal
from nose.plugins.skip import SkipTest
import nose
from itertools import chain
import numpy as np
Expand Down Expand Up @@ -262,3 +263,25 @@ def test_keymaps():
key_list = [k for k in mpl.rcParams if 'keymap' in k]
for k in key_list:
assert(isinstance(mpl.rcParams[k], list))


def test_rcparams_reset_after_fail():

# There was previously a bug that meant that if rc_context failed and
# raised an exception due to issues in the supplied rc parameters, the
# global rc parameters were left in a modified state.

if sys.version_info[:2] >= (2, 7):
from collections import OrderedDict
else:
raise SkipTest("Test can only be run in Python >= 2.7 as it requires OrderedDict")

with mpl.rc_context(rc={'text.usetex': False}):

assert mpl.rcParams['text.usetex'] is False

with assert_raises(KeyError):
with mpl.rc_context(rc=OrderedDict([('text.usetex', True),('test.blah', True)])):
pass

assert mpl.rcParams['text.usetex'] is False