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
19import fnmatch
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
28
29#-----------------------------------------------------------------------------
30# Magics class implementation
31#-----------------------------------------------------------------------------
32
33
34_unspecified = object()
35
36
37@magics_class
38class 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 )
78 @argument(
79 "-y",
80 dest="overwrite",
81 help="yes, overwrite filename even if exists",
82 action="store_true",
83 default=None,
84 )
85 @argument(
86 "-g",
87 dest="pattern",
88 nargs="*",
89 default=None,
90 help="""
91 treat the arg as a glob pattern to search for in (full) history.
92 This includes the saved history (almost all commands ever written).
93 The pattern may contain '?' to match one unknown character and '*'
94 to match any number of unknown characters. Use '%%hist -g' to show
95 full saved history (may be very long).
96 """)
97 @argument(
98 '-l', dest='limit', type=int, nargs='?', default=_unspecified,
99 help="""
100 get the last n lines from all sessions. Specify n as a single
101 arg, or the default is the last 10 lines.
102 """)
103 @argument(
104 '-u', dest='unique', action='store_true',
105 help="""
106 when searching history using `-g`, show only unique history.
107 """)
108 @argument('range', nargs='*')
109 @skip_doctest
110 @line_magic
111 def history(self, parameter_s = ''):
112 """Print input history (_i<n> variables), with most recent last.
113
114 By default, input history is printed without line numbers so it can be
115 directly pasted into an editor. Use -n to show them.
116
117 By default, all input history from the current session is displayed.
118 Ranges of history can be indicated using the syntax:
119
120 ``4``
121 Line 4, current session
122 ``4-6``
123 Lines 4-6, current session
124 ``243/1-5``
125 Lines 1-5, session 243
126 ``~2/7``
127 Line 7, session 2 before current
128 ``~8/1-~6/5``
129 From the first line of 8 sessions ago, to the fifth line of 6
130 sessions ago.
131
132 Multiple ranges can be entered, separated by spaces
133
134 The same syntax is used by %macro, %save, %edit, %rerun
135
136 Examples
137 --------
138 ::
139
140 In [6]: %history -n 4-6
141 4:a = 12
142 5:print(a**2)
143 6:%history -n 4-6
144
145 """
146
147 args = parse_argstring(self.history, parameter_s)
148
149 # For brevity
150 history_manager = self.shell.history_manager
151
152 def _format_lineno(session, line):
153 """Helper function to format line numbers properly."""
154 if session in (0, history_manager.session_number):
155 return str(line)
156 return "%s/%s" % (session, line)
157
158 # Check if output to specific file was requested.
159 outfname = args.filename
160 if not outfname:
161 outfile = sys.stdout # default
162 # We don't want to close stdout at the end!
163 close_at_end = False
164 else:
165 outfname = os.path.expanduser(outfname)
166 if args.overwrite is True:
167 ans = True
168 elif os.path.exists(outfname):
169 ans = True
170 if sys.stdin.isatty():
171 ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname)
172 else:
173 try:
174 ans = io.ask_yes_no("File %r exists. Overwrite?" % outfname)
175 except StdinNotImplementedError:
176 ans = True
177 if not ans:
178 print("Aborting.")
179 return
180 print("Overwriting file.")
181 outfile = io_open(outfname, 'w', encoding='utf-8')
182 close_at_end = True
183
184 print_nums = args.print_nums
185 get_output = args.get_output
186 pyprompts = args.pyprompts
187 raw = args.raw
188
189 pattern = None
190 limit = None if args.limit is _unspecified else args.limit
191
192 range_pattern = False
193 if args.pattern is not None and not args.range:
194 if args.pattern:
195 pattern = "*" + " ".join(args.pattern) + "*"
196 else:
197 pattern = "*"
198 hist = history_manager.search(pattern, raw=raw, output=get_output,
199 n=limit, unique=args.unique)
200 print_nums = True
201 elif args.limit is not _unspecified:
202 n = 10 if limit is None else limit
203 hist = history_manager.get_tail(n, raw=raw, output=get_output)
204 else:
205 if args.pattern:
206 range_pattern = "*" + " ".join(args.pattern) + "*"
207 print_nums = True
208 hist = history_manager.get_range_by_str(
209 " ".join(args.range), raw, get_output
210 )
211
212 # We could be displaying the entire history, so let's not try to pull
213 # it into a list in memory. Anything that needs more space will just
214 # misalign.
215 width = 4
216
217 for session, lineno, inline in hist:
218 # Print user history with tabs expanded to 4 spaces. The GUI
219 # clients use hard tabs for easier usability in auto-indented code,
220 # but we want to produce PEP-8 compliant history for safe pasting
221 # into an editor.
222 if get_output:
223 inline, output = inline
224 if range_pattern:
225 if not fnmatch.fnmatch(inline, range_pattern):
226 continue
227 inline = inline.expandtabs(4).rstrip()
228
229 multiline = "\n" in inline
230 line_sep = '\n' if multiline else ' '
231 if print_nums:
232 print(u'%s:%s' % (_format_lineno(session, lineno).rjust(width),
233 line_sep), file=outfile, end=u'')
234 if pyprompts:
235 print(u">>> ", end=u"", file=outfile)
236 if multiline:
237 inline = "\n... ".join(inline.splitlines()) + "\n..."
238 print(inline, file=outfile)
239 if get_output and output:
240 print(output, file=outfile)
241
242 if close_at_end:
243 outfile.close()
244
245 @line_magic
246 def recall(self, arg):
247 r"""Repeat a command, or get command to input line for editing.
248
249 %recall and %rep are equivalent.
250
251 - %recall (no arguments):
252
253 Place a string version of last computation result (stored in the
254 special '_' variable) to the next input prompt. Allows you to create
255 elaborate command lines without using copy-paste::
256
257 In[1]: l = ["hei", "vaan"]
258 In[2]: "".join(l)
259 Out[2]: heivaan
260 In[3]: %recall
261 In[4]: heivaan_ <== cursor blinking
262
263 %recall 45
264
265 Place history line 45 on the next input prompt. Use %hist to find
266 out the number.
267
268 %recall 1-4
269
270 Combine the specified lines into one cell, and place it on the next
271 input prompt. See %history for the slice syntax.
272
273 %recall foo+bar
274
275 If foo+bar can be evaluated in the user namespace, the result is
276 placed at the next input prompt. Otherwise, the history is searched
277 for lines which contain that substring, and the most recent one is
278 placed at the next input prompt.
279 """
280 if not arg: # Last output
281 self.shell.set_next_input(str(self.shell.user_ns["_"]))
282 return
283 # Get history range
284 histlines = self.shell.history_manager.get_range_by_str(arg)
285 cmd = "\n".join(x[2] for x in histlines)
286 if cmd:
287 self.shell.set_next_input(cmd.rstrip())
288 return
289
290 try: # Variable in user namespace
291 cmd = str(eval(arg, self.shell.user_ns))
292 except Exception: # Search for term in history
293 histlines = self.shell.history_manager.search("*"+arg+"*")
294 for h in reversed([x[2] for x in histlines]):
295 if 'recall' in h or 'rep' in h:
296 continue
297 self.shell.set_next_input(h.rstrip())
298 return
299 else:
300 self.shell.set_next_input(cmd.rstrip())
301 return
302 print("Couldn't evaluate or find in history:", arg)
303
304 @line_magic
305 def rerun(self, parameter_s=''):
306 """Re-run previous input
307
308 By default, you can specify ranges of input history to be repeated
309 (as with %history). With no arguments, it will repeat the last line.
310
311 Options:
312
313 -l <n> : Repeat the last n lines of input, not including the
314 current command.
315
316 -g foo : Repeat the most recent line which contains foo
317 """
318 opts, args = self.parse_options(parameter_s, 'l:g:', mode='string')
319 if "l" in opts: # Last n lines
320 try:
321 n = int(opts["l"])
322 except ValueError:
323 print("Number of lines must be an integer")
324 return
325
326 if n == 0:
327 print("Requested 0 last lines - nothing to run")
328 return
329 elif n < 0:
330 print("Number of lines to rerun cannot be negative")
331 return
332
333 hist = self.shell.history_manager.get_tail(n)
334 elif "g" in opts: # Search
335 p = "*"+opts['g']+"*"
336 hist = list(self.shell.history_manager.search(p))
337 for l in reversed(hist):
338 if "rerun" not in l[2]:
339 hist = [l] # The last match which isn't a %rerun
340 break
341 else:
342 hist = [] # No matches except %rerun
343 elif args: # Specify history ranges
344 hist = self.shell.history_manager.get_range_by_str(args)
345 else: # Last line
346 hist = self.shell.history_manager.get_tail(1)
347 hist = [x[2] for x in hist]
348 if not hist:
349 print("No lines in history match specification")
350 return
351 histlines = "\n".join(hist)
352 print("=== Executing: ===")
353 print(histlines)
354 print("=== Output: ===")
355 self.shell.run_cell("\n".join(hist), store_history=False)