Format: text/x-python
⬇ Download File (7.5 KB)
   1  """Extra magics for terminal use."""
   2  
   3  # Copyright (c) IPython Development Team.
   4  # Distributed under the terms of the Modified BSD License.
   5  
   6  
   7  from logging import error
   8  import os
   9  import sys
  10  
  11  from IPython.core.error import TryNext, UsageError
  12  from IPython.core.magic import Magics, magics_class, line_magic
  13  from IPython.lib.clipboard import ClipboardEmpty
  14  from IPython.testing.skipdoctest import skip_doctest
  15  from IPython.utils.text import SList, strip_email_quotes
  16  from IPython.utils import py3compat
  17  
  18  def get_pasted_lines(sentinel, l_input=py3compat.input, quiet=False):
  19      """ Yield pasted lines until the user enters the given sentinel value.
  20      """
  21      if not quiet:
  22          print("Pasting code; enter '%s' alone on the line to stop or use Ctrl-D." \
  23                % sentinel)
  24          prompt = ":"
  25      else:
  26          prompt = ""
  27      while True:
  28          try:
  29              l = l_input(prompt)
  30              if l == sentinel:
  31                  return
  32              else:
  33                  yield l
  34          except EOFError:
  35              print('<EOF>')
  36              return
  37  
  38  
  39  @magics_class
  40  class TerminalMagics(Magics):
  41      def __init__(self, shell):
  42          super(TerminalMagics, self).__init__(shell)
  43  
  44      def store_or_execute(self, block, name, store_history=False):
  45          """ Execute a block, or store it in a variable, per the user's request.
  46          """
  47          if name:
  48              # If storing it for further editing
  49              self.shell.user_ns[name] = SList(block.splitlines())
  50              print("Block assigned to '%s'" % name)
  51          else:
  52              b = self.preclean_input(block)
  53              self.shell.user_ns['pasted_block'] = b
  54              self.shell.using_paste_magics = True
  55              try:
  56                  self.shell.run_cell(b, store_history)
  57              finally:
  58                  self.shell.using_paste_magics = False
  59  
  60      def preclean_input(self, block):
  61          lines = block.splitlines()
  62          while lines and not lines[0].strip():
  63              lines = lines[1:]
  64          return strip_email_quotes('\n'.join(lines))
  65  
  66      def rerun_pasted(self, name='pasted_block'):
  67          """ Rerun a previously pasted command.
  68          """
  69          b = self.shell.user_ns.get(name)
  70  
  71          # Sanity checks
  72          if b is None:
  73              raise UsageError('No previous pasted block available')
  74          if not isinstance(b, str):
  75              raise UsageError(
  76                  "Variable 'pasted_block' is not a string, can't execute")
  77  
  78          print("Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)))
  79          self.shell.run_cell(b)
  80  
  81      @line_magic
  82      def autoindent(self, parameter_s = ''):
  83          """Toggle autoindent on/off (deprecated)"""
  84          self.shell.set_autoindent()
  85          print("Automatic indentation is:",['OFF','ON'][self.shell.autoindent])
  86  
  87      @skip_doctest
  88      @line_magic
  89      def cpaste(self, parameter_s=''):
  90          """Paste & execute a pre-formatted code block from clipboard.
  91  
  92          You must terminate the block with '--' (two minus-signs) or Ctrl-D
  93          alone on the line. You can also provide your own sentinel with '%paste
  94          -s %%' ('%%' is the new sentinel for this operation).
  95  
  96          The block is dedented prior to execution to enable execution of method
  97          definitions. '>' and '+' characters at the beginning of a line are
  98          ignored, to allow pasting directly from e-mails, diff files and
  99          doctests (the '...' continuation prompt is also stripped).  The
 100          executed block is also assigned to variable named 'pasted_block' for
 101          later editing with '%edit pasted_block'.
 102  
 103          You can also pass a variable name as an argument, e.g. '%cpaste foo'.
 104          This assigns the pasted block to variable 'foo' as string, without
 105          dedenting or executing it (preceding >>> and + is still stripped)
 106  
 107          '%cpaste -r' re-executes the block previously entered by cpaste.
 108          '%cpaste -q' suppresses any additional output messages.
 109  
 110          Do not be alarmed by garbled output on Windows (it's a readline bug).
 111          Just press enter and type -- (and press enter again) and the block
 112          will be what was just pasted.
 113  
 114          Shell escapes are not supported (yet).
 115  
 116          See Also
 117          --------
 118          paste : automatically pull code from clipboard.
 119  
 120          Examples
 121          --------
 122          ::
 123  
 124            In [8]: %cpaste
 125            Pasting code; enter '--' alone on the line to stop.
 126            :>>> a = ["world!", "Hello"]
 127            :>>> print(" ".join(sorted(a)))
 128            :--
 129            Hello world!
 130  
 131          ::
 132            In [8]: %cpaste
 133            Pasting code; enter '--' alone on the line to stop.
 134            :>>> %alias_magic t timeit
 135            :>>> %t -n1 pass
 136            :--
 137            Created `%t` as an alias for `%timeit`.
 138            Created `%%t` as an alias for `%%timeit`.
 139            354 ns ± 224 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
 140          """
 141          opts, name = self.parse_options(parameter_s, 'rqs:', mode='string')
 142          if 'r' in opts:
 143              self.rerun_pasted()
 144              return
 145  
 146          quiet = ('q' in opts)
 147  
 148          sentinel = opts.get('s', u'--')
 149          block = '\n'.join(get_pasted_lines(sentinel, quiet=quiet))
 150          self.store_or_execute(block, name, store_history=True)
 151  
 152      @line_magic
 153      def paste(self, parameter_s=''):
 154          """Paste & execute a pre-formatted code block from clipboard.
 155  
 156          The text is pulled directly from the clipboard without user
 157          intervention and printed back on the screen before execution (unless
 158          the -q flag is given to force quiet mode).
 159  
 160          The block is dedented prior to execution to enable execution of method
 161          definitions. '>' and '+' characters at the beginning of a line are
 162          ignored, to allow pasting directly from e-mails, diff files and
 163          doctests (the '...' continuation prompt is also stripped).  The
 164          executed block is also assigned to variable named 'pasted_block' for
 165          later editing with '%edit pasted_block'.
 166  
 167          You can also pass a variable name as an argument, e.g. '%paste foo'.
 168          This assigns the pasted block to variable 'foo' as string, without
 169          executing it (preceding >>> and + is still stripped).
 170  
 171          Options:
 172  
 173            -r: re-executes the block previously entered by cpaste.
 174  
 175            -q: quiet mode: do not echo the pasted text back to the terminal.
 176  
 177          IPython statements (magics, shell escapes) are not supported (yet).
 178  
 179          See Also
 180          --------
 181          cpaste : manually paste code into terminal until you mark its end.
 182          """
 183          opts, name = self.parse_options(parameter_s, 'rq', mode='string')
 184          if 'r' in opts:
 185              self.rerun_pasted()
 186              return
 187          try:
 188              block = self.shell.hooks.clipboard_get()
 189          except TryNext as clipboard_exc:
 190              message = getattr(clipboard_exc, 'args')
 191              if message:
 192                  error(message[0])
 193              else:
 194                  error('Could not get text from the clipboard.')
 195              return
 196          except ClipboardEmpty as e:
 197              raise UsageError("The clipboard appears to be empty") from e
 198  
 199          # By default, echo back to terminal unless quiet mode is requested
 200          if 'q' not in opts:
 201              sys.stdout.write(self.shell.pycolorize(block))
 202              if not block.endswith("\n"):
 203                  sys.stdout.write("\n")
 204              sys.stdout.write("## -- End pasted text --\n")
 205  
 206          self.store_or_execute(block, name, store_history=True)
 207  
 208      # Class-level: add a '%cls' magic only on Windows
 209      if sys.platform == 'win32':
 210          @line_magic
 211          def cls(self, s):
 212              """Clear screen.
 213              """
 214              os.system("cls")
 215