Skip to content

Commit 2d918ba

Browse files
committed
Simplify impl. of functions optionally used as context managers.
We can actually just put the "exit" logic into an ExitStack callback. If the return value is never `__enter__`'d via a "with" statement, it is never `__exit__`'d either.
1 parent 12d3c8e commit 2d918ba

File tree

1 file changed

+49
-100
lines changed

1 file changed

+49
-100
lines changed

lib/matplotlib/pyplot.py

Lines changed: 49 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
implicit and explicit interfaces.
3636
"""
3737

38+
from contextlib import ExitStack
3839
from enum import Enum
3940
import functools
4041
import importlib
@@ -438,58 +439,6 @@ def isinteractive():
438439
return matplotlib.is_interactive()
439440

440441

441-
class _IoffContext:
442-
"""
443-
Context manager for `.ioff`.
444-
445-
The state is changed in ``__init__()`` instead of ``__enter__()``. The
446-
latter is a no-op. This allows using `.ioff` both as a function and
447-
as a context.
448-
"""
449-
450-
def __init__(self):
451-
self.wasinteractive = isinteractive()
452-
matplotlib.interactive(False)
453-
uninstall_repl_displayhook()
454-
455-
def __enter__(self):
456-
pass
457-
458-
def __exit__(self, exc_type, exc_value, traceback):
459-
if self.wasinteractive:
460-
matplotlib.interactive(True)
461-
install_repl_displayhook()
462-
else:
463-
matplotlib.interactive(False)
464-
uninstall_repl_displayhook()
465-
466-
467-
class _IonContext:
468-
"""
469-
Context manager for `.ion`.
470-
471-
The state is changed in ``__init__()`` instead of ``__enter__()``. The
472-
latter is a no-op. This allows using `.ion` both as a function and
473-
as a context.
474-
"""
475-
476-
def __init__(self):
477-
self.wasinteractive = isinteractive()
478-
matplotlib.interactive(True)
479-
install_repl_displayhook()
480-
481-
def __enter__(self):
482-
pass
483-
484-
def __exit__(self, exc_type, exc_value, traceback):
485-
if not self.wasinteractive:
486-
matplotlib.interactive(False)
487-
uninstall_repl_displayhook()
488-
else:
489-
matplotlib.interactive(True)
490-
install_repl_displayhook()
491-
492-
493442
def ioff():
494443
"""
495444
Disable interactive mode.
@@ -519,11 +468,15 @@ def ioff():
519468
fig2 = plt.figure()
520469
# ...
521470
522-
To enable usage as a context manager, this function returns an
523-
``_IoffContext`` object. The return value is not intended to be stored
524-
or accessed by the user.
471+
To enable optional usage as a context manager, this function returns a
472+
`~contextlib.ExitStack` object, which is not intended to be stored or
473+
accessed by the user.
525474
"""
526-
return _IoffContext()
475+
stack = ExitStack()
476+
stack.callback(ion if isinteractive() else ioff)
477+
matplotlib.interactive(False)
478+
uninstall_repl_displayhook()
479+
return stack
527480

528481

529482
def ion():
@@ -555,11 +508,15 @@ def ion():
555508
fig2 = plt.figure()
556509
# ...
557510
558-
To enable usage as a context manager, this function returns an
559-
``_IonContext`` object. The return value is not intended to be stored
560-
or accessed by the user.
511+
To enable optional usage as a context manager, this function returns a
512+
`~contextlib.ExitStack` object, which is not intended to be stored or
513+
accessed by the user.
561514
"""
562-
return _IonContext()
515+
stack = ExitStack()
516+
stack.callback(ion if isinteractive() else ioff)
517+
matplotlib.interactive(True)
518+
install_repl_displayhook()
519+
return stack
563520

564521

565522
def pause(interval):
@@ -658,46 +615,38 @@ def xkcd(scale=1, length=100, randomness=2):
658615
# This figure will be in regular style
659616
fig2 = plt.figure()
660617
"""
661-
return _xkcd(scale, length, randomness)
662-
663-
664-
class _xkcd:
665-
# This cannot be implemented in terms of rc_context() because this needs to
666-
# work as a non-contextmanager too.
667-
668-
def __init__(self, scale, length, randomness):
669-
self._orig = rcParams.copy()
670-
671-
if rcParams['text.usetex']:
672-
raise RuntimeError(
673-
"xkcd mode is not compatible with text.usetex = True")
674-
675-
from matplotlib import patheffects
676-
rcParams.update({
677-
'font.family': ['xkcd', 'xkcd Script', 'Humor Sans', 'Comic Neue',
678-
'Comic Sans MS'],
679-
'font.size': 14.0,
680-
'path.sketch': (scale, length, randomness),
681-
'path.effects': [
682-
patheffects.withStroke(linewidth=4, foreground="w")],
683-
'axes.linewidth': 1.5,
684-
'lines.linewidth': 2.0,
685-
'figure.facecolor': 'white',
686-
'grid.linewidth': 0.0,
687-
'axes.grid': False,
688-
'axes.unicode_minus': False,
689-
'axes.edgecolor': 'black',
690-
'xtick.major.size': 8,
691-
'xtick.major.width': 3,
692-
'ytick.major.size': 8,
693-
'ytick.major.width': 3,
694-
})
695-
696-
def __enter__(self):
697-
return self
698-
699-
def __exit__(self, *args):
700-
dict.update(rcParams, self._orig)
618+
# This cannot be implemented in terms of contextmanager() or rc_context()
619+
# because this needs to work as a non-contextmanager too.
620+
621+
if rcParams['text.usetex']:
622+
raise RuntimeError(
623+
"xkcd mode is not compatible with text.usetex = True")
624+
625+
stack = ExitStack()
626+
stack.callback(dict.update, rcParams, rcParams.copy())
627+
628+
from matplotlib import patheffects
629+
rcParams.update({
630+
'font.family': ['xkcd', 'xkcd Script', 'Humor Sans', 'Comic Neue',
631+
'Comic Sans MS'],
632+
'font.size': 14.0,
633+
'path.sketch': (scale, length, randomness),
634+
'path.effects': [
635+
patheffects.withStroke(linewidth=4, foreground="w")],
636+
'axes.linewidth': 1.5,
637+
'lines.linewidth': 2.0,
638+
'figure.facecolor': 'white',
639+
'grid.linewidth': 0.0,
640+
'axes.grid': False,
641+
'axes.unicode_minus': False,
642+
'axes.edgecolor': 'black',
643+
'xtick.major.size': 8,
644+
'xtick.major.width': 3,
645+
'ytick.major.size': 8,
646+
'ytick.major.width': 3,
647+
})
648+
649+
return stack
701650

702651

703652
## Figures ##

0 commit comments

Comments
 (0)