##// END OF EJS Templates
Fix compatibility with pytest 8 (#14413)...
M Bussonnier -
r28736:f68c7376 merge
parent child Browse files
Show More
@@ -1,354 +1,354 b''
1 1 """Tests for pylab tools module.
2 2 """
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7
8 8 from binascii import a2b_base64
9 9 from io import BytesIO
10 10
11 11 import pytest
12 12
13 13 matplotlib = pytest.importorskip("matplotlib")
14 14 matplotlib.use('Agg')
15 15 from matplotlib.figure import Figure
16 16
17 17 from matplotlib import pyplot as plt
18 18 from matplotlib_inline import backend_inline
19 19 import numpy as np
20 20
21 21 from IPython.core.getipython import get_ipython
22 22 from IPython.core.interactiveshell import InteractiveShell
23 23 from IPython.core.display import _PNG, _JPEG
24 24 from .. import pylabtools as pt
25 25
26 26 from IPython.testing import decorators as dec
27 27
28 28
29 29 def test_figure_to_svg():
30 30 # simple empty-figure test
31 31 fig = plt.figure()
32 32 assert pt.print_figure(fig, "svg") is None
33 33
34 34 plt.close('all')
35 35
36 36 # simple check for at least svg-looking output
37 37 fig = plt.figure()
38 38 ax = fig.add_subplot(1,1,1)
39 39 ax.plot([1,2,3])
40 40 plt.draw()
41 41 svg = pt.print_figure(fig, "svg")[:100].lower()
42 42 assert "doctype svg" in svg
43 43
44 44
45 45 def _check_pil_jpeg_bytes():
46 46 """Skip if PIL can't write JPEGs to BytesIO objects"""
47 47 # PIL's JPEG plugin can't write to BytesIO objects
48 48 # Pillow fixes this
49 49 from PIL import Image
50 50 buf = BytesIO()
51 51 img = Image.new("RGB", (4,4))
52 52 try:
53 53 img.save(buf, 'jpeg')
54 54 except Exception as e:
55 55 ename = e.__class__.__name__
56 56 raise pytest.skip("PIL can't write JPEG to BytesIO: %s: %s" % (ename, e)) from e
57 57
58 58 @dec.skip_without("PIL.Image")
59 59 def test_figure_to_jpeg():
60 60 _check_pil_jpeg_bytes()
61 61 # simple check for at least jpeg-looking output
62 62 fig = plt.figure()
63 63 ax = fig.add_subplot(1,1,1)
64 64 ax.plot([1,2,3])
65 65 plt.draw()
66 66 jpeg = pt.print_figure(fig, 'jpeg', pil_kwargs={'optimize': 50})[:100].lower()
67 67 assert jpeg.startswith(_JPEG)
68 68
69 69 def test_retina_figure():
70 70 # simple empty-figure test
71 71 fig = plt.figure()
72 72 assert pt.retina_figure(fig) == None
73 73 plt.close('all')
74 74
75 75 fig = plt.figure()
76 76 ax = fig.add_subplot(1,1,1)
77 77 ax.plot([1,2,3])
78 78 plt.draw()
79 79 png, md = pt.retina_figure(fig)
80 80 assert png.startswith(_PNG)
81 81 assert "width" in md
82 82 assert "height" in md
83 83
84 84
85 85 _fmt_mime_map = {
86 86 'png': 'image/png',
87 87 'jpeg': 'image/jpeg',
88 88 'pdf': 'application/pdf',
89 89 'retina': 'image/png',
90 90 'svg': 'image/svg+xml',
91 91 }
92 92
93 93 def test_select_figure_formats_str():
94 94 ip = get_ipython()
95 95 for fmt, active_mime in _fmt_mime_map.items():
96 96 pt.select_figure_formats(ip, fmt)
97 97 for mime, f in ip.display_formatter.formatters.items():
98 98 if mime == active_mime:
99 99 assert Figure in f
100 100 else:
101 101 assert Figure not in f
102 102
103 103 def test_select_figure_formats_kwargs():
104 104 ip = get_ipython()
105 105 kwargs = dict(bbox_inches="tight")
106 106 pt.select_figure_formats(ip, "png", **kwargs)
107 107 formatter = ip.display_formatter.formatters["image/png"]
108 108 f = formatter.lookup_by_type(Figure)
109 109 cell = f.keywords
110 110 expected = kwargs
111 111 expected["base64"] = True
112 112 expected["fmt"] = "png"
113 113 assert cell == expected
114 114
115 115 # check that the formatter doesn't raise
116 116 fig = plt.figure()
117 117 ax = fig.add_subplot(1,1,1)
118 118 ax.plot([1,2,3])
119 119 plt.draw()
120 120 formatter.enabled = True
121 121 png = formatter(fig)
122 122 assert isinstance(png, str)
123 123 png_bytes = a2b_base64(png)
124 124 assert png_bytes.startswith(_PNG)
125 125
126 126 def test_select_figure_formats_set():
127 127 ip = get_ipython()
128 128 for fmts in [
129 129 {'png', 'svg'},
130 130 ['png'],
131 131 ('jpeg', 'pdf', 'retina'),
132 132 {'svg'},
133 133 ]:
134 134 active_mimes = {_fmt_mime_map[fmt] for fmt in fmts}
135 135 pt.select_figure_formats(ip, fmts)
136 136 for mime, f in ip.display_formatter.formatters.items():
137 137 if mime in active_mimes:
138 138 assert Figure in f
139 139 else:
140 140 assert Figure not in f
141 141
142 142 def test_select_figure_formats_bad():
143 143 ip = get_ipython()
144 144 with pytest.raises(ValueError):
145 145 pt.select_figure_formats(ip, 'foo')
146 146 with pytest.raises(ValueError):
147 147 pt.select_figure_formats(ip, {'png', 'foo'})
148 148 with pytest.raises(ValueError):
149 149 pt.select_figure_formats(ip, ['retina', 'pdf', 'bar', 'bad'])
150 150
151 151 def test_import_pylab():
152 152 ns = {}
153 153 pt.import_pylab(ns, import_all=False)
154 154 assert "plt" in ns
155 155 assert ns["np"] == np
156 156
157 157
158 158 class TestPylabSwitch(object):
159 159 class Shell(InteractiveShell):
160 160 def init_history(self):
161 161 """Sets up the command history, and starts regular autosaves."""
162 162 self.config.HistoryManager.hist_file = ":memory:"
163 163 super().init_history()
164 164
165 165 def enable_gui(self, gui):
166 166 pass
167 167
168 def setup(self):
168 def setup_method(self):
169 169 import matplotlib
170 170 def act_mpl(backend):
171 171 matplotlib.rcParams['backend'] = backend
172 172
173 173 # Save rcParams since they get modified
174 174 self._saved_rcParams = matplotlib.rcParams
175 175 self._saved_rcParamsOrig = matplotlib.rcParamsOrig
176 176 matplotlib.rcParams = dict(backend="QtAgg")
177 177 matplotlib.rcParamsOrig = dict(backend="QtAgg")
178 178
179 179 # Mock out functions
180 180 self._save_am = pt.activate_matplotlib
181 181 pt.activate_matplotlib = act_mpl
182 182 self._save_ip = pt.import_pylab
183 183 pt.import_pylab = lambda *a,**kw:None
184 184 self._save_cis = backend_inline.configure_inline_support
185 185 backend_inline.configure_inline_support = lambda *a, **kw: None
186 186
187 def teardown(self):
187 def teardown_method(self):
188 188 pt.activate_matplotlib = self._save_am
189 189 pt.import_pylab = self._save_ip
190 190 backend_inline.configure_inline_support = self._save_cis
191 191 import matplotlib
192 192 matplotlib.rcParams = self._saved_rcParams
193 193 matplotlib.rcParamsOrig = self._saved_rcParamsOrig
194 194
195 195 def test_qt(self):
196 196 s = self.Shell()
197 197 gui, backend = s.enable_matplotlib(None)
198 198 assert gui == "qt"
199 199 assert s.pylab_gui_select == "qt"
200 200
201 201 gui, backend = s.enable_matplotlib("inline")
202 202 assert gui is None
203 203 assert s.pylab_gui_select == "qt"
204 204
205 205 gui, backend = s.enable_matplotlib("qt")
206 206 assert gui == "qt"
207 207 assert s.pylab_gui_select == "qt"
208 208
209 209 gui, backend = s.enable_matplotlib("inline")
210 210 assert gui is None
211 211 assert s.pylab_gui_select == "qt"
212 212
213 213 gui, backend = s.enable_matplotlib()
214 214 assert gui == "qt"
215 215 assert s.pylab_gui_select == "qt"
216 216
217 217 def test_inline(self):
218 218 s = self.Shell()
219 219 gui, backend = s.enable_matplotlib("inline")
220 220 assert gui is None
221 221 assert s.pylab_gui_select == None
222 222
223 223 gui, backend = s.enable_matplotlib("inline")
224 224 assert gui is None
225 225 assert s.pylab_gui_select == None
226 226
227 227 gui, backend = s.enable_matplotlib("qt")
228 228 assert gui == "qt"
229 229 assert s.pylab_gui_select == "qt"
230 230
231 231 def test_inline_twice(self):
232 232 "Using '%matplotlib inline' twice should not reset formatters"
233 233
234 234 ip = self.Shell()
235 235 gui, backend = ip.enable_matplotlib("inline")
236 236 assert gui is None
237 237
238 238 fmts = {'png'}
239 239 active_mimes = {_fmt_mime_map[fmt] for fmt in fmts}
240 240 pt.select_figure_formats(ip, fmts)
241 241
242 242 gui, backend = ip.enable_matplotlib("inline")
243 243 assert gui is None
244 244
245 245 for mime, f in ip.display_formatter.formatters.items():
246 246 if mime in active_mimes:
247 247 assert Figure in f
248 248 else:
249 249 assert Figure not in f
250 250
251 251 def test_qt_gtk(self):
252 252 s = self.Shell()
253 253 gui, backend = s.enable_matplotlib("qt")
254 254 assert gui == "qt"
255 255 assert s.pylab_gui_select == "qt"
256 256
257 257 gui, backend = s.enable_matplotlib("gtk3")
258 258 assert gui == "qt"
259 259 assert s.pylab_gui_select == "qt"
260 260
261 261
262 262 def test_no_gui_backends():
263 263 for k in ['agg', 'svg', 'pdf', 'ps']:
264 264 assert k not in pt.backend2gui
265 265
266 266
267 267 def test_figure_no_canvas():
268 268 fig = Figure()
269 269 fig.canvas = None
270 270 pt.print_figure(fig)
271 271
272 272
273 273 @pytest.mark.parametrize(
274 274 "name, expected_gui, expected_backend",
275 275 [
276 276 # name is gui
277 277 ("gtk3", "gtk3", "gtk3agg"),
278 278 ("gtk4", "gtk4", "gtk4agg"),
279 279 ("headless", "headless", "agg"),
280 280 ("osx", "osx", "macosx"),
281 281 ("qt", "qt", "qtagg"),
282 282 ("qt5", "qt5", "qt5agg"),
283 283 ("qt6", "qt6", "qt6agg"),
284 284 ("tk", "tk", "tkagg"),
285 285 ("wx", "wx", "wxagg"),
286 286 # name is backend
287 287 ("agg", None, "agg"),
288 288 ("cairo", None, "cairo"),
289 289 ("pdf", None, "pdf"),
290 290 ("ps", None, "ps"),
291 291 ("svg", None, "svg"),
292 292 ("template", None, "template"),
293 293 ("gtk3agg", "gtk3", "gtk3agg"),
294 294 ("gtk3cairo", "gtk3", "gtk3cairo"),
295 295 ("gtk4agg", "gtk4", "gtk4agg"),
296 296 ("gtk4cairo", "gtk4", "gtk4cairo"),
297 297 ("macosx", "osx", "macosx"),
298 298 ("nbagg", "nbagg", "nbagg"),
299 299 ("notebook", "nbagg", "notebook"),
300 300 ("qtagg", "qt", "qtagg"),
301 301 ("qtcairo", "qt", "qtcairo"),
302 302 ("qt5agg", "qt5", "qt5agg"),
303 303 ("qt5cairo", "qt5", "qt5cairo"),
304 304 ("qt6agg", "qt", "qt6agg"),
305 305 ("qt6cairo", "qt", "qt6cairo"),
306 306 ("tkagg", "tk", "tkagg"),
307 307 ("tkcairo", "tk", "tkcairo"),
308 308 ("webagg", "webagg", "webagg"),
309 309 ("wxagg", "wx", "wxagg"),
310 310 ("wxcairo", "wx", "wxcairo"),
311 311 ],
312 312 )
313 313 def test_backend_builtin(name, expected_gui, expected_backend):
314 314 # Test correct identification of Matplotlib built-in backends without importing and using them,
315 315 # otherwise we would need to ensure all the complex dependencies such as windowing toolkits are
316 316 # installed.
317 317
318 318 mpl_manages_backends = pt._matplotlib_manages_backends()
319 319 if not mpl_manages_backends:
320 320 # Backends not supported before _matplotlib_manages_backends or supported
321 321 # but with different expected_gui or expected_backend.
322 322 if (
323 323 name.endswith("agg")
324 324 or name.endswith("cairo")
325 325 or name in ("headless", "macosx", "pdf", "ps", "svg", "template")
326 326 ):
327 327 pytest.skip()
328 328 elif name == "qt6":
329 329 expected_backend = "qtagg"
330 330 elif name == "notebook":
331 331 expected_backend, expected_gui = expected_gui, expected_backend
332 332
333 333 gui, backend = pt.find_gui_and_backend(name)
334 334 if not mpl_manages_backends:
335 335 gui = gui.lower() if gui else None
336 336 backend = backend.lower() if backend else None
337 337 assert gui == expected_gui
338 338 assert backend == expected_backend
339 339
340 340
341 341 def test_backend_entry_point():
342 342 gui, backend = pt.find_gui_and_backend("inline")
343 343 assert gui is None
344 344 expected_backend = (
345 345 "inline"
346 346 if pt._matplotlib_manages_backends()
347 347 else "module://matplotlib_inline.backend_inline"
348 348 )
349 349 assert backend == expected_backend
350 350
351 351
352 352 def test_backend_unknown():
353 353 with pytest.raises(RuntimeError if pt._matplotlib_manages_backends() else KeyError):
354 354 pt.find_gui_and_backend("name-does-not-exist")
@@ -1,209 +1,209 b''
1 1 [build-system]
2 2 requires = ["setuptools>=61.2"]
3 3 # We need access to the 'setupbase' module at build time.
4 4 # Hence we declare a custom build backend.
5 5 build-backend = "_build_meta" # just re-exports setuptools.build_meta definitions
6 6 backend-path = ["."]
7 7
8 8 [project]
9 9 name = "ipython"
10 10 description = "IPython: Productive Interactive Computing"
11 11 keywords = ["Interactive", "Interpreter", "Shell", "Embedding"]
12 12 classifiers = [
13 13 "Framework :: IPython",
14 14 "Framework :: Jupyter",
15 15 "Intended Audience :: Developers",
16 16 "Intended Audience :: Science/Research",
17 17 "License :: OSI Approved :: BSD License",
18 18 "Programming Language :: Python",
19 19 "Programming Language :: Python :: 3",
20 20 "Programming Language :: Python :: 3 :: Only",
21 21 "Topic :: System :: Shells",
22 22 ]
23 23 requires-python = ">=3.10"
24 24 dependencies = [
25 25 'colorama; sys_platform == "win32"',
26 26 "decorator",
27 27 "exceptiongroup; python_version<'3.11'",
28 28 "jedi>=0.16",
29 29 "matplotlib-inline",
30 30 'pexpect>4.3; sys_platform != "win32" and sys_platform != "emscripten"',
31 31 "prompt_toolkit>=3.0.41,<3.1.0",
32 32 "pygments>=2.4.0",
33 33 "stack_data",
34 34 "traitlets>=5.13.0",
35 35 "typing_extensions>=4.6; python_version<'3.12'",
36 36 ]
37 37 dynamic = ["authors", "license", "version"]
38 38
39 39 [project.entry-points."pygments.lexers"]
40 40 ipythonconsole = "IPython.lib.lexers:IPythonConsoleLexer"
41 41 ipython = "IPython.lib.lexers:IPythonLexer"
42 42 ipython3 = "IPython.lib.lexers:IPython3Lexer"
43 43
44 44 [project.scripts]
45 45 ipython = "IPython:start_ipython"
46 46 ipython3 = "IPython:start_ipython"
47 47
48 48 [project.readme]
49 49 file = "long_description.rst"
50 50 content-type = "text/x-rst"
51 51
52 52 [project.urls]
53 53 Homepage = "https://ipython.org"
54 54 Documentation = "https://ipython.readthedocs.io/"
55 55 Funding = "https://numfocus.org/"
56 56 Source = "https://github.com/ipython/ipython"
57 57 Tracker = "https://github.com/ipython/ipython/issues"
58 58
59 59 [project.optional-dependencies]
60 60 black = [
61 61 "black",
62 62 ]
63 63 doc = [
64 64 "ipykernel",
65 65 "setuptools>=18.5",
66 66 "sphinx>=1.3",
67 67 "sphinx-rtd-theme",
68 68 "sphinxcontrib-jquery",
69 69 "docrepr",
70 70 "matplotlib",
71 71 "stack_data",
72 72 "typing_extensions",
73 73 "exceptiongroup",
74 74 "ipython[test]",
75 75 ]
76 76 kernel = [
77 77 "ipykernel",
78 78 ]
79 79 nbconvert = [
80 80 "nbconvert",
81 81 ]
82 82 nbformat = [
83 83 "nbformat",
84 84 ]
85 85 notebook = [
86 86 "ipywidgets",
87 87 "notebook",
88 88 ]
89 89 parallel = [
90 90 "ipyparallel",
91 91 ]
92 92 qtconsole = [
93 93 "qtconsole",
94 94 ]
95 95 terminal = []
96 96 test = [
97 "pytest<8",
97 "pytest",
98 98 "pytest-asyncio<0.22",
99 99 "testpath",
100 100 "pickleshare",
101 101 ]
102 102 test_extra = [
103 103 "ipython[test]",
104 104 "curio",
105 105 "matplotlib!=3.2.0",
106 106 "nbformat",
107 107 "numpy>=1.23",
108 108 "pandas",
109 109 "trio",
110 110 ]
111 111 matplotlib = [
112 112 "matplotlib"
113 113 ]
114 114 all = [
115 115 "ipython[black,doc,kernel,nbconvert,nbformat,notebook,parallel,qtconsole,matplotlib]",
116 116 "ipython[test,test_extra]",
117 117 ]
118 118
119 119 [tool.mypy]
120 120 python_version = "3.10"
121 121 ignore_missing_imports = true
122 122 follow_imports = 'silent'
123 123 exclude = [
124 124 'test_\.+\.py',
125 125 'IPython.utils.tests.test_wildcard',
126 126 'testing',
127 127 'tests',
128 128 'PyColorize.py',
129 129 '_process_win32_controller.py',
130 130 'IPython/core/application.py',
131 131 'IPython/core/profileapp.py',
132 132 'IPython/lib/deepreload.py',
133 133 'IPython/sphinxext/ipython_directive.py',
134 134 'IPython/terminal/ipapp.py',
135 135 'IPython/utils/_process_win32.py',
136 136 'IPython/utils/path.py',
137 137 ]
138 138
139 139 [tool.pytest.ini_options]
140 140 addopts = [
141 141 "--durations=10",
142 142 "-pIPython.testing.plugin.pytest_ipdoctest",
143 143 "--ipdoctest-modules",
144 144 "--ignore=docs",
145 145 "--ignore=examples",
146 146 "--ignore=htmlcov",
147 147 "--ignore=ipython_kernel",
148 148 "--ignore=ipython_parallel",
149 149 "--ignore=results",
150 150 "--ignore=tmp",
151 151 "--ignore=tools",
152 152 "--ignore=traitlets",
153 153 "--ignore=IPython/core/tests/daft_extension",
154 154 "--ignore=IPython/sphinxext",
155 155 "--ignore=IPython/terminal/pt_inputhooks",
156 156 "--ignore=IPython/__main__.py",
157 157 "--ignore=IPython/external/qt_for_kernel.py",
158 158 "--ignore=IPython/html/widgets/widget_link.py",
159 159 "--ignore=IPython/html/widgets/widget_output.py",
160 160 "--ignore=IPython/terminal/console.py",
161 161 "--ignore=IPython/utils/_process_cli.py",
162 162 "--ignore=IPython/utils/_process_posix.py",
163 163 "--ignore=IPython/utils/_process_win32.py",
164 164 "--ignore=IPython/utils/_process_win32_controller.py",
165 165 "--ignore=IPython/utils/daemonize.py",
166 166 "--ignore=IPython/utils/eventful.py",
167 167 "--ignore=IPython/kernel",
168 168 "--ignore=IPython/consoleapp.py",
169 169 "--ignore=IPython/core/inputsplitter.py",
170 170 "--ignore=IPython/lib/kernel.py",
171 171 "--ignore=IPython/utils/jsonutil.py",
172 172 "--ignore=IPython/utils/localinterfaces.py",
173 173 "--ignore=IPython/utils/log.py",
174 174 "--ignore=IPython/utils/signatures.py",
175 175 "--ignore=IPython/utils/traitlets.py",
176 176 "--ignore=IPython/utils/version.py"
177 177 ]
178 178 doctest_optionflags = [
179 179 "NORMALIZE_WHITESPACE",
180 180 "ELLIPSIS"
181 181 ]
182 182 ipdoctest_optionflags = [
183 183 "NORMALIZE_WHITESPACE",
184 184 "ELLIPSIS"
185 185 ]
186 186 asyncio_mode = "strict"
187 187
188 188 [tool.pyright]
189 189 pythonPlatform="All"
190 190
191 191 [tool.setuptools]
192 192 zip-safe = false
193 193 platforms = ["Linux", "Mac OSX", "Windows"]
194 194 license-files = ["LICENSE"]
195 195 include-package-data = false
196 196
197 197 [tool.setuptools.packages.find]
198 198 exclude = ["setupext"]
199 199 namespaces = false
200 200
201 201 [tool.setuptools.package-data]
202 202 "IPython" = ["py.typed"]
203 203 "IPython.core" = ["profile/README*"]
204 204 "IPython.core.tests" = ["*.png", "*.jpg", "daft_extension/*.py"]
205 205 "IPython.lib.tests" = ["*.wav"]
206 206 "IPython.testing.plugin" = ["*.txt"]
207 207
208 208 [tool.setuptools.dynamic]
209 209 version = {attr = "IPython.core.release.__version__"}
General Comments 0
You need to be logged in to leave comments. Login now