Skip to content

Commit ddc260c

Browse files
authored
Merge pull request matplotlib#23546 from anntzer/cm_or_switch
MNT: Simplify impl. of functions optionally used as context managers.
2 parents b5ac96a + 2d918ba commit ddc260c

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)