##// END OF EJS Templates
Correctly handle "ipython --matplotlib=<whatever>"
Ian Thomas -
Show More
@@ -1,178 +1,173 b''
1 1 """Implementation of magic functions for matplotlib/pylab support.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 # Our own packages
16 16 from traitlets.config.application import Application
17 17 from IPython.core import magic_arguments
18 18 from IPython.core.magic import Magics, magics_class, line_magic
19 19 from IPython.testing.skipdoctest import skip_doctest
20 20 from warnings import warn
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Magic implementation classes
24 24 #-----------------------------------------------------------------------------
25 25
26 26 magic_gui_arg = magic_arguments.argument(
27 27 "gui",
28 28 nargs="?",
29 29 help="""Name of the matplotlib backend to use such as 'qt' or 'widget'.
30 30 If given, the corresponding matplotlib backend is used,
31 31 otherwise it will be matplotlib's default
32 32 (which you can set in your matplotlib config file).
33 33 """,
34 34 )
35 35
36 36
37 37 @magics_class
38 38 class PylabMagics(Magics):
39 39 """Magics related to matplotlib's pylab support"""
40 40
41 41 @skip_doctest
42 42 @line_magic
43 43 @magic_arguments.magic_arguments()
44 44 @magic_arguments.argument('-l', '--list', action='store_true',
45 45 help='Show available matplotlib backends')
46 46 @magic_gui_arg
47 47 def matplotlib(self, line=''):
48 48 """Set up matplotlib to work interactively.
49 49
50 50 This function lets you activate matplotlib interactive support
51 51 at any point during an IPython session. It does not import anything
52 52 into the interactive namespace.
53 53
54 54 If you are using the inline matplotlib backend in the IPython Notebook
55 55 you can set which figure formats are enabled using the following::
56 56
57 57 In [1]: from matplotlib_inline.backend_inline import set_matplotlib_formats
58 58
59 59 In [2]: set_matplotlib_formats('pdf', 'svg')
60 60
61 61 The default for inline figures sets `bbox_inches` to 'tight'. This can
62 62 cause discrepancies between the displayed image and the identical
63 63 image created using `savefig`. This behavior can be disabled using the
64 64 `%config` magic::
65 65
66 66 In [3]: %config InlineBackend.print_figure_kwargs = {'bbox_inches':None}
67 67
68 68 In addition, see the docstrings of
69 69 `matplotlib_inline.backend_inline.set_matplotlib_formats` and
70 70 `matplotlib_inline.backend_inline.set_matplotlib_close` for more information on
71 71 changing additional behaviors of the inline backend.
72 72
73 73 Examples
74 74 --------
75 75 To enable the inline backend for usage with the IPython Notebook::
76 76
77 77 In [1]: %matplotlib inline
78 78
79 79 In this case, where the matplotlib default is TkAgg::
80 80
81 81 In [2]: %matplotlib
82 82 Using matplotlib backend: TkAgg
83 83
84 84 But you can explicitly request a different GUI backend::
85 85
86 86 In [3]: %matplotlib qt
87 87
88 88 You can list the available backends using the -l/--list option::
89 89
90 90 In [4]: %matplotlib --list
91 91 Available matplotlib backends: ['osx', 'qt4', 'qt5', 'gtk3', 'gtk4', 'notebook', 'wx', 'qt', 'nbagg',
92 92 'gtk', 'tk', 'inline']
93 93 """
94 94 args = magic_arguments.parse_argstring(self.matplotlib, line)
95 95 if args.list:
96 from IPython.core.pylabtools import _matplotlib_manages_backends
96 from IPython.core.pylabtools import _list_matplotlib_backends_and_gui_loops
97 97
98 if _matplotlib_manages_backends():
99 from matplotlib.backends.registry import backend_registry
100
101 backends_list = backend_registry.list_all()
102 else:
103 from IPython.core.pylabtools import backends
104
105 backends_list = list(backends.keys())
106 print("Available matplotlib backends: %s" % backends_list)
98 print(
99 "Available matplotlib backends: %s"
100 % _list_matplotlib_backends_and_gui_loops()
101 )
107 102 else:
108 103 gui, backend = self.shell.enable_matplotlib(args.gui.lower() if isinstance(args.gui, str) else args.gui)
109 104 self._show_matplotlib_backend(args.gui, backend)
110 105
111 106 @skip_doctest
112 107 @line_magic
113 108 @magic_arguments.magic_arguments()
114 109 @magic_arguments.argument(
115 110 '--no-import-all', action='store_true', default=None,
116 111 help="""Prevent IPython from performing ``import *`` into the interactive namespace.
117 112
118 113 You can govern the default behavior of this flag with the
119 114 InteractiveShellApp.pylab_import_all configurable.
120 115 """
121 116 )
122 117 @magic_gui_arg
123 118 def pylab(self, line=''):
124 119 """Load numpy and matplotlib to work interactively.
125 120
126 121 This function lets you activate pylab (matplotlib, numpy and
127 122 interactive support) at any point during an IPython session.
128 123
129 124 %pylab makes the following imports::
130 125
131 126 import numpy
132 127 import matplotlib
133 128 from matplotlib import pylab, mlab, pyplot
134 129 np = numpy
135 130 plt = pyplot
136 131
137 132 from IPython.display import display
138 133 from IPython.core.pylabtools import figsize, getfigs
139 134
140 135 from pylab import *
141 136 from numpy import *
142 137
143 138 If you pass `--no-import-all`, the last two `*` imports will be excluded.
144 139
145 140 See the %matplotlib magic for more details about activating matplotlib
146 141 without affecting the interactive namespace.
147 142 """
148 143 args = magic_arguments.parse_argstring(self.pylab, line)
149 144 if args.no_import_all is None:
150 145 # get default from Application
151 146 if Application.initialized():
152 147 app = Application.instance()
153 148 try:
154 149 import_all = app.pylab_import_all
155 150 except AttributeError:
156 151 import_all = True
157 152 else:
158 153 # nothing specified, no app - default True
159 154 import_all = True
160 155 else:
161 156 # invert no-import flag
162 157 import_all = not args.no_import_all
163 158
164 159 gui, backend, clobbered = self.shell.enable_pylab(args.gui, import_all=import_all)
165 160 self._show_matplotlib_backend(args.gui, backend)
166 161 print(
167 162 "%pylab is deprecated, use %matplotlib inline and import the required libraries."
168 163 )
169 164 print("Populating the interactive namespace from numpy and matplotlib")
170 165 if clobbered:
171 166 warn("pylab import has clobbered these variables: %s" % clobbered +
172 167 "\n`%matplotlib` prevents importing * from pylab and numpy"
173 168 )
174 169
175 170 def _show_matplotlib_backend(self, gui, backend):
176 171 """show matplotlib message backend message"""
177 172 if not gui or gui == 'auto':
178 173 print("Using matplotlib backend: %s" % backend)
@@ -1,499 +1,518 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Pylab (matplotlib) support utilities."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 from io import BytesIO
8 8 from binascii import b2a_base64
9 9 from functools import partial
10 10 import warnings
11 11
12 12 from IPython.core.display import _pngxy
13 13 from IPython.utils.decorators import flag_calls
14 14
15 15
16 16 # Matplotlib backend resolution functionality moved from IPython to Matplotlib
17 17 # in IPython 8.24 and Matplotlib 3.9.1. Need to keep `backends` and `backend2gui`
18 18 # here for earlier Matplotlib and for external backend libraries such as
19 19 # mplcairo that might rely upon it.
20 20 _deprecated_backends = {
21 21 "tk": "TkAgg",
22 22 "gtk": "GTKAgg",
23 23 "gtk3": "GTK3Agg",
24 24 "gtk4": "GTK4Agg",
25 25 "wx": "WXAgg",
26 26 "qt4": "Qt4Agg",
27 27 "qt5": "Qt5Agg",
28 28 "qt6": "QtAgg",
29 29 "qt": "QtAgg",
30 30 "osx": "MacOSX",
31 31 "nbagg": "nbAgg",
32 32 "webagg": "WebAgg",
33 33 "notebook": "nbAgg",
34 34 "agg": "agg",
35 35 "svg": "svg",
36 36 "pdf": "pdf",
37 37 "ps": "ps",
38 38 "inline": "module://matplotlib_inline.backend_inline",
39 39 "ipympl": "module://ipympl.backend_nbagg",
40 40 "widget": "module://ipympl.backend_nbagg",
41 41 }
42 42
43 43 # We also need a reverse backends2guis mapping that will properly choose which
44 44 # GUI support to activate based on the desired matplotlib backend. For the
45 45 # most part it's just a reverse of the above dict, but we also need to add a
46 46 # few others that map to the same GUI manually:
47 47 _deprecated_backend2gui = dict(
48 48 zip(_deprecated_backends.values(), _deprecated_backends.keys())
49 49 )
50 50 # In the reverse mapping, there are a few extra valid matplotlib backends that
51 51 # map to the same GUI support
52 52 _deprecated_backend2gui["GTK"] = _deprecated_backend2gui["GTKCairo"] = "gtk"
53 53 _deprecated_backend2gui["GTK3Cairo"] = "gtk3"
54 54 _deprecated_backend2gui["GTK4Cairo"] = "gtk4"
55 55 _deprecated_backend2gui["WX"] = "wx"
56 56 _deprecated_backend2gui["CocoaAgg"] = "osx"
57 57 # There needs to be a hysteresis here as the new QtAgg Matplotlib backend
58 58 # supports either Qt5 or Qt6 and the IPython qt event loop support Qt4, Qt5,
59 59 # and Qt6.
60 60 _deprecated_backend2gui["QtAgg"] = "qt"
61 61 _deprecated_backend2gui["Qt4Agg"] = "qt4"
62 62 _deprecated_backend2gui["Qt5Agg"] = "qt5"
63 63
64 64 # And some backends that don't need GUI integration
65 65 del _deprecated_backend2gui["nbAgg"]
66 66 del _deprecated_backend2gui["agg"]
67 67 del _deprecated_backend2gui["svg"]
68 68 del _deprecated_backend2gui["pdf"]
69 69 del _deprecated_backend2gui["ps"]
70 70 del _deprecated_backend2gui["module://matplotlib_inline.backend_inline"]
71 71 del _deprecated_backend2gui["module://ipympl.backend_nbagg"]
72 72
73 73
74 74 # Deprecated attributes backends and backend2gui mostly following PEP 562.
75 75 def __getattr__(name):
76 76 if name in ("backends", "backend2gui"):
77 77 warnings.warn(
78 78 f"{name} is deprecated since IPython 8.24, backends are managed "
79 79 "in matplotlib and can be externally registered.",
80 80 DeprecationWarning,
81 81 )
82 82 return globals()[f"_deprecated_{name}"]
83 83 raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
84 84
85 85
86 86 #-----------------------------------------------------------------------------
87 87 # Matplotlib utilities
88 88 #-----------------------------------------------------------------------------
89 89
90 90
91 91 def getfigs(*fig_nums):
92 92 """Get a list of matplotlib figures by figure numbers.
93 93
94 94 If no arguments are given, all available figures are returned. If the
95 95 argument list contains references to invalid figures, a warning is printed
96 96 but the function continues pasting further figures.
97 97
98 98 Parameters
99 99 ----------
100 100 figs : tuple
101 101 A tuple of ints giving the figure numbers of the figures to return.
102 102 """
103 103 from matplotlib._pylab_helpers import Gcf
104 104 if not fig_nums:
105 105 fig_managers = Gcf.get_all_fig_managers()
106 106 return [fm.canvas.figure for fm in fig_managers]
107 107 else:
108 108 figs = []
109 109 for num in fig_nums:
110 110 f = Gcf.figs.get(num)
111 111 if f is None:
112 112 print('Warning: figure %s not available.' % num)
113 113 else:
114 114 figs.append(f.canvas.figure)
115 115 return figs
116 116
117 117
118 118 def figsize(sizex, sizey):
119 119 """Set the default figure size to be [sizex, sizey].
120 120
121 121 This is just an easy to remember, convenience wrapper that sets::
122 122
123 123 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
124 124 """
125 125 import matplotlib
126 126 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
127 127
128 128
129 129 def print_figure(fig, fmt="png", bbox_inches="tight", base64=False, **kwargs):
130 130 """Print a figure to an image, and return the resulting file data
131 131
132 132 Returned data will be bytes unless ``fmt='svg'``,
133 133 in which case it will be unicode.
134 134
135 135 Any keyword args are passed to fig.canvas.print_figure,
136 136 such as ``quality`` or ``bbox_inches``.
137 137
138 138 If `base64` is True, return base64-encoded str instead of raw bytes
139 139 for binary-encoded image formats
140 140
141 141 .. versionadded:: 7.29
142 142 base64 argument
143 143 """
144 144 # When there's an empty figure, we shouldn't return anything, otherwise we
145 145 # get big blank areas in the qt console.
146 146 if not fig.axes and not fig.lines:
147 147 return
148 148
149 149 dpi = fig.dpi
150 150 if fmt == 'retina':
151 151 dpi = dpi * 2
152 152 fmt = 'png'
153 153
154 154 # build keyword args
155 155 kw = {
156 156 "format":fmt,
157 157 "facecolor":fig.get_facecolor(),
158 158 "edgecolor":fig.get_edgecolor(),
159 159 "dpi":dpi,
160 160 "bbox_inches":bbox_inches,
161 161 }
162 162 # **kwargs get higher priority
163 163 kw.update(kwargs)
164 164
165 165 bytes_io = BytesIO()
166 166 if fig.canvas is None:
167 167 from matplotlib.backend_bases import FigureCanvasBase
168 168 FigureCanvasBase(fig)
169 169
170 170 fig.canvas.print_figure(bytes_io, **kw)
171 171 data = bytes_io.getvalue()
172 172 if fmt == 'svg':
173 173 data = data.decode('utf-8')
174 174 elif base64:
175 175 data = b2a_base64(data, newline=False).decode("ascii")
176 176 return data
177 177
178 178 def retina_figure(fig, base64=False, **kwargs):
179 179 """format a figure as a pixel-doubled (retina) PNG
180 180
181 181 If `base64` is True, return base64-encoded str instead of raw bytes
182 182 for binary-encoded image formats
183 183
184 184 .. versionadded:: 7.29
185 185 base64 argument
186 186 """
187 187 pngdata = print_figure(fig, fmt="retina", base64=False, **kwargs)
188 188 # Make sure that retina_figure acts just like print_figure and returns
189 189 # None when the figure is empty.
190 190 if pngdata is None:
191 191 return
192 192 w, h = _pngxy(pngdata)
193 193 metadata = {"width": w//2, "height":h//2}
194 194 if base64:
195 195 pngdata = b2a_base64(pngdata, newline=False).decode("ascii")
196 196 return pngdata, metadata
197 197
198 198
199 199 # We need a little factory function here to create the closure where
200 200 # safe_execfile can live.
201 201 def mpl_runner(safe_execfile):
202 202 """Factory to return a matplotlib-enabled runner for %run.
203 203
204 204 Parameters
205 205 ----------
206 206 safe_execfile : function
207 207 This must be a function with the same interface as the
208 208 :meth:`safe_execfile` method of IPython.
209 209
210 210 Returns
211 211 -------
212 212 A function suitable for use as the ``runner`` argument of the %run magic
213 213 function.
214 214 """
215 215
216 216 def mpl_execfile(fname,*where,**kw):
217 217 """matplotlib-aware wrapper around safe_execfile.
218 218
219 219 Its interface is identical to that of the :func:`execfile` builtin.
220 220
221 221 This is ultimately a call to execfile(), but wrapped in safeties to
222 222 properly handle interactive rendering."""
223 223
224 224 import matplotlib
225 225 import matplotlib.pyplot as plt
226 226
227 227 #print '*** Matplotlib runner ***' # dbg
228 228 # turn off rendering until end of script
229 229 with matplotlib.rc_context({"interactive": False}):
230 230 safe_execfile(fname, *where, **kw)
231 231
232 232 if matplotlib.is_interactive():
233 233 plt.show()
234 234
235 235 # make rendering call now, if the user tried to do it
236 236 if plt.draw_if_interactive.called:
237 237 plt.draw()
238 238 plt.draw_if_interactive.called = False
239 239
240 240 # re-draw everything that is stale
241 241 try:
242 242 da = plt.draw_all
243 243 except AttributeError:
244 244 pass
245 245 else:
246 246 da()
247 247
248 248 return mpl_execfile
249 249
250 250
251 251 def _reshow_nbagg_figure(fig):
252 252 """reshow an nbagg figure"""
253 253 try:
254 254 reshow = fig.canvas.manager.reshow
255 255 except AttributeError as e:
256 256 raise NotImplementedError() from e
257 257 else:
258 258 reshow()
259 259
260 260
261 261 def select_figure_formats(shell, formats, **kwargs):
262 262 """Select figure formats for the inline backend.
263 263
264 264 Parameters
265 265 ----------
266 266 shell : InteractiveShell
267 267 The main IPython instance.
268 268 formats : str or set
269 269 One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
270 270 **kwargs : any
271 271 Extra keyword arguments to be passed to fig.canvas.print_figure.
272 272 """
273 273 import matplotlib
274 274 from matplotlib.figure import Figure
275 275
276 276 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
277 277 png_formatter = shell.display_formatter.formatters['image/png']
278 278 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
279 279 pdf_formatter = shell.display_formatter.formatters['application/pdf']
280 280
281 281 if isinstance(formats, str):
282 282 formats = {formats}
283 283 # cast in case of list / tuple
284 284 formats = set(formats)
285 285
286 286 [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]
287 287 mplbackend = matplotlib.get_backend().lower()
288 288 if mplbackend in ("nbagg", "ipympl", "widget", "module://ipympl.backend_nbagg"):
289 289 formatter = shell.display_formatter.ipython_display_formatter
290 290 formatter.for_type(Figure, _reshow_nbagg_figure)
291 291
292 292 supported = {'png', 'png2x', 'retina', 'jpg', 'jpeg', 'svg', 'pdf'}
293 293 bad = formats.difference(supported)
294 294 if bad:
295 295 bs = "%s" % ','.join([repr(f) for f in bad])
296 296 gs = "%s" % ','.join([repr(f) for f in supported])
297 297 raise ValueError("supported formats are: %s not %s" % (gs, bs))
298 298
299 299 if "png" in formats:
300 300 png_formatter.for_type(
301 301 Figure, partial(print_figure, fmt="png", base64=True, **kwargs)
302 302 )
303 303 if "retina" in formats or "png2x" in formats:
304 304 png_formatter.for_type(Figure, partial(retina_figure, base64=True, **kwargs))
305 305 if "jpg" in formats or "jpeg" in formats:
306 306 jpg_formatter.for_type(
307 307 Figure, partial(print_figure, fmt="jpg", base64=True, **kwargs)
308 308 )
309 309 if "svg" in formats:
310 310 svg_formatter.for_type(Figure, partial(print_figure, fmt="svg", **kwargs))
311 311 if "pdf" in formats:
312 312 pdf_formatter.for_type(
313 313 Figure, partial(print_figure, fmt="pdf", base64=True, **kwargs)
314 314 )
315 315
316 316 #-----------------------------------------------------------------------------
317 317 # Code for initializing matplotlib and importing pylab
318 318 #-----------------------------------------------------------------------------
319 319
320 320
321 321 def find_gui_and_backend(gui=None, gui_select=None):
322 322 """Given a gui string return the gui and mpl backend.
323 323
324 324 Parameters
325 325 ----------
326 326 gui : str
327 327 Can be one of ('tk','gtk','wx','qt','qt4','inline','agg').
328 328 gui_select : str
329 329 Can be one of ('tk','gtk','wx','qt','qt4','inline').
330 330 This is any gui already selected by the shell.
331 331
332 332 Returns
333 333 -------
334 334 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
335 335 'WXAgg','Qt4Agg','module://matplotlib_inline.backend_inline','agg').
336 336 """
337 337
338 338 import matplotlib
339 339
340 340 if _matplotlib_manages_backends():
341 341 backend_registry = matplotlib.backends.registry.backend_registry
342 342
343 343 # gui argument may be a gui event loop or may be a backend name.
344 344 if gui in ("auto", None):
345 345 backend = matplotlib.rcParamsOrig["backend"]
346 346 backend, gui = backend_registry.resolve_backend(backend)
347 347 else:
348 348 backend, gui = backend_registry.resolve_gui_or_backend(gui)
349 349
350 350 return gui, backend
351 351
352 352 # Fallback to previous behaviour (Matplotlib < 3.9)
353 353 mpl_version_info = getattr(matplotlib, "__version_info__", (0, 0))
354 354 has_unified_qt_backend = mpl_version_info >= (3, 5)
355 355
356 356 from IPython.core.pylabtools import backends
357 357
358 358 backends_ = dict(backends)
359 359 if not has_unified_qt_backend:
360 360 backends_["qt"] = "qt5agg"
361 361
362 362 if gui and gui != 'auto':
363 363 # select backend based on requested gui
364 364 backend = backends_[gui]
365 365 if gui == 'agg':
366 366 gui = None
367 367 else:
368 368 # We need to read the backend from the original data structure, *not*
369 369 # from mpl.rcParams, since a prior invocation of %matplotlib may have
370 370 # overwritten that.
371 371 # WARNING: this assumes matplotlib 1.1 or newer!!
372 372 backend = matplotlib.rcParamsOrig['backend']
373 373 # In this case, we need to find what the appropriate gui selection call
374 374 # should be for IPython, so we can activate inputhook accordingly
375 375 from IPython.core.pylabtools import backend2gui
376 376 gui = backend2gui.get(backend, None)
377 377
378 378 # If we have already had a gui active, we need it and inline are the
379 379 # ones allowed.
380 380 if gui_select and gui != gui_select:
381 381 gui = gui_select
382 382 backend = backends_[gui]
383 383
384 384 # Matplotlib before _matplotlib_manages_backends() can return "inline" for
385 385 # no gui event loop rather than the None that IPython >= 8.24.0 expects.
386 386 if gui == "inline":
387 387 gui = None
388 388
389 389 return gui, backend
390 390
391 391
392 392 def activate_matplotlib(backend):
393 393 """Activate the given backend and set interactive to True."""
394 394
395 395 import matplotlib
396 396 matplotlib.interactive(True)
397 397
398 398 # Matplotlib had a bug where even switch_backend could not force
399 399 # the rcParam to update. This needs to be set *before* the module
400 400 # magic of switch_backend().
401 401 matplotlib.rcParams['backend'] = backend
402 402
403 403 # Due to circular imports, pyplot may be only partially initialised
404 404 # when this function runs.
405 405 # So avoid needing matplotlib attribute-lookup to access pyplot.
406 406 from matplotlib import pyplot as plt
407 407
408 408 plt.switch_backend(backend)
409 409
410 410 plt.show._needmain = False
411 411 # We need to detect at runtime whether show() is called by the user.
412 412 # For this, we wrap it into a decorator which adds a 'called' flag.
413 413 plt.draw_if_interactive = flag_calls(plt.draw_if_interactive)
414 414
415 415
416 416 def import_pylab(user_ns, import_all=True):
417 417 """Populate the namespace with pylab-related values.
418 418
419 419 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
420 420
421 421 Also imports a few names from IPython (figsize, display, getfigs)
422 422
423 423 """
424 424
425 425 # Import numpy as np/pyplot as plt are conventions we're trying to
426 426 # somewhat standardize on. Making them available to users by default
427 427 # will greatly help this.
428 428 s = ("import numpy\n"
429 429 "import matplotlib\n"
430 430 "from matplotlib import pylab, mlab, pyplot\n"
431 431 "np = numpy\n"
432 432 "plt = pyplot\n"
433 433 )
434 434 exec(s, user_ns)
435 435
436 436 if import_all:
437 437 s = ("from matplotlib.pylab import *\n"
438 438 "from numpy import *\n")
439 439 exec(s, user_ns)
440 440
441 441 # IPython symbols to add
442 442 user_ns['figsize'] = figsize
443 443 from IPython.display import display
444 444 # Add display and getfigs to the user's namespace
445 445 user_ns['display'] = display
446 446 user_ns['getfigs'] = getfigs
447 447
448 448
449 449 def configure_inline_support(shell, backend):
450 450 """
451 451 .. deprecated:: 7.23
452 452
453 453 use `matplotlib_inline.backend_inline.configure_inline_support()`
454 454
455 455 Configure an IPython shell object for matplotlib use.
456 456
457 457 Parameters
458 458 ----------
459 459 shell : InteractiveShell instance
460 460 backend : matplotlib backend
461 461 """
462 462 warnings.warn(
463 463 "`configure_inline_support` is deprecated since IPython 7.23, directly "
464 464 "use `matplotlib_inline.backend_inline.configure_inline_support()`",
465 465 DeprecationWarning,
466 466 stacklevel=2,
467 467 )
468 468
469 469 from matplotlib_inline.backend_inline import (
470 470 configure_inline_support as configure_inline_support_orig,
471 471 )
472 472
473 473 configure_inline_support_orig(shell, backend)
474 474
475 475
476 476 # Determine if Matplotlib manages backends only if needed, and cache result.
477 477 # Do not read this directly, instead use _matplotlib_manages_backends().
478 478 _matplotlib_manages_backends_value: bool | None = None
479 479
480 480
481 481 def _matplotlib_manages_backends() -> bool:
482 482 """Return True if Matplotlib manages backends, False otherwise.
483 483
484 484 If it returns True, the caller can be sure that
485 485 matplotlib.backends.registry.backend_registry is available along with
486 member functions resolve_gui_or_backend, resolve_backend and list_all.
486 member functions resolve_gui_or_backend, resolve_backend, list_all, and
487 list_gui_frameworks.
487 488 """
488 489 global _matplotlib_manages_backends_value
489 490 if _matplotlib_manages_backends_value is None:
490 491 try:
491 492 from matplotlib.backends.registry import backend_registry
492 493
493 494 _matplotlib_manages_backends_value = hasattr(
494 495 backend_registry, "resolve_gui_or_backend"
495 496 )
496 497 except ImportError:
497 498 _matplotlib_manages_backends_value = False
498 499
499 500 return _matplotlib_manages_backends_value
501
502
503 def _list_matplotlib_backends_and_gui_loops() -> list[str]:
504 """Return list of all Matplotlib backends and GUI event loops.
505
506 This is the list returned by
507 %matplotlib --list
508 """
509 if _matplotlib_manages_backends():
510 from matplotlib.backends.registry import backend_registry
511
512 ret = backend_registry.list_all() + backend_registry.list_gui_frameworks()
513 else:
514 from IPython.core import pylabtools
515
516 ret = list(pylabtools.backends.keys())
517
518 return sorted(["auto"] + ret)
@@ -1,450 +1,491 b''
1 1 # encoding: utf-8
2 2 """
3 3 A mixin for :class:`~IPython.core.application.Application` classes that
4 4 launch InteractiveShell instances, load extensions, etc.
5 5 """
6 6
7 7 # Copyright (c) IPython Development Team.
8 8 # Distributed under the terms of the Modified BSD License.
9 9
10 10 import glob
11 11 from itertools import chain
12 12 import os
13 13 import sys
14 import typing as t
14 15
15 16 from traitlets.config.application import boolean_flag
16 17 from traitlets.config.configurable import Configurable
17 18 from traitlets.config.loader import Config
18 19 from IPython.core.application import SYSTEM_CONFIG_DIRS, ENV_CONFIG_DIRS
19 from IPython.core import pylabtools
20 20 from IPython.utils.contexts import preserve_keys
21 21 from IPython.utils.path import filefind
22 22 from traitlets import (
23 Unicode, Instance, List, Bool, CaselessStrEnum, observe,
23 Unicode,
24 Instance,
25 List,
26 Bool,
27 CaselessStrEnum,
28 observe,
24 29 DottedObjectName,
30 Undefined,
25 31 )
26 32 from IPython.terminal import pt_inputhooks
27 33
28 #-----------------------------------------------------------------------------
34 # -----------------------------------------------------------------------------
29 35 # Aliases and Flags
30 #-----------------------------------------------------------------------------
36 # -----------------------------------------------------------------------------
31 37
32 38 gui_keys = tuple(sorted(pt_inputhooks.backends) + sorted(pt_inputhooks.aliases))
33 39
34 backend_keys: list[str] = []
35
36 40 shell_flags = {}
37 41
38 42 addflag = lambda *args: shell_flags.update(boolean_flag(*args))
39 addflag('autoindent', 'InteractiveShell.autoindent',
40 'Turn on autoindenting.', 'Turn off autoindenting.'
43 addflag(
44 "autoindent",
45 "InteractiveShell.autoindent",
46 "Turn on autoindenting.",
47 "Turn off autoindenting.",
41 48 )
42 addflag('automagic', 'InteractiveShell.automagic',
43 """Turn on the auto calling of magic commands. Type %%magic at the
49 addflag(
50 "automagic",
51 "InteractiveShell.automagic",
52 """Turn on the auto calling of magic commands. Type %%magic at the
44 53 IPython prompt for more information.""",
45 54 'Turn off the auto calling of magic commands.'
46 55 )
47 56 addflag('pdb', 'InteractiveShell.pdb',
48 57 "Enable auto calling the pdb debugger after every exception.",
49 58 "Disable auto calling the pdb debugger after every exception."
50 59 )
51 60 addflag('pprint', 'PlainTextFormatter.pprint',
52 61 "Enable auto pretty printing of results.",
53 62 "Disable auto pretty printing of results."
54 63 )
55 64 addflag('color-info', 'InteractiveShell.color_info',
56 65 """IPython can display information about objects via a set of functions,
57 66 and optionally can use colors for this, syntax highlighting
58 67 source code and various other elements. This is on by default, but can cause
59 68 problems with some pagers. If you see such problems, you can disable the
60 69 colours.""",
61 70 "Disable using colors for info related things."
62 71 )
63 72 addflag('ignore-cwd', 'InteractiveShellApp.ignore_cwd',
64 73 "Exclude the current working directory from sys.path",
65 74 "Include the current working directory in sys.path",
66 75 )
67 76 nosep_config = Config()
68 77 nosep_config.InteractiveShell.separate_in = ''
69 78 nosep_config.InteractiveShell.separate_out = ''
70 79 nosep_config.InteractiveShell.separate_out2 = ''
71 80
72 81 shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.")
73 82 shell_flags['pylab'] = (
74 83 {'InteractiveShellApp' : {'pylab' : 'auto'}},
75 84 """Pre-load matplotlib and numpy for interactive use with
76 85 the default matplotlib backend."""
77 86 )
78 87 shell_flags['matplotlib'] = (
79 88 {'InteractiveShellApp' : {'matplotlib' : 'auto'}},
80 89 """Configure matplotlib for interactive use with
81 90 the default matplotlib backend."""
82 91 )
83 92
84 93 # it's possible we don't want short aliases for *all* of these:
85 94 shell_aliases = dict(
86 95 autocall='InteractiveShell.autocall',
87 96 colors='InteractiveShell.colors',
88 97 logfile='InteractiveShell.logfile',
89 98 logappend='InteractiveShell.logappend',
90 99 c='InteractiveShellApp.code_to_run',
91 100 m='InteractiveShellApp.module_to_run',
92 101 ext="InteractiveShellApp.extra_extensions",
93 102 gui='InteractiveShellApp.gui',
94 103 pylab='InteractiveShellApp.pylab',
95 104 matplotlib='InteractiveShellApp.matplotlib',
96 105 )
97 106 shell_aliases['cache-size'] = 'InteractiveShell.cache_size'
98 107
108
109 # -----------------------------------------------------------------------------
110 # Traitlets
111 # -----------------------------------------------------------------------------
112
113
114 class MatplotlibBackendCaselessStrEnum(CaselessStrEnum):
115 """An enum of Matplotlib backend strings where the case should be ignored.
116
117 Prior to Matplotlib 3.9.1 the list of valid backends is hardcoded in
118 pylabtools.backends. After that, Matplotlib manages backends.
119
120 The list of valid backends is determined when it is first needed to avoid
121 wasting unnecessary initialisation time.
122 """
123
124 def __init__(
125 self: CaselessStrEnum[t.Any],
126 default_value: t.Any = Undefined,
127 **kwargs: t.Any,
128 ) -> None:
129 super().__init__(None, default_value=default_value, **kwargs)
130
131 def __getattribute__(self, name):
132 if name == "values" and object.__getattribute__(self, name) is None:
133 from IPython.core.pylabtools import _list_matplotlib_backends_and_gui_loops
134
135 self.values = _list_matplotlib_backends_and_gui_loops()
136 return object.__getattribute__(self, name)
137
138
99 139 #-----------------------------------------------------------------------------
100 140 # Main classes and functions
101 141 #-----------------------------------------------------------------------------
102 142
103 143 class InteractiveShellApp(Configurable):
104 144 """A Mixin for applications that start InteractiveShell instances.
105 145
106 146 Provides configurables for loading extensions and executing files
107 147 as part of configuring a Shell environment.
108 148
109 149 The following methods should be called by the :meth:`initialize` method
110 150 of the subclass:
111 151
112 152 - :meth:`init_path`
113 153 - :meth:`init_shell` (to be implemented by the subclass)
114 154 - :meth:`init_gui_pylab`
115 155 - :meth:`init_extensions`
116 156 - :meth:`init_code`
117 157 """
118 158 extensions = List(Unicode(),
119 159 help="A list of dotted module names of IPython extensions to load."
120 160 ).tag(config=True)
121 161
122 162 extra_extensions = List(
123 163 DottedObjectName(),
124 164 help="""
125 165 Dotted module name(s) of one or more IPython extensions to load.
126 166
127 167 For specifying extra extensions to load on the command-line.
128 168
129 169 .. versionadded:: 7.10
130 170 """,
131 171 ).tag(config=True)
132 172
133 173 reraise_ipython_extension_failures = Bool(False,
134 174 help="Reraise exceptions encountered loading IPython extensions?",
135 175 ).tag(config=True)
136 176
137 177 # Extensions that are always loaded (not configurable)
138 178 default_extensions = List(Unicode(), [u'storemagic']).tag(config=False)
139 179
140 180 hide_initial_ns = Bool(True,
141 181 help="""Should variables loaded at startup (by startup files, exec_lines, etc.)
142 182 be hidden from tools like %who?"""
143 183 ).tag(config=True)
144 184
145 185 exec_files = List(Unicode(),
146 186 help="""List of files to run at IPython startup."""
147 187 ).tag(config=True)
148 188 exec_PYTHONSTARTUP = Bool(True,
149 189 help="""Run the file referenced by the PYTHONSTARTUP environment
150 190 variable at IPython startup."""
151 191 ).tag(config=True)
152 192 file_to_run = Unicode('',
153 193 help="""A file to be run""").tag(config=True)
154 194
155 195 exec_lines = List(Unicode(),
156 196 help="""lines of code to run at IPython startup."""
157 197 ).tag(config=True)
158 code_to_run = Unicode('',
159 help="Execute the given command string."
160 ).tag(config=True)
161 module_to_run = Unicode('',
162 help="Run the module as a script."
198 code_to_run = Unicode("", help="Execute the given command string.").tag(config=True)
199 module_to_run = Unicode("", help="Run the module as a script.").tag(config=True)
200 gui = CaselessStrEnum(
201 gui_keys,
202 allow_none=True,
203 help="Enable GUI event loop integration with any of {0}.".format(gui_keys),
163 204 ).tag(config=True)
164 gui = CaselessStrEnum(gui_keys, allow_none=True,
165 help="Enable GUI event loop integration with any of {0}.".format(gui_keys)
166 ).tag(config=True)
167 matplotlib = CaselessStrEnum(backend_keys, allow_none=True,
205 matplotlib = MatplotlibBackendCaselessStrEnum(
206 allow_none=True,
168 207 help="""Configure matplotlib for interactive use with
169 the default matplotlib backend."""
208 the default matplotlib backend.""",
170 209 ).tag(config=True)
171 pylab = CaselessStrEnum(backend_keys, allow_none=True,
210 pylab = MatplotlibBackendCaselessStrEnum(
211 allow_none=True,
172 212 help="""Pre-load matplotlib and numpy for interactive use,
173 213 selecting a particular matplotlib backend and loop integration.
174 """
214 """,
175 215 ).tag(config=True)
176 pylab_import_all = Bool(True,
216 pylab_import_all = Bool(
217 True,
177 218 help="""If true, IPython will populate the user namespace with numpy, pylab, etc.
178 219 and an ``import *`` is done from numpy and pylab, when using pylab mode.
179 220
180 221 When False, pylab mode should not import any names into the user namespace.
181 """
222 """,
182 223 ).tag(config=True)
183 224 ignore_cwd = Bool(
184 225 False,
185 226 help="""If True, IPython will not add the current working directory to sys.path.
186 227 When False, the current working directory is added to sys.path, allowing imports
187 228 of modules defined in the current directory."""
188 229 ).tag(config=True)
189 230 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
190 231 allow_none=True)
191 232 # whether interact-loop should start
192 233 interact = Bool(True)
193 234
194 235 user_ns = Instance(dict, args=None, allow_none=True)
195 236 @observe('user_ns')
196 237 def _user_ns_changed(self, change):
197 238 if self.shell is not None:
198 239 self.shell.user_ns = change['new']
199 240 self.shell.init_user_ns()
200 241
201 242 def init_path(self):
202 243 """Add current working directory, '', to sys.path
203 244
204 245 Unlike Python's default, we insert before the first `site-packages`
205 246 or `dist-packages` directory,
206 247 so that it is after the standard library.
207 248
208 249 .. versionchanged:: 7.2
209 250 Try to insert after the standard library, instead of first.
210 251 .. versionchanged:: 8.0
211 252 Allow optionally not including the current directory in sys.path
212 253 """
213 254 if '' in sys.path or self.ignore_cwd:
214 255 return
215 256 for idx, path in enumerate(sys.path):
216 257 parent, last_part = os.path.split(path)
217 258 if last_part in {'site-packages', 'dist-packages'}:
218 259 break
219 260 else:
220 261 # no site-packages or dist-packages found (?!)
221 262 # back to original behavior of inserting at the front
222 263 idx = 0
223 264 sys.path.insert(idx, '')
224 265
225 266 def init_shell(self):
226 267 raise NotImplementedError("Override in subclasses")
227 268
228 269 def init_gui_pylab(self):
229 270 """Enable GUI event loop integration, taking pylab into account."""
230 271 enable = False
231 272 shell = self.shell
232 273 if self.pylab:
233 274 enable = lambda key: shell.enable_pylab(key, import_all=self.pylab_import_all)
234 275 key = self.pylab
235 276 elif self.matplotlib:
236 277 enable = shell.enable_matplotlib
237 278 key = self.matplotlib
238 279 elif self.gui:
239 280 enable = shell.enable_gui
240 281 key = self.gui
241 282
242 283 if not enable:
243 284 return
244 285
245 286 try:
246 287 r = enable(key)
247 288 except ImportError:
248 289 self.log.warning("Eventloop or matplotlib integration failed. Is matplotlib installed?")
249 290 self.shell.showtraceback()
250 291 return
251 292 except Exception:
252 293 self.log.warning("GUI event loop or pylab initialization failed")
253 294 self.shell.showtraceback()
254 295 return
255 296
256 297 if isinstance(r, tuple):
257 298 gui, backend = r[:2]
258 299 self.log.info("Enabling GUI event loop integration, "
259 300 "eventloop=%s, matplotlib=%s", gui, backend)
260 301 if key == "auto":
261 302 print("Using matplotlib backend: %s" % backend)
262 303 else:
263 304 gui = r
264 305 self.log.info("Enabling GUI event loop integration, "
265 306 "eventloop=%s", gui)
266 307
267 308 def init_extensions(self):
268 309 """Load all IPython extensions in IPythonApp.extensions.
269 310
270 311 This uses the :meth:`ExtensionManager.load_extensions` to load all
271 312 the extensions listed in ``self.extensions``.
272 313 """
273 314 try:
274 315 self.log.debug("Loading IPython extensions...")
275 316 extensions = (
276 317 self.default_extensions + self.extensions + self.extra_extensions
277 318 )
278 319 for ext in extensions:
279 320 try:
280 321 self.log.info("Loading IPython extension: %s", ext)
281 322 self.shell.extension_manager.load_extension(ext)
282 323 except:
283 324 if self.reraise_ipython_extension_failures:
284 325 raise
285 326 msg = ("Error in loading extension: {ext}\n"
286 327 "Check your config files in {location}".format(
287 328 ext=ext,
288 329 location=self.profile_dir.location
289 330 ))
290 331 self.log.warning(msg, exc_info=True)
291 332 except:
292 333 if self.reraise_ipython_extension_failures:
293 334 raise
294 335 self.log.warning("Unknown error in loading extensions:", exc_info=True)
295 336
296 337 def init_code(self):
297 338 """run the pre-flight code, specified via exec_lines"""
298 339 self._run_startup_files()
299 340 self._run_exec_lines()
300 341 self._run_exec_files()
301 342
302 343 # Hide variables defined here from %who etc.
303 344 if self.hide_initial_ns:
304 345 self.shell.user_ns_hidden.update(self.shell.user_ns)
305 346
306 347 # command-line execution (ipython -i script.py, ipython -m module)
307 348 # should *not* be excluded from %whos
308 349 self._run_cmd_line_code()
309 350 self._run_module()
310 351
311 352 # flush output, so itwon't be attached to the first cell
312 353 sys.stdout.flush()
313 354 sys.stderr.flush()
314 355 self.shell._sys_modules_keys = set(sys.modules.keys())
315 356
316 357 def _run_exec_lines(self):
317 358 """Run lines of code in IPythonApp.exec_lines in the user's namespace."""
318 359 if not self.exec_lines:
319 360 return
320 361 try:
321 362 self.log.debug("Running code from IPythonApp.exec_lines...")
322 363 for line in self.exec_lines:
323 364 try:
324 365 self.log.info("Running code in user namespace: %s" %
325 366 line)
326 367 self.shell.run_cell(line, store_history=False)
327 368 except:
328 369 self.log.warning("Error in executing line in user "
329 370 "namespace: %s" % line)
330 371 self.shell.showtraceback()
331 372 except:
332 373 self.log.warning("Unknown error in handling IPythonApp.exec_lines:")
333 374 self.shell.showtraceback()
334 375
335 376 def _exec_file(self, fname, shell_futures=False):
336 377 try:
337 378 full_filename = filefind(fname, [u'.', self.ipython_dir])
338 379 except IOError:
339 380 self.log.warning("File not found: %r"%fname)
340 381 return
341 382 # Make sure that the running script gets a proper sys.argv as if it
342 383 # were run from a system shell.
343 384 save_argv = sys.argv
344 385 sys.argv = [full_filename] + self.extra_args[1:]
345 386 try:
346 387 if os.path.isfile(full_filename):
347 388 self.log.info("Running file in user namespace: %s" %
348 389 full_filename)
349 390 # Ensure that __file__ is always defined to match Python
350 391 # behavior.
351 392 with preserve_keys(self.shell.user_ns, '__file__'):
352 393 self.shell.user_ns['__file__'] = fname
353 394 if full_filename.endswith('.ipy') or full_filename.endswith('.ipynb'):
354 395 self.shell.safe_execfile_ipy(full_filename,
355 396 shell_futures=shell_futures)
356 397 else:
357 398 # default to python, even without extension
358 399 self.shell.safe_execfile(full_filename,
359 400 self.shell.user_ns,
360 401 shell_futures=shell_futures,
361 402 raise_exceptions=True)
362 403 finally:
363 404 sys.argv = save_argv
364 405
365 406 def _run_startup_files(self):
366 407 """Run files from profile startup directory"""
367 408 startup_dirs = [self.profile_dir.startup_dir] + [
368 409 os.path.join(p, 'startup') for p in chain(ENV_CONFIG_DIRS, SYSTEM_CONFIG_DIRS)
369 410 ]
370 411 startup_files = []
371 412
372 413 if self.exec_PYTHONSTARTUP and os.environ.get('PYTHONSTARTUP', False) and \
373 414 not (self.file_to_run or self.code_to_run or self.module_to_run):
374 415 python_startup = os.environ['PYTHONSTARTUP']
375 416 self.log.debug("Running PYTHONSTARTUP file %s...", python_startup)
376 417 try:
377 418 self._exec_file(python_startup)
378 419 except:
379 420 self.log.warning("Unknown error in handling PYTHONSTARTUP file %s:", python_startup)
380 421 self.shell.showtraceback()
381 422 for startup_dir in startup_dirs[::-1]:
382 423 startup_files += glob.glob(os.path.join(startup_dir, '*.py'))
383 424 startup_files += glob.glob(os.path.join(startup_dir, '*.ipy'))
384 425 if not startup_files:
385 426 return
386 427
387 428 self.log.debug("Running startup files from %s...", startup_dir)
388 429 try:
389 430 for fname in sorted(startup_files):
390 431 self._exec_file(fname)
391 432 except:
392 433 self.log.warning("Unknown error in handling startup files:")
393 434 self.shell.showtraceback()
394 435
395 436 def _run_exec_files(self):
396 437 """Run files from IPythonApp.exec_files"""
397 438 if not self.exec_files:
398 439 return
399 440
400 441 self.log.debug("Running files in IPythonApp.exec_files...")
401 442 try:
402 443 for fname in self.exec_files:
403 444 self._exec_file(fname)
404 445 except:
405 446 self.log.warning("Unknown error in handling IPythonApp.exec_files:")
406 447 self.shell.showtraceback()
407 448
408 449 def _run_cmd_line_code(self):
409 450 """Run code or file specified at the command-line"""
410 451 if self.code_to_run:
411 452 line = self.code_to_run
412 453 try:
413 454 self.log.info("Running code given at command line (c=): %s" %
414 455 line)
415 456 self.shell.run_cell(line, store_history=False)
416 457 except:
417 458 self.log.warning("Error in executing line in user namespace: %s" %
418 459 line)
419 460 self.shell.showtraceback()
420 461 if not self.interact:
421 462 self.exit(1)
422 463
423 464 # Like Python itself, ignore the second if the first of these is present
424 465 elif self.file_to_run:
425 466 fname = self.file_to_run
426 467 if os.path.isdir(fname):
427 468 fname = os.path.join(fname, "__main__.py")
428 469 if not os.path.exists(fname):
429 470 self.log.warning("File '%s' doesn't exist", fname)
430 471 if not self.interact:
431 472 self.exit(2)
432 473 try:
433 474 self._exec_file(fname, shell_futures=True)
434 475 except:
435 476 self.shell.showtraceback(tb_offset=4)
436 477 if not self.interact:
437 478 self.exit(1)
438 479
439 480 def _run_module(self):
440 481 """Run module specified at the command-line."""
441 482 if self.module_to_run:
442 483 # Make sure that the module gets a proper sys.argv as if it were
443 484 # run using `python -m`.
444 485 save_argv = sys.argv
445 486 sys.argv = [sys.executable] + self.extra_args
446 487 try:
447 488 self.shell.safe_run_module(self.module_to_run,
448 489 self.shell.user_ns)
449 490 finally:
450 491 sys.argv = save_argv
General Comments 0
You need to be logged in to leave comments. Login now