Format: text/x-python
⬇ Download File (22.7 KB)
   1  """Implementation of basic magic functions."""
   2  
   3  
   4  from logging import error
   5  import io
   6  import os
   7  from pprint import pformat
   8  import sys
   9  from warnings import warn
  10  
  11  from traitlets.utils.importstring import import_item
  12  from IPython.core import magic_arguments, page
  13  from IPython.core.error import UsageError
  14  from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes
  15  from IPython.utils.text import format_screen, dedent, indent
  16  from IPython.testing.skipdoctest import skip_doctest
  17  from IPython.utils.ipstruct import Struct
  18  
  19  
  20  class MagicsDisplay(object):
  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
  74  class 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 = ["""
 231  IPython's 'magic' functions
 232  ===========================
 233  
 234  The magic function system provides a series of functions which allow you to
 235  control the behavior of IPython itself, plus a lot of system-type
 236  features. There are two kinds of magics, line-oriented and cell-oriented.
 237  
 238  Line magics are prefixed with the % character and work much like OS
 239  command-line calls: they get as an argument the rest of the line, where
 240  arguments are passed without parentheses or quotes.  For example, this will
 241  time the given statement::
 242  
 243          %timeit range(1000)
 244  
 245  Cell magics are prefixed with a double %%, and they are functions that get as
 246  an argument not only the rest of the line, but also the lines below it in a
 247  separate argument.  These magics are called with two arguments: the rest of the
 248  call line and the body of the cell, consisting of the lines below the first.
 249  For example::
 250  
 251          %%timeit x = numpy.random.randn((100, 100))
 252          numpy.linalg.svd(x)
 253  
 254  will time the execution of the numpy svd routine, running the assignment of x
 255  as part of the setup phase, which is not timed.
 256  
 257  In a line-oriented client (the terminal or Qt console IPython), starting a new
 258  input with %% will automatically enter cell mode, and IPython will continue
 259  reading input until a blank line is given.  In the notebook, simply type the
 260  whole cell as one entity, but keep in mind that the %% escape can only be at
 261  the very start of the cell.
 262  
 263  NOTE: 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
 265  magics; cell magics always require an explicit '%%' escape.  By default,
 266  IPython ships with automagic on, so you should only rarely need the % escape.
 267  
 268  Example: typing '%cd mydir' (without the quotes) changes your working directory
 269  to 'mydir', if it exists.
 270  
 271  For a list of the available magic functions, use %lsmagic. For a description
 272  of any of them, type %magic_name?, e.g. '%cd?'.
 273  
 274  Currently 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 for prompts, info system and exception handlers.
 322  
 323          Currently implemented schemes: NoColor, Linux, LightBG.
 324  
 325          Color scheme names are not case-sensitive.
 326  
 327          Examples
 328          --------
 329          To get a plain black and white terminal::
 330  
 331            %colors nocolor
 332          """
 333          def color_switch_err(name):
 334              warn('Error changing %s color schemes.\n%s' %
 335                   (name, sys.exc_info()[1]), stacklevel=2)
 336  
 337  
 338          new_scheme = parameter_s.strip()
 339          if not new_scheme:
 340              raise UsageError(
 341                  "%colors: you must specify a color scheme. See '%colors?'")
 342          # local shortcut
 343          shell = self.shell
 344  
 345          # Set shell colour scheme
 346          try:
 347              shell.colors = new_scheme
 348              shell.refresh_style()
 349          except:
 350              color_switch_err('shell')
 351  
 352          # Set exception colors
 353          try:
 354              shell.InteractiveTB.set_colors(scheme = new_scheme)
 355              shell.SyntaxTB.set_colors(scheme = new_scheme)
 356          except:
 357              color_switch_err('exception')
 358  
 359          # Set info (for 'object?') colors
 360          if shell.color_info:
 361              try:
 362                  shell.inspector.set_active_scheme(new_scheme)
 363              except:
 364                  color_switch_err('object inspector')
 365          else:
 366              shell.inspector.set_active_scheme('NoColor')
 367  
 368      @line_magic
 369      def xmode(self, parameter_s=''):
 370          """Switch modes for the exception handlers.
 371  
 372          Valid modes: Plain, Context, Verbose, and Minimal.
 373  
 374          If called without arguments, acts as a toggle.
 375  
 376          When in verbose mode the value `--show` (and `--hide`)
 377          will respectively show (or hide) frames with ``__tracebackhide__ =
 378          True`` value set.
 379          """
 380  
 381          def xmode_switch_err(name):
 382              warn('Error changing %s exception modes.\n%s' %
 383                   (name,sys.exc_info()[1]))
 384  
 385          shell = self.shell
 386          if parameter_s.strip() == "--show":
 387              shell.InteractiveTB.skip_hidden = False
 388              return
 389          if parameter_s.strip() == "--hide":
 390              shell.InteractiveTB.skip_hidden = True
 391              return
 392  
 393          new_mode = parameter_s.strip().capitalize()
 394          try:
 395              shell.InteractiveTB.set_mode(mode=new_mode)
 396              print('Exception reporting mode:',shell.InteractiveTB.mode)
 397          except:
 398              xmode_switch_err('user')
 399  
 400      @line_magic
 401      def quickref(self, arg):
 402          """ Show a quick reference sheet """
 403          from IPython.core.usage import quick_reference
 404          qr = quick_reference + self._magic_docs(brief=True)
 405          page.page(qr)
 406  
 407      @line_magic
 408      def doctest_mode(self, parameter_s=''):
 409          """Toggle doctest mode on and off.
 410  
 411          This mode is intended to make IPython behave as much as possible like a
 412          plain Python shell, from the perspective of how its prompts, exceptions
 413          and output look.  This makes it easy to copy and paste parts of a
 414          session into doctests.  It does so by:
 415  
 416          - Changing the prompts to the classic ``>>>`` ones.
 417          - Changing the exception reporting mode to 'Plain'.
 418          - Disabling pretty-printing of output.
 419  
 420          Note that IPython also supports the pasting of code snippets that have
 421          leading '>>>' and '...' prompts in them.  This means that you can paste
 422          doctests from files or docstrings (even if they have leading
 423          whitespace), and the code will execute correctly.  You can then use
 424          '%history -t' to see the translated history; this will give you the
 425          input after removal of all the leading prompts and whitespace, which
 426          can be pasted back into an editor.
 427  
 428          With these features, you can switch into this mode easily whenever you
 429          need to do testing and changes to doctests, without having to leave
 430          your existing IPython session.
 431          """
 432  
 433          # Shorthands
 434          shell = self.shell
 435          meta = shell.meta
 436          disp_formatter = self.shell.display_formatter
 437          ptformatter = disp_formatter.formatters['text/plain']
 438          # dstore is a data store kept in the instance metadata bag to track any
 439          # changes we make, so we can undo them later.
 440          dstore = meta.setdefault('doctest_mode',Struct())
 441          save_dstore = dstore.setdefault
 442  
 443          # save a few values we'll need to recover later
 444          mode = save_dstore('mode',False)
 445          save_dstore('rc_pprint',ptformatter.pprint)
 446          save_dstore('xmode',shell.InteractiveTB.mode)
 447          save_dstore('rc_separate_out',shell.separate_out)
 448          save_dstore('rc_separate_out2',shell.separate_out2)
 449          save_dstore('rc_separate_in',shell.separate_in)
 450          save_dstore('rc_active_types',disp_formatter.active_types)
 451  
 452          if not mode:
 453              # turn on
 454  
 455              # Prompt separators like plain python
 456              shell.separate_in = ''
 457              shell.separate_out = ''
 458              shell.separate_out2 = ''
 459  
 460  
 461              ptformatter.pprint = False
 462              disp_formatter.active_types = ['text/plain']
 463  
 464              shell.run_line_magic("xmode", "Plain")
 465          else:
 466              # turn off
 467              shell.separate_in = dstore.rc_separate_in
 468  
 469              shell.separate_out = dstore.rc_separate_out
 470              shell.separate_out2 = dstore.rc_separate_out2
 471  
 472              ptformatter.pprint = dstore.rc_pprint
 473              disp_formatter.active_types = dstore.rc_active_types
 474  
 475              shell.run_line_magic("xmode", dstore.xmode)
 476  
 477          # mode here is the state before we switch; switch_doctest_mode takes
 478          # the mode we're switching to.
 479          shell.switch_doctest_mode(not mode)
 480  
 481          # Store new mode and inform
 482          dstore.mode = bool(not mode)
 483          mode_label = ['OFF','ON'][dstore.mode]
 484          print('Doctest mode is:', mode_label)
 485  
 486      @line_magic
 487      def gui(self, parameter_s=''):
 488          """Enable or disable IPython GUI event loop integration.
 489  
 490          %gui [GUINAME]
 491  
 492          This magic replaces IPython's threaded shells that were activated
 493          using the (pylab/wthread/etc.) command line flags.  GUI toolkits
 494          can now be enabled at runtime and keyboard
 495          interrupts should work without any problems.  The following toolkits
 496          are supported:  wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::
 497  
 498              %gui wx      # enable wxPython event loop integration
 499              %gui qt      # enable PyQt/PySide event loop integration
 500                           # with the latest version available.
 501              %gui qt6     # enable PyQt6/PySide6 event loop integration
 502              %gui qt5     # enable PyQt5/PySide2 event loop integration
 503              %gui gtk     # enable PyGTK event loop integration
 504              %gui gtk3    # enable Gtk3 event loop integration
 505              %gui gtk4    # enable Gtk4 event loop integration
 506              %gui tk      # enable Tk event loop integration
 507              %gui osx     # enable Cocoa event loop integration
 508                           # (requires %matplotlib 1.1)
 509              %gui         # disable all event loop integration
 510  
 511          WARNING:  after any of these has been called you can simply create
 512          an application object, but DO NOT start the event loop yourself, as
 513          we have already handled that.
 514          """
 515          opts, arg = self.parse_options(parameter_s, '')
 516          if arg=='': arg = None
 517          try:
 518              return self.shell.enable_gui(arg)
 519          except Exception as e:
 520              # print simple error message, rather than traceback if we can't
 521              # hook up the GUI
 522              error(str(e))
 523  
 524      @skip_doctest
 525      @line_magic
 526      def precision(self, s=''):
 527          """Set floating point precision for pretty printing.
 528  
 529          Can set either integer precision or a format string.
 530  
 531          If numpy has been imported and precision is an int,
 532          numpy display precision will also be set, via ``numpy.set_printoptions``.
 533  
 534          If no argument is given, defaults will be restored.
 535  
 536          Examples
 537          --------
 538          ::
 539  
 540              In [1]: from math import pi
 541  
 542              In [2]: %precision 3
 543              Out[2]: '%.3f'
 544  
 545              In [3]: pi
 546              Out[3]: 3.142
 547  
 548              In [4]: %precision %i
 549              Out[4]: '%i'
 550  
 551              In [5]: pi
 552              Out[5]: 3
 553  
 554              In [6]: %precision %e
 555              Out[6]: '%e'
 556  
 557              In [7]: pi**10
 558              Out[7]: 9.364805e+04
 559  
 560              In [8]: %precision
 561              Out[8]: '%r'
 562  
 563              In [9]: pi**10
 564              Out[9]: 93648.047476082982
 565          """
 566          ptformatter = self.shell.display_formatter.formatters['text/plain']
 567          ptformatter.float_precision = s
 568          return ptformatter.float_format
 569  
 570      @magic_arguments.magic_arguments()
 571      @magic_arguments.argument(
 572          'filename', type=str,
 573          help='Notebook name or filename'
 574      )
 575      @line_magic
 576      def notebook(self, s):
 577          """Export and convert IPython notebooks.
 578  
 579          This function can export the current IPython history to a notebook file.
 580          For example, to export the history to "foo.ipynb" do "%notebook foo.ipynb".
 581          """
 582          args = magic_arguments.parse_argstring(self.notebook, s)
 583          outfname = os.path.expanduser(args.filename)
 584  
 585          from nbformat import write, v4
 586  
 587          cells = []
 588          hist = list(self.shell.history_manager.get_range())
 589          if(len(hist)<=1):
 590              raise ValueError('History is empty, cannot export')
 591          for session, execution_count, source in hist[:-1]:
 592              cells.append(v4.new_code_cell(
 593                  execution_count=execution_count,
 594                  source=source
 595              ))
 596          nb = v4.new_notebook(cells=cells)
 597          with io.open(outfname, "w", encoding="utf-8") as f:
 598              write(nb, f, version=4)
 599  
 600  @magics_class
 601  class AsyncMagics(BasicMagics):
 602  
 603      @line_magic
 604      def autoawait(self, parameter_s):
 605          """
 606          Allow to change the status of the autoawait option.
 607  
 608          This allow you to set a specific asynchronous code runner.
 609  
 610          If no value is passed, print the currently used asynchronous integration
 611          and whether it is activated.
 612  
 613          It can take a number of value evaluated in the following order:
 614  
 615          - False/false/off deactivate autoawait integration
 616          - True/true/on activate autoawait integration using configured default
 617            loop
 618          - asyncio/curio/trio activate autoawait integration and use integration
 619            with said library.
 620  
 621          - `sync` turn on the pseudo-sync integration (mostly used for
 622            `IPython.embed()` which does not run IPython with a real eventloop and
 623            deactivate running asynchronous code. Turning on Asynchronous code with
 624            the pseudo sync loop is undefined behavior and may lead IPython to crash.
 625  
 626          If the passed parameter does not match any of the above and is a python
 627          identifier, get said object from user namespace and set it as the
 628          runner, and activate autoawait.
 629  
 630          If the object is a fully qualified object name, attempt to import it and
 631          set it as the runner, and activate autoawait.
 632  
 633          The exact behavior of autoawait is experimental and subject to change
 634          across version of IPython and Python.
 635          """
 636  
 637          param = parameter_s.strip()
 638          d = {True: "on", False: "off"}
 639  
 640          if not param:
 641              print("IPython autoawait is `{}`, and set to use `{}`".format(
 642                  d[self.shell.autoawait],
 643                  self.shell.loop_runner
 644              ))
 645              return None
 646  
 647          if param.lower() in ('false', 'off'):
 648              self.shell.autoawait = False
 649              return None
 650          if param.lower() in ('true', 'on'):
 651              self.shell.autoawait = True
 652              return None
 653  
 654          if param in self.shell.loop_runner_map:
 655              self.shell.loop_runner, self.shell.autoawait = self.shell.loop_runner_map[param]
 656              return None
 657  
 658          if param in self.shell.user_ns :
 659              self.shell.loop_runner = self.shell.user_ns[param]
 660              self.shell.autoawait = True
 661              return None
 662  
 663          runner = import_item(param)
 664  
 665          self.shell.loop_runner = runner
 666          self.shell.autoawait = True
 667