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