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