##// END OF EJS Templates
Don't force matplotlib backend names to be lowercase (#14475)...
M Bussonnier -
r28831:6bde8f6c merge
parent child Browse files
Show More
@@ -1,173 +1,173 b''
1 """Implementation of magic functions for matplotlib/pylab support.
1 """Implementation of magic functions for matplotlib/pylab support.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2012 The IPython Development Team.
4 # Copyright (c) 2012 The IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 # Our own packages
15 # Our own packages
16 from traitlets.config.application import Application
16 from traitlets.config.application import Application
17 from IPython.core import magic_arguments
17 from IPython.core import magic_arguments
18 from IPython.core.magic import Magics, magics_class, line_magic
18 from IPython.core.magic import Magics, magics_class, line_magic
19 from IPython.testing.skipdoctest import skip_doctest
19 from IPython.testing.skipdoctest import skip_doctest
20 from warnings import warn
20 from warnings import warn
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Magic implementation classes
23 # Magic implementation classes
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 magic_gui_arg = magic_arguments.argument(
26 magic_gui_arg = magic_arguments.argument(
27 "gui",
27 "gui",
28 nargs="?",
28 nargs="?",
29 help="""Name of the matplotlib backend to use such as 'qt' or 'widget'.
29 help="""Name of the matplotlib backend to use such as 'qt' or 'widget'.
30 If given, the corresponding matplotlib backend is used,
30 If given, the corresponding matplotlib backend is used,
31 otherwise it will be matplotlib's default
31 otherwise it will be matplotlib's default
32 (which you can set in your matplotlib config file).
32 (which you can set in your matplotlib config file).
33 """,
33 """,
34 )
34 )
35
35
36
36
37 @magics_class
37 @magics_class
38 class PylabMagics(Magics):
38 class PylabMagics(Magics):
39 """Magics related to matplotlib's pylab support"""
39 """Magics related to matplotlib's pylab support"""
40
40
41 @skip_doctest
41 @skip_doctest
42 @line_magic
42 @line_magic
43 @magic_arguments.magic_arguments()
43 @magic_arguments.magic_arguments()
44 @magic_arguments.argument('-l', '--list', action='store_true',
44 @magic_arguments.argument('-l', '--list', action='store_true',
45 help='Show available matplotlib backends')
45 help='Show available matplotlib backends')
46 @magic_gui_arg
46 @magic_gui_arg
47 def matplotlib(self, line=''):
47 def matplotlib(self, line=''):
48 """Set up matplotlib to work interactively.
48 """Set up matplotlib to work interactively.
49
49
50 This function lets you activate matplotlib interactive support
50 This function lets you activate matplotlib interactive support
51 at any point during an IPython session. It does not import anything
51 at any point during an IPython session. It does not import anything
52 into the interactive namespace.
52 into the interactive namespace.
53
53
54 If you are using the inline matplotlib backend in the IPython Notebook
54 If you are using the inline matplotlib backend in the IPython Notebook
55 you can set which figure formats are enabled using the following::
55 you can set which figure formats are enabled using the following::
56
56
57 In [1]: from matplotlib_inline.backend_inline import set_matplotlib_formats
57 In [1]: from matplotlib_inline.backend_inline import set_matplotlib_formats
58
58
59 In [2]: set_matplotlib_formats('pdf', 'svg')
59 In [2]: set_matplotlib_formats('pdf', 'svg')
60
60
61 The default for inline figures sets `bbox_inches` to 'tight'. This can
61 The default for inline figures sets `bbox_inches` to 'tight'. This can
62 cause discrepancies between the displayed image and the identical
62 cause discrepancies between the displayed image and the identical
63 image created using `savefig`. This behavior can be disabled using the
63 image created using `savefig`. This behavior can be disabled using the
64 `%config` magic::
64 `%config` magic::
65
65
66 In [3]: %config InlineBackend.print_figure_kwargs = {'bbox_inches':None}
66 In [3]: %config InlineBackend.print_figure_kwargs = {'bbox_inches':None}
67
67
68 In addition, see the docstrings of
68 In addition, see the docstrings of
69 `matplotlib_inline.backend_inline.set_matplotlib_formats` and
69 `matplotlib_inline.backend_inline.set_matplotlib_formats` and
70 `matplotlib_inline.backend_inline.set_matplotlib_close` for more information on
70 `matplotlib_inline.backend_inline.set_matplotlib_close` for more information on
71 changing additional behaviors of the inline backend.
71 changing additional behaviors of the inline backend.
72
72
73 Examples
73 Examples
74 --------
74 --------
75 To enable the inline backend for usage with the IPython Notebook::
75 To enable the inline backend for usage with the IPython Notebook::
76
76
77 In [1]: %matplotlib inline
77 In [1]: %matplotlib inline
78
78
79 In this case, where the matplotlib default is TkAgg::
79 In this case, where the matplotlib default is TkAgg::
80
80
81 In [2]: %matplotlib
81 In [2]: %matplotlib
82 Using matplotlib backend: TkAgg
82 Using matplotlib backend: TkAgg
83
83
84 But you can explicitly request a different GUI backend::
84 But you can explicitly request a different GUI backend::
85
85
86 In [3]: %matplotlib qt
86 In [3]: %matplotlib qt
87
87
88 You can list the available backends using the -l/--list option::
88 You can list the available backends using the -l/--list option::
89
89
90 In [4]: %matplotlib --list
90 In [4]: %matplotlib --list
91 Available matplotlib backends: ['osx', 'qt4', 'qt5', 'gtk3', 'gtk4', 'notebook', 'wx', 'qt', 'nbagg',
91 Available matplotlib backends: ['osx', 'qt4', 'qt5', 'gtk3', 'gtk4', 'notebook', 'wx', 'qt', 'nbagg',
92 'gtk', 'tk', 'inline']
92 'gtk', 'tk', 'inline']
93 """
93 """
94 args = magic_arguments.parse_argstring(self.matplotlib, line)
94 args = magic_arguments.parse_argstring(self.matplotlib, line)
95 if args.list:
95 if args.list:
96 from IPython.core.pylabtools import _list_matplotlib_backends_and_gui_loops
96 from IPython.core.pylabtools import _list_matplotlib_backends_and_gui_loops
97
97
98 print(
98 print(
99 "Available matplotlib backends: %s"
99 "Available matplotlib backends: %s"
100 % _list_matplotlib_backends_and_gui_loops()
100 % _list_matplotlib_backends_and_gui_loops()
101 )
101 )
102 else:
102 else:
103 gui, backend = self.shell.enable_matplotlib(args.gui.lower() if isinstance(args.gui, str) else args.gui)
103 gui, backend = self.shell.enable_matplotlib(args.gui)
104 self._show_matplotlib_backend(args.gui, backend)
104 self._show_matplotlib_backend(args.gui, backend)
105
105
106 @skip_doctest
106 @skip_doctest
107 @line_magic
107 @line_magic
108 @magic_arguments.magic_arguments()
108 @magic_arguments.magic_arguments()
109 @magic_arguments.argument(
109 @magic_arguments.argument(
110 '--no-import-all', action='store_true', default=None,
110 '--no-import-all', action='store_true', default=None,
111 help="""Prevent IPython from performing ``import *`` into the interactive namespace.
111 help="""Prevent IPython from performing ``import *`` into the interactive namespace.
112
112
113 You can govern the default behavior of this flag with the
113 You can govern the default behavior of this flag with the
114 InteractiveShellApp.pylab_import_all configurable.
114 InteractiveShellApp.pylab_import_all configurable.
115 """
115 """
116 )
116 )
117 @magic_gui_arg
117 @magic_gui_arg
118 def pylab(self, line=''):
118 def pylab(self, line=''):
119 """Load numpy and matplotlib to work interactively.
119 """Load numpy and matplotlib to work interactively.
120
120
121 This function lets you activate pylab (matplotlib, numpy and
121 This function lets you activate pylab (matplotlib, numpy and
122 interactive support) at any point during an IPython session.
122 interactive support) at any point during an IPython session.
123
123
124 %pylab makes the following imports::
124 %pylab makes the following imports::
125
125
126 import numpy
126 import numpy
127 import matplotlib
127 import matplotlib
128 from matplotlib import pylab, mlab, pyplot
128 from matplotlib import pylab, mlab, pyplot
129 np = numpy
129 np = numpy
130 plt = pyplot
130 plt = pyplot
131
131
132 from IPython.display import display
132 from IPython.display import display
133 from IPython.core.pylabtools import figsize, getfigs
133 from IPython.core.pylabtools import figsize, getfigs
134
134
135 from pylab import *
135 from pylab import *
136 from numpy import *
136 from numpy import *
137
137
138 If you pass `--no-import-all`, the last two `*` imports will be excluded.
138 If you pass `--no-import-all`, the last two `*` imports will be excluded.
139
139
140 See the %matplotlib magic for more details about activating matplotlib
140 See the %matplotlib magic for more details about activating matplotlib
141 without affecting the interactive namespace.
141 without affecting the interactive namespace.
142 """
142 """
143 args = magic_arguments.parse_argstring(self.pylab, line)
143 args = magic_arguments.parse_argstring(self.pylab, line)
144 if args.no_import_all is None:
144 if args.no_import_all is None:
145 # get default from Application
145 # get default from Application
146 if Application.initialized():
146 if Application.initialized():
147 app = Application.instance()
147 app = Application.instance()
148 try:
148 try:
149 import_all = app.pylab_import_all
149 import_all = app.pylab_import_all
150 except AttributeError:
150 except AttributeError:
151 import_all = True
151 import_all = True
152 else:
152 else:
153 # nothing specified, no app - default True
153 # nothing specified, no app - default True
154 import_all = True
154 import_all = True
155 else:
155 else:
156 # invert no-import flag
156 # invert no-import flag
157 import_all = not args.no_import_all
157 import_all = not args.no_import_all
158
158
159 gui, backend, clobbered = self.shell.enable_pylab(args.gui, import_all=import_all)
159 gui, backend, clobbered = self.shell.enable_pylab(args.gui, import_all=import_all)
160 self._show_matplotlib_backend(args.gui, backend)
160 self._show_matplotlib_backend(args.gui, backend)
161 print(
161 print(
162 "%pylab is deprecated, use %matplotlib inline and import the required libraries."
162 "%pylab is deprecated, use %matplotlib inline and import the required libraries."
163 )
163 )
164 print("Populating the interactive namespace from numpy and matplotlib")
164 print("Populating the interactive namespace from numpy and matplotlib")
165 if clobbered:
165 if clobbered:
166 warn("pylab import has clobbered these variables: %s" % clobbered +
166 warn("pylab import has clobbered these variables: %s" % clobbered +
167 "\n`%matplotlib` prevents importing * from pylab and numpy"
167 "\n`%matplotlib` prevents importing * from pylab and numpy"
168 )
168 )
169
169
170 def _show_matplotlib_backend(self, gui, backend):
170 def _show_matplotlib_backend(self, gui, backend):
171 """show matplotlib message backend message"""
171 """show matplotlib message backend message"""
172 if not gui or gui == 'auto':
172 if not gui or gui == 'auto':
173 print("Using matplotlib backend: %s" % backend)
173 print("Using matplotlib backend: %s" % backend)
@@ -1,352 +1,375 b''
1 """Tests for pylab tools module.
1 """Tests for pylab tools module.
2 """
2 """
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7
7
8 from binascii import a2b_base64
8 from binascii import a2b_base64
9 from io import BytesIO
9 from io import BytesIO
10
10
11 import pytest
11 import pytest
12
12
13 matplotlib = pytest.importorskip("matplotlib")
13 matplotlib = pytest.importorskip("matplotlib")
14 matplotlib.use('Agg')
14 matplotlib.use('Agg')
15 from matplotlib.figure import Figure
15 from matplotlib.figure import Figure
16
16
17 from matplotlib import pyplot as plt
17 from matplotlib import pyplot as plt
18 from matplotlib_inline import backend_inline
18 from matplotlib_inline import backend_inline
19 import numpy as np
19 import numpy as np
20
20
21 from IPython.core.getipython import get_ipython
21 from IPython.core.getipython import get_ipython
22 from IPython.core.interactiveshell import InteractiveShell
22 from IPython.core.interactiveshell import InteractiveShell
23 from IPython.core.display import _PNG, _JPEG
23 from IPython.core.display import _PNG, _JPEG
24 from .. import pylabtools as pt
24 from .. import pylabtools as pt
25
25
26 from IPython.testing import decorators as dec
26 from IPython.testing import decorators as dec
27
27
28
28
29 def test_figure_to_svg():
29 def test_figure_to_svg():
30 # simple empty-figure test
30 # simple empty-figure test
31 fig = plt.figure()
31 fig = plt.figure()
32 assert pt.print_figure(fig, "svg") is None
32 assert pt.print_figure(fig, "svg") is None
33
33
34 plt.close('all')
34 plt.close('all')
35
35
36 # simple check for at least svg-looking output
36 # simple check for at least svg-looking output
37 fig = plt.figure()
37 fig = plt.figure()
38 ax = fig.add_subplot(1,1,1)
38 ax = fig.add_subplot(1,1,1)
39 ax.plot([1,2,3])
39 ax.plot([1,2,3])
40 plt.draw()
40 plt.draw()
41 svg = pt.print_figure(fig, "svg")[:100].lower()
41 svg = pt.print_figure(fig, "svg")[:100].lower()
42 assert "doctype svg" in svg
42 assert "doctype svg" in svg
43
43
44
44
45 def _check_pil_jpeg_bytes():
45 def _check_pil_jpeg_bytes():
46 """Skip if PIL can't write JPEGs to BytesIO objects"""
46 """Skip if PIL can't write JPEGs to BytesIO objects"""
47 # PIL's JPEG plugin can't write to BytesIO objects
47 # PIL's JPEG plugin can't write to BytesIO objects
48 # Pillow fixes this
48 # Pillow fixes this
49 from PIL import Image
49 from PIL import Image
50 buf = BytesIO()
50 buf = BytesIO()
51 img = Image.new("RGB", (4,4))
51 img = Image.new("RGB", (4,4))
52 try:
52 try:
53 img.save(buf, 'jpeg')
53 img.save(buf, 'jpeg')
54 except Exception as e:
54 except Exception as e:
55 ename = e.__class__.__name__
55 ename = e.__class__.__name__
56 raise pytest.skip("PIL can't write JPEG to BytesIO: %s: %s" % (ename, e)) from e
56 raise pytest.skip("PIL can't write JPEG to BytesIO: %s: %s" % (ename, e)) from e
57
57
58 @dec.skip_without("PIL.Image")
58 @dec.skip_without("PIL.Image")
59 def test_figure_to_jpeg():
59 def test_figure_to_jpeg():
60 _check_pil_jpeg_bytes()
60 _check_pil_jpeg_bytes()
61 # simple check for at least jpeg-looking output
61 # simple check for at least jpeg-looking output
62 fig = plt.figure()
62 fig = plt.figure()
63 ax = fig.add_subplot(1,1,1)
63 ax = fig.add_subplot(1,1,1)
64 ax.plot([1,2,3])
64 ax.plot([1,2,3])
65 plt.draw()
65 plt.draw()
66 jpeg = pt.print_figure(fig, 'jpeg', pil_kwargs={'optimize': 50})[:100].lower()
66 jpeg = pt.print_figure(fig, 'jpeg', pil_kwargs={'optimize': 50})[:100].lower()
67 assert jpeg.startswith(_JPEG)
67 assert jpeg.startswith(_JPEG)
68
68
69 def test_retina_figure():
69 def test_retina_figure():
70 # simple empty-figure test
70 # simple empty-figure test
71 fig = plt.figure()
71 fig = plt.figure()
72 assert pt.retina_figure(fig) == None
72 assert pt.retina_figure(fig) == None
73 plt.close('all')
73 plt.close('all')
74
74
75 fig = plt.figure()
75 fig = plt.figure()
76 ax = fig.add_subplot(1,1,1)
76 ax = fig.add_subplot(1,1,1)
77 ax.plot([1,2,3])
77 ax.plot([1,2,3])
78 plt.draw()
78 plt.draw()
79 png, md = pt.retina_figure(fig)
79 png, md = pt.retina_figure(fig)
80 assert png.startswith(_PNG)
80 assert png.startswith(_PNG)
81 assert "width" in md
81 assert "width" in md
82 assert "height" in md
82 assert "height" in md
83
83
84
84
85 _fmt_mime_map = {
85 _fmt_mime_map = {
86 'png': 'image/png',
86 'png': 'image/png',
87 'jpeg': 'image/jpeg',
87 'jpeg': 'image/jpeg',
88 'pdf': 'application/pdf',
88 'pdf': 'application/pdf',
89 'retina': 'image/png',
89 'retina': 'image/png',
90 'svg': 'image/svg+xml',
90 'svg': 'image/svg+xml',
91 }
91 }
92
92
93 def test_select_figure_formats_str():
93 def test_select_figure_formats_str():
94 ip = get_ipython()
94 ip = get_ipython()
95 for fmt, active_mime in _fmt_mime_map.items():
95 for fmt, active_mime in _fmt_mime_map.items():
96 pt.select_figure_formats(ip, fmt)
96 pt.select_figure_formats(ip, fmt)
97 for mime, f in ip.display_formatter.formatters.items():
97 for mime, f in ip.display_formatter.formatters.items():
98 if mime == active_mime:
98 if mime == active_mime:
99 assert Figure in f
99 assert Figure in f
100 else:
100 else:
101 assert Figure not in f
101 assert Figure not in f
102
102
103 def test_select_figure_formats_kwargs():
103 def test_select_figure_formats_kwargs():
104 ip = get_ipython()
104 ip = get_ipython()
105 kwargs = dict(bbox_inches="tight")
105 kwargs = dict(bbox_inches="tight")
106 pt.select_figure_formats(ip, "png", **kwargs)
106 pt.select_figure_formats(ip, "png", **kwargs)
107 formatter = ip.display_formatter.formatters["image/png"]
107 formatter = ip.display_formatter.formatters["image/png"]
108 f = formatter.lookup_by_type(Figure)
108 f = formatter.lookup_by_type(Figure)
109 cell = f.keywords
109 cell = f.keywords
110 expected = kwargs
110 expected = kwargs
111 expected["base64"] = True
111 expected["base64"] = True
112 expected["fmt"] = "png"
112 expected["fmt"] = "png"
113 assert cell == expected
113 assert cell == expected
114
114
115 # check that the formatter doesn't raise
115 # check that the formatter doesn't raise
116 fig = plt.figure()
116 fig = plt.figure()
117 ax = fig.add_subplot(1,1,1)
117 ax = fig.add_subplot(1,1,1)
118 ax.plot([1,2,3])
118 ax.plot([1,2,3])
119 plt.draw()
119 plt.draw()
120 formatter.enabled = True
120 formatter.enabled = True
121 png = formatter(fig)
121 png = formatter(fig)
122 assert isinstance(png, str)
122 assert isinstance(png, str)
123 png_bytes = a2b_base64(png)
123 png_bytes = a2b_base64(png)
124 assert png_bytes.startswith(_PNG)
124 assert png_bytes.startswith(_PNG)
125
125
126 def test_select_figure_formats_set():
126 def test_select_figure_formats_set():
127 ip = get_ipython()
127 ip = get_ipython()
128 for fmts in [
128 for fmts in [
129 {'png', 'svg'},
129 {'png', 'svg'},
130 ['png'],
130 ['png'],
131 ('jpeg', 'pdf', 'retina'),
131 ('jpeg', 'pdf', 'retina'),
132 {'svg'},
132 {'svg'},
133 ]:
133 ]:
134 active_mimes = {_fmt_mime_map[fmt] for fmt in fmts}
134 active_mimes = {_fmt_mime_map[fmt] for fmt in fmts}
135 pt.select_figure_formats(ip, fmts)
135 pt.select_figure_formats(ip, fmts)
136 for mime, f in ip.display_formatter.formatters.items():
136 for mime, f in ip.display_formatter.formatters.items():
137 if mime in active_mimes:
137 if mime in active_mimes:
138 assert Figure in f
138 assert Figure in f
139 else:
139 else:
140 assert Figure not in f
140 assert Figure not in f
141
141
142 def test_select_figure_formats_bad():
142 def test_select_figure_formats_bad():
143 ip = get_ipython()
143 ip = get_ipython()
144 with pytest.raises(ValueError):
144 with pytest.raises(ValueError):
145 pt.select_figure_formats(ip, 'foo')
145 pt.select_figure_formats(ip, 'foo')
146 with pytest.raises(ValueError):
146 with pytest.raises(ValueError):
147 pt.select_figure_formats(ip, {'png', 'foo'})
147 pt.select_figure_formats(ip, {'png', 'foo'})
148 with pytest.raises(ValueError):
148 with pytest.raises(ValueError):
149 pt.select_figure_formats(ip, ['retina', 'pdf', 'bar', 'bad'])
149 pt.select_figure_formats(ip, ['retina', 'pdf', 'bar', 'bad'])
150
150
151 def test_import_pylab():
151 def test_import_pylab():
152 ns = {}
152 ns = {}
153 pt.import_pylab(ns, import_all=False)
153 pt.import_pylab(ns, import_all=False)
154 assert "plt" in ns
154 assert "plt" in ns
155 assert ns["np"] == np
155 assert ns["np"] == np
156
156
157
157
158 class TestPylabSwitch(object):
158 class TestPylabSwitch(object):
159 class Shell(InteractiveShell):
159 class Shell(InteractiveShell):
160 def init_history(self):
160 def init_history(self):
161 """Sets up the command history, and starts regular autosaves."""
161 """Sets up the command history, and starts regular autosaves."""
162 self.config.HistoryManager.hist_file = ":memory:"
162 self.config.HistoryManager.hist_file = ":memory:"
163 super().init_history()
163 super().init_history()
164
164
165 def enable_gui(self, gui):
165 def enable_gui(self, gui):
166 pass
166 pass
167
167
168 def setup_method(self):
168 def setup_method(self):
169 import matplotlib
169 import matplotlib
170 def act_mpl(backend):
170 def act_mpl(backend):
171 matplotlib.rcParams['backend'] = backend
171 matplotlib.rcParams['backend'] = backend
172
172
173 # Save rcParams since they get modified
173 # Save rcParams since they get modified
174 self._saved_rcParams = matplotlib.rcParams
174 self._saved_rcParams = matplotlib.rcParams
175 self._saved_rcParamsOrig = matplotlib.rcParamsOrig
175 self._saved_rcParamsOrig = matplotlib.rcParamsOrig
176 matplotlib.rcParams = dict(backend="QtAgg")
176 matplotlib.rcParams = dict(backend="QtAgg")
177 matplotlib.rcParamsOrig = dict(backend="QtAgg")
177 matplotlib.rcParamsOrig = dict(backend="QtAgg")
178
178
179 # Mock out functions
179 # Mock out functions
180 self._save_am = pt.activate_matplotlib
180 self._save_am = pt.activate_matplotlib
181 pt.activate_matplotlib = act_mpl
181 pt.activate_matplotlib = act_mpl
182 self._save_ip = pt.import_pylab
182 self._save_ip = pt.import_pylab
183 pt.import_pylab = lambda *a,**kw:None
183 pt.import_pylab = lambda *a,**kw:None
184 self._save_cis = backend_inline.configure_inline_support
184 self._save_cis = backend_inline.configure_inline_support
185 backend_inline.configure_inline_support = lambda *a, **kw: None
185 backend_inline.configure_inline_support = lambda *a, **kw: None
186
186
187 def teardown_method(self):
187 def teardown_method(self):
188 pt.activate_matplotlib = self._save_am
188 pt.activate_matplotlib = self._save_am
189 pt.import_pylab = self._save_ip
189 pt.import_pylab = self._save_ip
190 backend_inline.configure_inline_support = self._save_cis
190 backend_inline.configure_inline_support = self._save_cis
191 import matplotlib
191 import matplotlib
192 matplotlib.rcParams = self._saved_rcParams
192 matplotlib.rcParams = self._saved_rcParams
193 matplotlib.rcParamsOrig = self._saved_rcParamsOrig
193 matplotlib.rcParamsOrig = self._saved_rcParamsOrig
194
194
195 def test_qt(self):
195 def test_qt(self):
196 s = self.Shell()
196 s = self.Shell()
197 gui, backend = s.enable_matplotlib(None)
197 gui, backend = s.enable_matplotlib(None)
198 assert gui == "qt"
198 assert gui == "qt"
199 assert s.pylab_gui_select == "qt"
199 assert s.pylab_gui_select == "qt"
200
200
201 gui, backend = s.enable_matplotlib("inline")
201 gui, backend = s.enable_matplotlib("inline")
202 assert gui is None
202 assert gui is None
203 assert s.pylab_gui_select == "qt"
203 assert s.pylab_gui_select == "qt"
204
204
205 gui, backend = s.enable_matplotlib("qt")
205 gui, backend = s.enable_matplotlib("qt")
206 assert gui == "qt"
206 assert gui == "qt"
207 assert s.pylab_gui_select == "qt"
207 assert s.pylab_gui_select == "qt"
208
208
209 gui, backend = s.enable_matplotlib("inline")
209 gui, backend = s.enable_matplotlib("inline")
210 assert gui is None
210 assert gui is None
211 assert s.pylab_gui_select == "qt"
211 assert s.pylab_gui_select == "qt"
212
212
213 gui, backend = s.enable_matplotlib()
213 gui, backend = s.enable_matplotlib()
214 assert gui == "qt"
214 assert gui == "qt"
215 assert s.pylab_gui_select == "qt"
215 assert s.pylab_gui_select == "qt"
216
216
217 def test_inline(self):
217 def test_inline(self):
218 s = self.Shell()
218 s = self.Shell()
219 gui, backend = s.enable_matplotlib("inline")
219 gui, backend = s.enable_matplotlib("inline")
220 assert gui is None
220 assert gui is None
221 assert s.pylab_gui_select == None
221 assert s.pylab_gui_select == None
222
222
223 gui, backend = s.enable_matplotlib("inline")
223 gui, backend = s.enable_matplotlib("inline")
224 assert gui is None
224 assert gui is None
225 assert s.pylab_gui_select == None
225 assert s.pylab_gui_select == None
226
226
227 gui, backend = s.enable_matplotlib("qt")
227 gui, backend = s.enable_matplotlib("qt")
228 assert gui == "qt"
228 assert gui == "qt"
229 assert s.pylab_gui_select == "qt"
229 assert s.pylab_gui_select == "qt"
230
230
231 def test_inline_twice(self):
231 def test_inline_twice(self):
232 "Using '%matplotlib inline' twice should not reset formatters"
232 "Using '%matplotlib inline' twice should not reset formatters"
233
233
234 ip = self.Shell()
234 ip = self.Shell()
235 gui, backend = ip.enable_matplotlib("inline")
235 gui, backend = ip.enable_matplotlib("inline")
236 assert gui is None
236 assert gui is None
237
237
238 fmts = {'png'}
238 fmts = {'png'}
239 active_mimes = {_fmt_mime_map[fmt] for fmt in fmts}
239 active_mimes = {_fmt_mime_map[fmt] for fmt in fmts}
240 pt.select_figure_formats(ip, fmts)
240 pt.select_figure_formats(ip, fmts)
241
241
242 gui, backend = ip.enable_matplotlib("inline")
242 gui, backend = ip.enable_matplotlib("inline")
243 assert gui is None
243 assert gui is None
244
244
245 for mime, f in ip.display_formatter.formatters.items():
245 for mime, f in ip.display_formatter.formatters.items():
246 if mime in active_mimes:
246 if mime in active_mimes:
247 assert Figure in f
247 assert Figure in f
248 else:
248 else:
249 assert Figure not in f
249 assert Figure not in f
250
250
251 def test_qt_gtk(self):
251 def test_qt_gtk(self):
252 s = self.Shell()
252 s = self.Shell()
253 gui, backend = s.enable_matplotlib("qt")
253 gui, backend = s.enable_matplotlib("qt")
254 assert gui == "qt"
254 assert gui == "qt"
255 assert s.pylab_gui_select == "qt"
255 assert s.pylab_gui_select == "qt"
256
256
257 gui, backend = s.enable_matplotlib("gtk3")
257 gui, backend = s.enable_matplotlib("gtk3")
258 assert gui == "qt"
258 assert gui == "qt"
259 assert s.pylab_gui_select == "qt"
259 assert s.pylab_gui_select == "qt"
260
260
261 @dec.skipif(not pt._matplotlib_manages_backends())
262 def test_backend_module_name_case_sensitive(self):
263 # Matplotlib backend names are case insensitive unless explicitly specified using
264 # "module://some_module.some_name" syntax which are case sensitive for mpl >= 3.9.1
265 all_lowercase = "module://matplotlib_inline.backend_inline"
266 some_uppercase = "module://matplotlib_inline.Backend_inline"
267 mpl3_9_1 = matplotlib.__version_info__ >= (3, 9, 1)
268
269 s = self.Shell()
270 s.enable_matplotlib(all_lowercase)
271 if mpl3_9_1:
272 with pytest.raises(RuntimeError):
273 s.enable_matplotlib(some_uppercase)
274 else:
275 s.enable_matplotlib(some_uppercase)
276
277 s.run_line_magic("matplotlib", all_lowercase)
278 if mpl3_9_1:
279 with pytest.raises(RuntimeError):
280 s.run_line_magic("matplotlib", some_uppercase)
281 else:
282 s.run_line_magic("matplotlib", some_uppercase)
283
261
284
262 def test_no_gui_backends():
285 def test_no_gui_backends():
263 for k in ['agg', 'svg', 'pdf', 'ps']:
286 for k in ['agg', 'svg', 'pdf', 'ps']:
264 assert k not in pt.backend2gui
287 assert k not in pt.backend2gui
265
288
266
289
267 def test_figure_no_canvas():
290 def test_figure_no_canvas():
268 fig = Figure()
291 fig = Figure()
269 fig.canvas = None
292 fig.canvas = None
270 pt.print_figure(fig)
293 pt.print_figure(fig)
271
294
272
295
273 @pytest.mark.parametrize(
296 @pytest.mark.parametrize(
274 "name, expected_gui, expected_backend",
297 "name, expected_gui, expected_backend",
275 [
298 [
276 # name is gui
299 # name is gui
277 ("gtk3", "gtk3", "gtk3agg"),
300 ("gtk3", "gtk3", "gtk3agg"),
278 ("gtk4", "gtk4", "gtk4agg"),
301 ("gtk4", "gtk4", "gtk4agg"),
279 ("headless", None, "agg"),
302 ("headless", None, "agg"),
280 ("osx", "osx", "macosx"),
303 ("osx", "osx", "macosx"),
281 ("qt", "qt", "qtagg"),
304 ("qt", "qt", "qtagg"),
282 ("qt5", "qt5", "qt5agg"),
305 ("qt5", "qt5", "qt5agg"),
283 ("qt6", "qt6", "qtagg"),
306 ("qt6", "qt6", "qtagg"),
284 ("tk", "tk", "tkagg"),
307 ("tk", "tk", "tkagg"),
285 ("wx", "wx", "wxagg"),
308 ("wx", "wx", "wxagg"),
286 # name is backend
309 # name is backend
287 ("agg", None, "agg"),
310 ("agg", None, "agg"),
288 ("cairo", None, "cairo"),
311 ("cairo", None, "cairo"),
289 ("pdf", None, "pdf"),
312 ("pdf", None, "pdf"),
290 ("ps", None, "ps"),
313 ("ps", None, "ps"),
291 ("svg", None, "svg"),
314 ("svg", None, "svg"),
292 ("template", None, "template"),
315 ("template", None, "template"),
293 ("gtk3agg", "gtk3", "gtk3agg"),
316 ("gtk3agg", "gtk3", "gtk3agg"),
294 ("gtk3cairo", "gtk3", "gtk3cairo"),
317 ("gtk3cairo", "gtk3", "gtk3cairo"),
295 ("gtk4agg", "gtk4", "gtk4agg"),
318 ("gtk4agg", "gtk4", "gtk4agg"),
296 ("gtk4cairo", "gtk4", "gtk4cairo"),
319 ("gtk4cairo", "gtk4", "gtk4cairo"),
297 ("macosx", "osx", "macosx"),
320 ("macosx", "osx", "macosx"),
298 ("nbagg", "nbagg", "nbagg"),
321 ("nbagg", "nbagg", "nbagg"),
299 ("notebook", "nbagg", "notebook"),
322 ("notebook", "nbagg", "notebook"),
300 ("qtagg", "qt", "qtagg"),
323 ("qtagg", "qt", "qtagg"),
301 ("qtcairo", "qt", "qtcairo"),
324 ("qtcairo", "qt", "qtcairo"),
302 ("qt5agg", "qt5", "qt5agg"),
325 ("qt5agg", "qt5", "qt5agg"),
303 ("qt5cairo", "qt5", "qt5cairo"),
326 ("qt5cairo", "qt5", "qt5cairo"),
304 ("tkagg", "tk", "tkagg"),
327 ("tkagg", "tk", "tkagg"),
305 ("tkcairo", "tk", "tkcairo"),
328 ("tkcairo", "tk", "tkcairo"),
306 ("webagg", "webagg", "webagg"),
329 ("webagg", "webagg", "webagg"),
307 ("wxagg", "wx", "wxagg"),
330 ("wxagg", "wx", "wxagg"),
308 ("wxcairo", "wx", "wxcairo"),
331 ("wxcairo", "wx", "wxcairo"),
309 ],
332 ],
310 )
333 )
311 def test_backend_builtin(name, expected_gui, expected_backend):
334 def test_backend_builtin(name, expected_gui, expected_backend):
312 # Test correct identification of Matplotlib built-in backends without importing and using them,
335 # Test correct identification of Matplotlib built-in backends without importing and using them,
313 # otherwise we would need to ensure all the complex dependencies such as windowing toolkits are
336 # otherwise we would need to ensure all the complex dependencies such as windowing toolkits are
314 # installed.
337 # installed.
315
338
316 mpl_manages_backends = pt._matplotlib_manages_backends()
339 mpl_manages_backends = pt._matplotlib_manages_backends()
317 if not mpl_manages_backends:
340 if not mpl_manages_backends:
318 # Backends not supported before _matplotlib_manages_backends or supported
341 # Backends not supported before _matplotlib_manages_backends or supported
319 # but with different expected_gui or expected_backend.
342 # but with different expected_gui or expected_backend.
320 if (
343 if (
321 name.endswith("agg")
344 name.endswith("agg")
322 or name.endswith("cairo")
345 or name.endswith("cairo")
323 or name in ("headless", "macosx", "pdf", "ps", "svg", "template")
346 or name in ("headless", "macosx", "pdf", "ps", "svg", "template")
324 ):
347 ):
325 pytest.skip()
348 pytest.skip()
326 elif name == "qt6":
349 elif name == "qt6":
327 expected_backend = "qtagg"
350 expected_backend = "qtagg"
328 elif name == "notebook":
351 elif name == "notebook":
329 expected_backend, expected_gui = expected_gui, expected_backend
352 expected_backend, expected_gui = expected_gui, expected_backend
330
353
331 gui, backend = pt.find_gui_and_backend(name)
354 gui, backend = pt.find_gui_and_backend(name)
332 if not mpl_manages_backends:
355 if not mpl_manages_backends:
333 gui = gui.lower() if gui else None
356 gui = gui.lower() if gui else None
334 backend = backend.lower() if backend else None
357 backend = backend.lower() if backend else None
335 assert gui == expected_gui
358 assert gui == expected_gui
336 assert backend == expected_backend
359 assert backend == expected_backend
337
360
338
361
339 def test_backend_entry_point():
362 def test_backend_entry_point():
340 gui, backend = pt.find_gui_and_backend("inline")
363 gui, backend = pt.find_gui_and_backend("inline")
341 assert gui is None
364 assert gui is None
342 expected_backend = (
365 expected_backend = (
343 "inline"
366 "inline"
344 if pt._matplotlib_manages_backends()
367 if pt._matplotlib_manages_backends()
345 else "module://matplotlib_inline.backend_inline"
368 else "module://matplotlib_inline.backend_inline"
346 )
369 )
347 assert backend == expected_backend
370 assert backend == expected_backend
348
371
349
372
350 def test_backend_unknown():
373 def test_backend_unknown():
351 with pytest.raises(RuntimeError if pt._matplotlib_manages_backends() else KeyError):
374 with pytest.raises(RuntimeError if pt._matplotlib_manages_backends() else KeyError):
352 pt.find_gui_and_backend("name-does-not-exist")
375 pt.find_gui_and_backend("name-does-not-exist")
General Comments 0
You need to be logged in to leave comments. Login now