Format: text/x-python
⬇ Download File (12.3 KB)
   1  """Implementation of magic functions related to History.
   2  """
   3  #-----------------------------------------------------------------------------
   4  #  Copyright (c) 2012, IPython Development Team.
   5  #
   6  #  Distributed under the terms of the Modified BSD License.
   7  #
   8  #  The full license is in the file COPYING.txt, distributed with this software.
   9  #-----------------------------------------------------------------------------
  10  
  11  #-----------------------------------------------------------------------------
  12  # Imports
  13  #-----------------------------------------------------------------------------
  14  
  15  # Stdlib
  16  import os
  17  import sys
  18  from io import open as io_open
  19  import fnmatch
  20  
  21  # Our own packages
  22  from IPython.core.error import StdinNotImplementedError
  23  from IPython.core.magic import Magics, magics_class, line_magic
  24  from IPython.core.magic_arguments import (argument, magic_arguments,
  25                                            parse_argstring)
  26  from IPython.testing.skipdoctest import skip_doctest
  27  from IPython.utils import io
  28  
  29  #-----------------------------------------------------------------------------
  30  # Magics class implementation
  31  #-----------------------------------------------------------------------------
  32  
  33  
  34  _unspecified = object()
  35  
  36  
  37  @magics_class
  38  class HistoryMagics(Magics):
  39  
  40      @magic_arguments()
  41      @argument(
  42          '-n', dest='print_nums', action='store_true', default=False,
  43          help="""
  44          print line numbers for each input.
  45          This feature is only available if numbered prompts are in use.
  46          """)
  47      @argument(
  48          '-o', dest='get_output', action='store_true', default=False,
  49          help="also print outputs for each input.")
  50      @argument(
  51          '-p', dest='pyprompts', action='store_true', default=False,
  52          help="""
  53          print classic '>>>' python prompts before each input.
  54          This is useful for making documentation, and in conjunction
  55          with -o, for producing doctest-ready output.
  56          """)
  57      @argument(
  58          '-t', dest='raw', action='store_false', default=True,
  59          help="""
  60          print the 'translated' history, as IPython understands it.
  61          IPython filters your input and converts it all into valid Python
  62          source before executing it (things like magics or aliases are turned
  63          into function calls, for example). With this option, you'll see the
  64          native history instead of the user-entered version: '%%cd /' will be
  65          seen as 'get_ipython().run_line_magic("cd", "/")' instead of '%%cd /'.
  66          """)
  67      @argument(
  68          '-f', dest='filename',
  69          help="""
  70          FILENAME: instead of printing the output to the screen, redirect
  71          it to the given file.  The file is always overwritten, though *when
  72          it can*, IPython asks for confirmation first. In particular, running
  73          the command 'history -f FILENAME' from the IPython Notebook
  74          interface will replace FILENAME even if it already exists *without*
  75          confirmation.
  76          """)
  77      @argument(
  78          '-g', dest='pattern', nargs='*', default=None,
  79          help="""
  80          treat the arg as a glob pattern to search for in (full) history.
  81          This includes the saved history (almost all commands ever written).
  82          The pattern may contain '?' to match one unknown character and '*'
  83          to match any number of unknown characters. Use '%%hist -g' to show
  84          full saved history (may be very long).
  85          """)
  86      @argument(
  87          '-l', dest='limit', type=int, nargs='?', default=_unspecified,
  88          help="""
  89          get the last n lines from all sessions. Specify n as a single
  90          arg, or the default is the last 10 lines.
  91          """)
  92      @argument(
  93          '-u', dest='unique', action='store_true',
  94          help="""
  95          when searching history using `-g`, show only unique history.
  96          """)
  97      @argument('range', nargs='*')
  98      @skip_doctest
  99      @line_magic
 100      def history(self, parameter_s = ''):
 101          """Print input history (_i<n> variables), with most recent last.
 102  
 103          By default, input history is printed without line numbers so it can be
 104          directly pasted into an editor. Use -n to show them.
 105  
 106          By default, all input history from the current session is displayed.
 107          Ranges of history can be indicated using the syntax:
 108  
 109          ``4``
 110              Line 4, current session
 111          ``4-6``
 112              Lines 4-6, current session
 113          ``243/1-5``
 114              Lines 1-5, session 243
 115          ``~2/7``
 116              Line 7, session 2 before current
 117          ``~8/1-~6/5``
 118              From the first line of 8 sessions ago, to the fifth line of 6
 119              sessions ago.
 120  
 121          Multiple ranges can be entered, separated by spaces
 122  
 123          The same syntax is used by %macro, %save, %edit, %rerun
 124  
 125          Examples
 126          --------
 127          ::
 128  
 129            In [6]: %history -n 4-6
 130            4:a = 12
 131            5:print(a**2)
 132            6:%history -n 4-6
 133  
 134          """
 135  
 136          args = parse_argstring(self.history, parameter_s)
 137  
 138          # For brevity
 139          history_manager = self.shell.history_manager
 140  
 141          def _format_lineno(session, line):
 142              """Helper function to format line numbers properly."""
 143              if session in (0, history_manager.session_number):
 144                  return str(line)
 145              return "%s/%s" % (session, line)
 146  
 147          # Check if output to specific file was requested.
 148          outfname = args.filename
 149          if not outfname:
 150              outfile = sys.stdout  # default
 151              # We don't want to close stdout at the end!
 152              close_at_end = False
 153          else:
 154              outfname = os.path.expanduser(outfname)
 155              if os.path.exists(outfname):
 156                  try:
 157                      ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname)
 158                  except StdinNotImplementedError:
 159                      ans = True
 160                  if not ans:
 161                      print('Aborting.')
 162                      return
 163                  print("Overwriting file.")
 164              outfile = io_open(outfname, 'w', encoding='utf-8')
 165              close_at_end = True
 166  
 167          print_nums = args.print_nums
 168          get_output = args.get_output
 169          pyprompts = args.pyprompts
 170          raw = args.raw
 171  
 172          pattern = None
 173          limit = None if args.limit is _unspecified else args.limit
 174  
 175          range_pattern = False
 176          if args.pattern is not None and not args.range:
 177              if args.pattern:
 178                  pattern = "*" + " ".join(args.pattern) + "*"
 179              else:
 180                  pattern = "*"
 181              hist = history_manager.search(pattern, raw=raw, output=get_output,
 182                                            n=limit, unique=args.unique)
 183              print_nums = True
 184          elif args.limit is not _unspecified:
 185              n = 10 if limit is None else limit
 186              hist = history_manager.get_tail(n, raw=raw, output=get_output)
 187          else:
 188              if args.pattern:
 189                  range_pattern = "*" + " ".join(args.pattern) + "*"
 190                  print_nums = True
 191              hist = history_manager.get_range_by_str(
 192                  " ".join(args.range), raw, get_output
 193              )
 194  
 195          # We could be displaying the entire history, so let's not try to pull
 196          # it into a list in memory. Anything that needs more space will just
 197          # misalign.
 198          width = 4
 199  
 200          for session, lineno, inline in hist:
 201              # Print user history with tabs expanded to 4 spaces.  The GUI
 202              # clients use hard tabs for easier usability in auto-indented code,
 203              # but we want to produce PEP-8 compliant history for safe pasting
 204              # into an editor.
 205              if get_output:
 206                  inline, output = inline
 207              if range_pattern:
 208                  if not fnmatch.fnmatch(inline, range_pattern):
 209                      continue
 210              inline = inline.expandtabs(4).rstrip()
 211  
 212              multiline = "\n" in inline
 213              line_sep = '\n' if multiline else ' '
 214              if print_nums:
 215                  print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width),
 216                          line_sep),  file=outfile, end=u'')
 217              if pyprompts:
 218                  print(u">>> ", end=u"", file=outfile)
 219                  if multiline:
 220                      inline = "\n... ".join(inline.splitlines()) + "\n..."
 221              print(inline, file=outfile)
 222              if get_output and output:
 223                  print(output, file=outfile)
 224  
 225          if close_at_end:
 226              outfile.close()
 227  
 228      @line_magic
 229      def recall(self, arg):
 230          r"""Repeat a command, or get command to input line for editing.
 231  
 232          %recall and %rep are equivalent.
 233  
 234          - %recall (no arguments):
 235  
 236          Place a string version of last computation result (stored in the
 237          special '_' variable) to the next input prompt. Allows you to create
 238          elaborate command lines without using copy-paste::
 239  
 240               In[1]: l = ["hei", "vaan"]
 241               In[2]: "".join(l)
 242              Out[2]: heivaan
 243               In[3]: %recall
 244               In[4]: heivaan_ <== cursor blinking
 245  
 246          %recall 45
 247  
 248          Place history line 45 on the next input prompt. Use %hist to find
 249          out the number.
 250  
 251          %recall 1-4
 252  
 253          Combine the specified lines into one cell, and place it on the next
 254          input prompt. See %history for the slice syntax.
 255  
 256          %recall foo+bar
 257  
 258          If foo+bar can be evaluated in the user namespace, the result is
 259          placed at the next input prompt. Otherwise, the history is searched
 260          for lines which contain that substring, and the most recent one is
 261          placed at the next input prompt.
 262          """
 263          if not arg:                 # Last output
 264              self.shell.set_next_input(str(self.shell.user_ns["_"]))
 265              return
 266                                      # Get history range
 267          histlines = self.shell.history_manager.get_range_by_str(arg)
 268          cmd = "\n".join(x[2] for x in histlines)
 269          if cmd:
 270              self.shell.set_next_input(cmd.rstrip())
 271              return
 272  
 273          try:                        # Variable in user namespace
 274              cmd = str(eval(arg, self.shell.user_ns))
 275          except Exception:           # Search for term in history
 276              histlines = self.shell.history_manager.search("*"+arg+"*")
 277              for h in reversed([x[2] for x in histlines]):
 278                  if 'recall' in h or 'rep' in h:
 279                      continue
 280                  self.shell.set_next_input(h.rstrip())
 281                  return
 282          else:
 283              self.shell.set_next_input(cmd.rstrip())
 284              return
 285          print("Couldn't evaluate or find in history:", arg)
 286  
 287      @line_magic
 288      def rerun(self, parameter_s=''):
 289          """Re-run previous input
 290  
 291          By default, you can specify ranges of input history to be repeated
 292          (as with %history). With no arguments, it will repeat the last line.
 293  
 294          Options:
 295  
 296            -l <n> : Repeat the last n lines of input, not including the
 297            current command.
 298  
 299            -g foo : Repeat the most recent line which contains foo
 300          """
 301          opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
 302          if "l" in opts:         # Last n lines
 303              try:
 304                  n = int(opts["l"])
 305              except ValueError:
 306                  print("Number of lines must be an integer")
 307                  return
 308  
 309              if n == 0:
 310                  print("Requested 0 last lines - nothing to run")
 311                  return
 312              elif n < 0:
 313                  print("Number of lines to rerun cannot be negative")
 314                  return
 315  
 316              hist = self.shell.history_manager.get_tail(n)
 317          elif "g" in opts:       # Search
 318              p = "*"+opts['g']+"*"
 319              hist = list(self.shell.history_manager.search(p))
 320              for l in reversed(hist):
 321                  if "rerun" not in l[2]:
 322                      hist = [l]     # The last match which isn't a %rerun
 323                      break
 324              else:
 325                  hist = []          # No matches except %rerun
 326          elif args:              # Specify history ranges
 327              hist = self.shell.history_manager.get_range_by_str(args)
 328          else:                   # Last line
 329              hist = self.shell.history_manager.get_tail(1)
 330          hist = [x[2] for x in hist]
 331          if not hist:
 332              print("No lines in history match specification")
 333              return
 334          histlines = "\n".join(hist)
 335          print("=== Executing: ===")
 336          print(histlines)
 337          print("=== Output: ===")
 338          self.shell.run_cell("\n".join(hist), store_history=False)
 339