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