1"""Implementation of basic magic functions."""
2
3
4from logging import error
5import io
6import os
7from pprint import pformat
8import sys
9from warnings import warn
10
11from traitlets.utils.importstring import import_item
12from IPython.core import magic_arguments, page
13from IPython.core.error import UsageError
14from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes
15from IPython.utils.text import format_screen, dedent, indent
16from IPython.testing.skipdoctest import skip_doctest
17from IPython.utils.ipstruct import Struct
18
19
20class MagicsDisplay:
21 def __init__(self, magics_manager, ignore=None):
22 self.ignore = ignore if ignore else []
23 self.magics_manager = magics_manager
24
25 def _lsmagic(self):
26 """The main implementation of the %lsmagic"""
27 mesc = magic_escapes['line']
28 cesc = magic_escapes['cell']
29 mman = self.magics_manager
30 magics = mman.lsmagic()
31 out = ['Available line magics:',
32 mesc + (' '+mesc).join(sorted([m for m,v in magics['line'].items() if (v not in self.ignore)])),
33 '',
34 'Available cell magics:',
35 cesc + (' '+cesc).join(sorted([m for m,v in magics['cell'].items() if (v not in self.ignore)])),
36 '',
37 mman.auto_status()]
38 return '\n'.join(out)
39
40 def _repr_pretty_(self, p, cycle):
41 p.text(self._lsmagic())
42
43 def __repr__(self):
44 return self.__str__()
45
46 def __str__(self):
47 return self._lsmagic()
48
49 def _jsonable(self):
50 """turn magics dict into jsonable dict of the same structure
51
52 replaces object instances with their class names as strings
53 """
54 magic_dict = {}
55 mman = self.magics_manager
56 magics = mman.lsmagic()
57 for key, subdict in magics.items():
58 d = {}
59 magic_dict[key] = d
60 for name, obj in subdict.items():
61 try:
62 classname = obj.__self__.__class__.__name__
63 except AttributeError:
64 classname = 'Other'
65
66 d[name] = classname
67 return magic_dict
68
69 def _repr_json_(self):
70 return self._jsonable()
71
72
73@magics_class
74class BasicMagics(Magics):
75 """Magics that provide central IPython functionality.
76
77 These are various magics that don't fit into specific categories but that
78 are all part of the base 'IPython experience'."""
79
80 @skip_doctest
81 @magic_arguments.magic_arguments()
82 @magic_arguments.argument(
83 '-l', '--line', action='store_true',
84 help="""Create a line magic alias."""
85 )
86 @magic_arguments.argument(
87 '-c', '--cell', action='store_true',
88 help="""Create a cell magic alias."""
89 )
90 @magic_arguments.argument(
91 'name',
92 help="""Name of the magic to be created."""
93 )
94 @magic_arguments.argument(
95 'target',
96 help="""Name of the existing line or cell magic."""
97 )
98 @magic_arguments.argument(
99 '-p', '--params', default=None,
100 help="""Parameters passed to the magic function."""
101 )
102 @line_magic
103 def alias_magic(self, line=''):
104 """Create an alias for an existing line or cell magic.
105
106 Examples
107 --------
108 ::
109
110 In [1]: %alias_magic t timeit
111 Created `%t` as an alias for `%timeit`.
112 Created `%%t` as an alias for `%%timeit`.
113
114 In [2]: %t -n1 pass
115 107 ns ± 43.6 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
116
117 In [3]: %%t -n1
118 ...: pass
119 ...:
120 107 ns ± 58.3 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
121
122 In [4]: %alias_magic --cell whereami pwd
123 UsageError: Cell magic function `%%pwd` not found.
124 In [5]: %alias_magic --line whereami pwd
125 Created `%whereami` as an alias for `%pwd`.
126
127 In [6]: %whereami
128 Out[6]: '/home/testuser'
129
130 In [7]: %alias_magic h history -p "-l 30" --line
131 Created `%h` as an alias for `%history -l 30`.
132 """
133
134 args = magic_arguments.parse_argstring(self.alias_magic, line)
135 shell = self.shell
136 mman = self.shell.magics_manager
137 escs = ''.join(magic_escapes.values())
138
139 target = args.target.lstrip(escs)
140 name = args.name.lstrip(escs)
141
142 params = args.params
143 if (params and
144 ((params.startswith('"') and params.endswith('"'))
145 or (params.startswith("'") and params.endswith("'")))):
146 params = params[1:-1]
147
148 # Find the requested magics.
149 m_line = shell.find_magic(target, 'line')
150 m_cell = shell.find_magic(target, 'cell')
151 if args.line and m_line is None:
152 raise UsageError('Line magic function `%s%s` not found.' %
153 (magic_escapes['line'], target))
154 if args.cell and m_cell is None:
155 raise UsageError('Cell magic function `%s%s` not found.' %
156 (magic_escapes['cell'], target))
157
158 # If --line and --cell are not specified, default to the ones
159 # that are available.
160 if not args.line and not args.cell:
161 if not m_line and not m_cell:
162 raise UsageError(
163 'No line or cell magic with name `%s` found.' % target
164 )
165 args.line = bool(m_line)
166 args.cell = bool(m_cell)
167
168 params_str = "" if params is None else " " + params
169
170 if args.line:
171 mman.register_alias(name, target, 'line', params)
172 print('Created `%s%s` as an alias for `%s%s%s`.' % (
173 magic_escapes['line'], name,
174 magic_escapes['line'], target, params_str))
175
176 if args.cell:
177 mman.register_alias(name, target, 'cell', params)
178 print('Created `%s%s` as an alias for `%s%s%s`.' % (
179 magic_escapes['cell'], name,
180 magic_escapes['cell'], target, params_str))
181
182 @line_magic
183 def lsmagic(self, parameter_s=''):
184 """List currently available magic functions."""
185 return MagicsDisplay(self.shell.magics_manager, ignore=[])
186
187 def _magic_docs(self, brief=False, rest=False):
188 """Return docstrings from magic functions."""
189 mman = self.shell.magics_manager
190 docs = mman.lsmagic_docs(brief, missing='No documentation')
191
192 if rest:
193 format_string = '**%s%s**::\n\n%s\n\n'
194 else:
195 format_string = '%s%s:\n%s\n'
196
197 return ''.join(
198 [format_string % (magic_escapes['line'], fname,
199 indent(dedent(fndoc)))
200 for fname, fndoc in sorted(docs['line'].items())]
201 +
202 [format_string % (magic_escapes['cell'], fname,
203 indent(dedent(fndoc)))
204 for fname, fndoc in sorted(docs['cell'].items())]
205 )
206
207 @line_magic
208 def magic(self, parameter_s=''):
209 """Print information about the magic function system.
210
211 Supported formats: -latex, -brief, -rest
212 """
213
214 mode = ''
215 try:
216 mode = parameter_s.split()[0][1:]
217 except IndexError:
218 pass
219
220 brief = (mode == 'brief')
221 rest = (mode == 'rest')
222 magic_docs = self._magic_docs(brief, rest)
223
224 if mode == 'latex':
225 print(self.format_latex(magic_docs))
226 return
227 else:
228 magic_docs = format_screen(magic_docs)
229
230 out = ["""
231IPython's 'magic' functions
232===========================
233
234The magic function system provides a series of functions which allow you to
235control the behavior of IPython itself, plus a lot of system-type
236features. There are two kinds of magics, line-oriented and cell-oriented.
237
238Line magics are prefixed with the % character and work much like OS
239command-line calls: they get as an argument the rest of the line, where
240arguments are passed without parentheses or quotes. For example, this will
241time the given statement::
242
243 %timeit range(1000)
244
245Cell magics are prefixed with a double %%, and they are functions that get as
246an argument not only the rest of the line, but also the lines below it in a
247separate argument. These magics are called with two arguments: the rest of the
248call line and the body of the cell, consisting of the lines below the first.
249For example::
250
251 %%timeit x = numpy.random.randn((100, 100))
252 numpy.linalg.svd(x)
253
254will time the execution of the numpy svd routine, running the assignment of x
255as part of the setup phase, which is not timed.
256
257In a line-oriented client (the terminal or Qt console IPython), starting a new
258input with %% will automatically enter cell mode, and IPython will continue
259reading input until a blank line is given. In the notebook, simply type the
260whole cell as one entity, but keep in mind that the %% escape can only be at
261the very start of the cell.
262
263NOTE: If you have 'automagic' enabled (via the command line option or with the
264%automagic function), you don't need to type in the % explicitly for line
265magics; cell magics always require an explicit '%%' escape. By default,
266IPython ships with automagic on, so you should only rarely need the % escape.
267
268Example: typing '%cd mydir' (without the quotes) changes your working directory
269to 'mydir', if it exists.
270
271For a list of the available magic functions, use %lsmagic. For a description
272of any of them, type %magic_name?, e.g. '%cd?'.
273
274Currently the magic system has the following functions:""",
275 magic_docs,
276 "Summary of magic functions (from %slsmagic):" % magic_escapes['line'],
277 str(self.lsmagic()),
278 ]
279 page.page('\n'.join(out))
280
281
282 @line_magic
283 def page(self, parameter_s=''):
284 """Pretty print the object and display it through a pager.
285
286 %page [options] OBJECT
287
288 If no object is given, use _ (last output).
289
290 Options:
291
292 -r: page str(object), don't pretty-print it."""
293
294 # After a function contributed by Olivier Aubert, slightly modified.
295
296 # Process options/args
297 opts, args = self.parse_options(parameter_s, 'r')
298 raw = 'r' in opts
299
300 oname = args and args or '_'
301 info = self.shell._ofind(oname)
302 if info.found:
303 if raw:
304 txt = str(info.obj)
305 else:
306 txt = pformat(info.obj)
307 page.page(txt)
308 else:
309 print('Object `%s` not found' % oname)
310
311 @line_magic
312 def pprint(self, parameter_s=''):
313 """Toggle pretty printing on/off."""
314 ptformatter = self.shell.display_formatter.formatters['text/plain']
315 ptformatter.pprint = bool(1 - ptformatter.pprint)
316 print('Pretty printing has been turned',
317 ['OFF','ON'][ptformatter.pprint])
318
319 @line_magic
320 def colors(self, parameter_s=''):
321 """Switch color scheme/theme globally for IPython
322
323 Examples
324 --------
325 To get a plain black and white terminal::
326
327 %colors nocolor
328 """
329
330
331 new_theme = parameter_s.strip()
332 if not new_theme:
333 from IPython.utils.PyColorize import theme_table
334
335 raise UsageError(
336 "%colors: you must specify a color theme. See '%colors?'."
337 f" Available themes: {list(theme_table.keys())}"
338 )
339
340 self.shell.colors = new_theme
341
342 @line_magic
343 def xmode(self, parameter_s=''):
344 """Switch modes for the exception handlers.
345
346 Valid modes: Plain, Context, Verbose, and Minimal.
347
348 If called without arguments, acts as a toggle.
349
350 When in verbose mode the value `--show` (and `--hide`)
351 will respectively show (or hide) frames with ``__tracebackhide__ =
352 True`` value set.
353 """
354
355 def xmode_switch_err(name):
356 warn('Error changing %s exception modes.\n%s' %
357 (name,sys.exc_info()[1]))
358
359 shell = self.shell
360 if parameter_s.strip() == "--show":
361 shell.InteractiveTB.skip_hidden = False
362 return
363 if parameter_s.strip() == "--hide":
364 shell.InteractiveTB.skip_hidden = True
365 return
366
367 new_mode = parameter_s.strip().capitalize()
368 try:
369 shell.InteractiveTB.set_mode(mode=new_mode)
370 print('Exception reporting mode:',shell.InteractiveTB.mode)
371 except:
372 raise
373 xmode_switch_err('user')
374
375 @line_magic
376 def quickref(self, arg):
377 """ Show a quick reference sheet """
378 from IPython.core.usage import quick_reference
379 qr = quick_reference + self._magic_docs(brief=True)
380 page.page(qr)
381
382 @line_magic
383 def doctest_mode(self, parameter_s=''):
384 """Toggle doctest mode on and off.
385
386 This mode is intended to make IPython behave as much as possible like a
387 plain Python shell, from the perspective of how its prompts, exceptions
388 and output look. This makes it easy to copy and paste parts of a
389 session into doctests. It does so by:
390
391 - Changing the prompts to the classic ``>>>`` ones.
392 - Changing the exception reporting mode to 'Plain'.
393 - Disabling pretty-printing of output.
394
395 Note that IPython also supports the pasting of code snippets that have
396 leading '>>>' and '...' prompts in them. This means that you can paste
397 doctests from files or docstrings (even if they have leading
398 whitespace), and the code will execute correctly. You can then use
399 '%history -t' to see the translated history; this will give you the
400 input after removal of all the leading prompts and whitespace, which
401 can be pasted back into an editor.
402
403 With these features, you can switch into this mode easily whenever you
404 need to do testing and changes to doctests, without having to leave
405 your existing IPython session.
406 """
407
408 # Shorthands
409 shell = self.shell
410 meta = shell.meta
411 disp_formatter = self.shell.display_formatter
412 ptformatter = disp_formatter.formatters['text/plain']
413 # dstore is a data store kept in the instance metadata bag to track any
414 # changes we make, so we can undo them later.
415 dstore = meta.setdefault('doctest_mode',Struct())
416 save_dstore = dstore.setdefault
417
418 # save a few values we'll need to recover later
419 mode = save_dstore('mode',False)
420 save_dstore('rc_pprint',ptformatter.pprint)
421 save_dstore('xmode',shell.InteractiveTB.mode)
422 save_dstore('rc_separate_out',shell.separate_out)
423 save_dstore('rc_separate_out2',shell.separate_out2)
424 save_dstore('rc_separate_in',shell.separate_in)
425 save_dstore('rc_active_types',disp_formatter.active_types)
426
427 if not mode:
428 # turn on
429
430 # Prompt separators like plain python
431 shell.separate_in = ''
432 shell.separate_out = ''
433 shell.separate_out2 = ''
434
435
436 ptformatter.pprint = False
437 disp_formatter.active_types = ['text/plain']
438
439 shell.run_line_magic("xmode", "Plain")
440 else:
441 # turn off
442 shell.separate_in = dstore.rc_separate_in
443
444 shell.separate_out = dstore.rc_separate_out
445 shell.separate_out2 = dstore.rc_separate_out2
446
447 ptformatter.pprint = dstore.rc_pprint
448 disp_formatter.active_types = dstore.rc_active_types
449
450 shell.run_line_magic("xmode", dstore.xmode)
451
452 # mode here is the state before we switch; switch_doctest_mode takes
453 # the mode we're switching to.
454 shell.switch_doctest_mode(not mode)
455
456 # Store new mode and inform
457 dstore.mode = bool(not mode)
458 mode_label = ['OFF','ON'][dstore.mode]
459 print('Doctest mode is:', mode_label)
460
461 @line_magic
462 def gui(self, parameter_s=''):
463 """Enable or disable IPython GUI event loop integration.
464
465 %gui [GUINAME]
466
467 This magic replaces IPython's threaded shells that were activated
468 using the (pylab/wthread/etc.) command line flags. GUI toolkits
469 can now be enabled at runtime and keyboard
470 interrupts should work without any problems. The following toolkits
471 are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::
472
473 %gui wx # enable wxPython event loop integration
474 %gui qt # enable PyQt/PySide event loop integration
475 # with the latest version available.
476 %gui qt6 # enable PyQt6/PySide6 event loop integration
477 %gui qt5 # enable PyQt5/PySide2 event loop integration
478 %gui gtk # enable PyGTK event loop integration
479 %gui gtk3 # enable Gtk3 event loop integration
480 %gui gtk4 # enable Gtk4 event loop integration
481 %gui tk # enable Tk event loop integration
482 %gui osx # enable Cocoa event loop integration
483 # (requires %matplotlib 1.1)
484 %gui # disable all event loop integration
485
486 WARNING: after any of these has been called you can simply create
487 an application object, but DO NOT start the event loop yourself, as
488 we have already handled that.
489 """
490 opts, arg = self.parse_options(parameter_s, '')
491 if arg=='': arg = None
492 try:
493 return self.shell.enable_gui(arg)
494 except Exception as e:
495 # print simple error message, rather than traceback if we can't
496 # hook up the GUI
497 error(str(e))
498
499 @skip_doctest
500 @line_magic
501 def precision(self, s=''):
502 """Set floating point precision for pretty printing.
503
504 Can set either integer precision or a format string.
505
506 If numpy has been imported and precision is an int,
507 numpy display precision will also be set, via ``numpy.set_printoptions``.
508
509 If no argument is given, defaults will be restored.
510
511 Examples
512 --------
513 ::
514
515 In [1]: from math import pi
516
517 In [2]: %precision 3
518 Out[2]: '%.3f'
519
520 In [3]: pi
521 Out[3]: 3.142
522
523 In [4]: %precision %i
524 Out[4]: '%i'
525
526 In [5]: pi
527 Out[5]: 3
528
529 In [6]: %precision %e
530 Out[6]: '%e'
531
532 In [7]: pi**10
533 Out[7]: 9.364805e+04
534
535 In [8]: %precision
536 Out[8]: '%r'
537
538 In [9]: pi**10
539 Out[9]: 93648.047476082982
540 """
541 ptformatter = self.shell.display_formatter.formatters['text/plain']
542 ptformatter.float_precision = s
543 return ptformatter.float_format
544
545 @magic_arguments.magic_arguments()
546 @magic_arguments.argument(
547 'filename', type=str,
548 help='Notebook name or filename'
549 )
550 @line_magic
551 def notebook(self, s):
552 """Export and convert IPython notebooks.
553
554 This function can export the current IPython history to a notebook file.
555 For example, to export the history to "foo.ipynb" do "%notebook foo.ipynb".
556 """
557 args = magic_arguments.parse_argstring(self.notebook, s)
558 outfname = os.path.expanduser(args.filename)
559
560 from nbformat import write, v4
561
562 cells = []
563 hist = list(self.shell.history_manager.get_range())
564 outputs = self.shell.history_manager.outputs
565 exceptions = self.shell.history_manager.exceptions
566
567 if(len(hist)<=1):
568 raise ValueError('History is empty, cannot export')
569 for session, execution_count, source in hist[:-1]:
570 cell = v4.new_code_cell(execution_count=execution_count, source=source)
571 for output in outputs[execution_count]:
572 for mime_type, data in output.bundle.items():
573 if output.output_type == "out_stream":
574 cell.outputs.append(v4.new_output("stream", text=[data]))
575 elif output.output_type == "err_stream":
576 err_output = v4.new_output("stream", text=[data])
577 err_output.name = "stderr"
578 cell.outputs.append(err_output)
579 elif output.output_type == "execute_result":
580 cell.outputs.append(
581 v4.new_output(
582 "execute_result",
583 data={mime_type: data},
584 execution_count=execution_count,
585 )
586 )
587 elif output.output_type == "display_data":
588 cell.outputs.append(
589 v4.new_output(
590 "display_data",
591 data={mime_type: data},
592 )
593 )
594 else:
595 raise ValueError(f"Unknown output type: {output.output_type}")
596
597 # Check if this execution_count is in exceptions (current session)
598 if execution_count in exceptions:
599 cell.outputs.append(
600 v4.new_output("error", **exceptions[execution_count])
601 )
602 cells.append(cell)
603
604 nb = v4.new_notebook(cells=cells)
605 with io.open(outfname, "w", encoding="utf-8") as f:
606 write(nb, f, version=4)
607
608@magics_class
609class AsyncMagics(BasicMagics):
610
611 @line_magic
612 def autoawait(self, parameter_s):
613 """
614 Allow to change the status of the autoawait option.
615
616 This allow you to set a specific asynchronous code runner.
617
618 If no value is passed, print the currently used asynchronous integration
619 and whether it is activated.
620
621 It can take a number of value evaluated in the following order:
622
623 - False/false/off deactivate autoawait integration
624 - True/true/on activate autoawait integration using configured default
625 loop
626 - asyncio/curio/trio activate autoawait integration and use integration
627 with said library.
628
629 - `sync` turn on the pseudo-sync integration (mostly used for
630 `IPython.embed()` which does not run IPython with a real eventloop and
631 deactivate running asynchronous code. Turning on Asynchronous code with
632 the pseudo sync loop is undefined behavior and may lead IPython to crash.
633
634 If the passed parameter does not match any of the above and is a python
635 identifier, get said object from user namespace and set it as the
636 runner, and activate autoawait.
637
638 If the object is a fully qualified object name, attempt to import it and
639 set it as the runner, and activate autoawait.
640
641 The exact behavior of autoawait is experimental and subject to change
642 across version of IPython and Python.
643 """
644
645 param = parameter_s.strip()
646 d = {True: "on", False: "off"}
647
648 if not param:
649 print("IPython autoawait is `{}`, and set to use `{}`".format(
650 d[self.shell.autoawait],
651 self.shell.loop_runner
652 ))
653 return None
654
655 if param.lower() in ('false', 'off'):
656 self.shell.autoawait = False
657 return None
658 if param.lower() in ('true', 'on'):
659 self.shell.autoawait = True
660 return None
661
662 if param in self.shell.loop_runner_map:
663 self.shell.loop_runner, self.shell.autoawait = self.shell.loop_runner_map[param]
664 return None
665
666 if param in self.shell.user_ns :
667 self.shell.loop_runner = self.shell.user_ns[param]
668 self.shell.autoawait = True
669 return None
670
671 runner = import_item(param)
672
673 self.shell.loop_runner = runner
674 self.shell.autoawait = True