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