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