Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 04e382f

Browse files
committed
Fix xkcd style garbage collection.
1 parent 6af4e4f commit 04e382f

File tree

3 files changed

+23
-27
lines changed

3 files changed

+23
-27
lines changed

lib/matplotlib/__init__.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,8 +1246,7 @@ def rc_file(fname):
12461246
rcParams.update(rc_params_from_file(fname))
12471247

12481248

1249-
@contextlib.contextmanager
1250-
def rc_context(rc=None, fname=None):
1249+
class rc_context:
12511250
"""
12521251
Return a context manager for managing rc settings.
12531252
@@ -1277,19 +1276,28 @@ def rc_context(rc=None, fname=None):
12771276
ax.plot(range(3), range(3))
12781277
fig.savefig('A.png', format='png')
12791278
plt.close(fig)
1280-
12811279
"""
1282-
1283-
orig = rcParams.copy()
1284-
try:
1280+
# While it may seem natural to implement rc_context using
1281+
# contextlib.contextmanager, that would entail always calling the finally:
1282+
# clause of the contextmanager (which restores the original rcs) including
1283+
# during garbage collection; as a result, something like `plt.xkcd();
1284+
# gc.collect()` would result in the style being lost (as `xkcd()` is
1285+
# implemented on top of rc_context, and nothing is holding onto context
1286+
# manager except possibly circular references.
1287+
1288+
def __init__(self, rc=None, fname=None):
1289+
self._orig = rcParams.copy()
12851290
if fname:
12861291
rc_file(fname)
12871292
if rc:
12881293
rcParams.update(rc)
1289-
yield
1290-
finally:
1294+
1295+
def __enter__(self):
1296+
return self
1297+
1298+
def __exit__(self, exc_type, exc_value, exc_tb):
12911299
# No need to revalidate the original values.
1292-
dict.update(rcParams, orig)
1300+
dict.update(rcParams, self._orig)
12931301

12941302

12951303
_use_error_msg = """

lib/matplotlib/pyplot.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ def xkcd(scale=1, length=100, randomness=2):
387387
"xkcd mode is not compatible with text.usetex = True")
388388

389389
from matplotlib import patheffects
390-
xkcd_ctx = rc_context({
390+
return rc_context({
391391
'font.family': ['xkcd', 'Humor Sans', 'Comic Sans MS'],
392392
'font.size': 14.0,
393393
'path.sketch': (scale, length, randomness),
@@ -404,21 +404,6 @@ def xkcd(scale=1, length=100, randomness=2):
404404
'ytick.major.size': 8,
405405
'ytick.major.width': 3,
406406
})
407-
xkcd_ctx.__enter__()
408-
409-
# In order to make the call to `xkcd` that does not use a context manager
410-
# (cm) work, we need to enter into the cm ourselves, and return a dummy
411-
# cm that does nothing on entry and cleans up the xkcd context on exit.
412-
# Additionally, we need to keep a reference to the dummy cm because it
413-
# would otherwise be exited when GC'd.
414-
415-
class dummy_ctx(object):
416-
def __enter__(self):
417-
pass
418-
419-
__exit__ = xkcd_ctx.__exit__
420-
421-
return dummy_ctx()
422407

423408

424409
## Figures ##

lib/matplotlib/tests/test_style.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from __future__ import absolute_import, division, print_function
22

3+
from collections import OrderedDict
4+
from contextlib import contextmanager
5+
import gc
36
import os
47
import shutil
58
import tempfile
69
import warnings
7-
from collections import OrderedDict
8-
from contextlib import contextmanager
910

1011
import pytest
1112

@@ -163,6 +164,8 @@ def test_xkcd_no_cm():
163164
assert mpl.rcParams["path.sketch"] is None
164165
plt.xkcd()
165166
assert mpl.rcParams["path.sketch"] == (1, 100, 2)
167+
gc.collect()
168+
assert mpl.rcParams["path.sketch"] == (1, 100, 2)
166169

167170

168171
def test_xkcd_cm():

0 commit comments

Comments
 (0)