##// END OF EJS Templates
removed quit/exit commands from history
Satrajit Ghosh -
Show More
@@ -1,512 +1,515 b''
1 1 """ History related magics and functionality """
2 2 #-----------------------------------------------------------------------------
3 3 # Copyright (C) 2010 The IPython Development Team.
4 4 #
5 5 # Distributed under the terms of the BSD License.
6 6 #
7 7 # The full license is in the file COPYING.txt, distributed with this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Imports
12 12 #-----------------------------------------------------------------------------
13 13 from __future__ import print_function
14 14
15 15 # Stdlib imports
16 16 import fnmatch
17 17 import json
18 18 import os
19 19 import sys
20 20
21 21 # Our own packages
22 22 import IPython.utils.io
23 23
24 24 from IPython.utils.pickleshare import PickleShareDB
25 25 from IPython.utils.io import ask_yes_no
26 26 from IPython.utils.warn import warn
27 27
28 28 #-----------------------------------------------------------------------------
29 29 # Classes and functions
30 30 #-----------------------------------------------------------------------------
31 31
32 32 class HistoryManager(object):
33 33 """A class to organize all history-related functionality in one place.
34 34 """
35 35 # Public interface
36 36
37 37 # An instance of the IPython shell we are attached to
38 38 shell = None
39 39 # A list to hold processed history
40 40 input_hist_parsed = None
41 41 # A list to hold raw history (as typed by user)
42 42 input_hist_raw = None
43 43 # A list of directories visited during session
44 44 dir_hist = None
45 45 # A dict of output history, keyed with ints from the shell's execution count
46 46 output_hist = None
47 47 # String with path to the history file
48 48 hist_file = None
49 49 # PickleShareDB instance holding the raw data for the shadow history
50 50 shadow_db = None
51 51 # ShadowHist instance with the actual shadow history
52 52 shadow_hist = None
53 53
54 54 # Private interface
55 55 # Variables used to store the three last inputs from the user. On each new
56 56 # history update, we populate the user's namespace with these, shifted as
57 57 # necessary.
58 58 _i00, _i, _ii, _iii = '','','',''
59 59
60 60 def __init__(self, shell):
61 61 """Create a new history manager associated with a shell instance.
62 62 """
63 63 # We need a pointer back to the shell for various tasks.
64 64 self.shell = shell
65 65
66 66 # List of input with multi-line handling.
67 67 self.input_hist_parsed = []
68 68 # This one will hold the 'raw' input history, without any
69 69 # pre-processing. This will allow users to retrieve the input just as
70 70 # it was exactly typed in by the user, with %hist -r.
71 71 self.input_hist_raw = []
72 72
73 73 # list of visited directories
74 74 try:
75 75 self.dir_hist = [os.getcwd()]
76 76 except OSError:
77 77 self.dir_hist = []
78 78
79 79 # dict of output history
80 80 self.output_hist = {}
81 81
82 82 # Now the history file
83 83 if shell.profile:
84 84 histfname = 'history-%s' % shell.profile
85 85 else:
86 86 histfname = 'history'
87 87 self.hist_file = os.path.join(shell.ipython_dir, histfname + '.json')
88 88
89 89 # Objects related to shadow history management
90 90 self._init_shadow_hist()
91 91
92 92 self._i00, self._i, self._ii, self._iii = '','','',''
93 93
94 94 # Object is fully initialized, we can now call methods on it.
95 95
96 96 # Fill the history zero entry, user counter starts at 1
97 97 self.store_inputs('\n', '\n')
98 98
99 99 def _init_shadow_hist(self):
100 100 try:
101 101 self.shadow_db = PickleShareDB(os.path.join(
102 102 self.shell.ipython_dir, 'db'))
103 103 except UnicodeDecodeError:
104 104 print("Your ipython_dir can't be decoded to unicode!")
105 105 print("Please set HOME environment variable to something that")
106 106 print(r"only has ASCII characters, e.g. c:\home")
107 107 print("Now it is", self.ipython_dir)
108 108 sys.exit()
109 109 self.shadow_hist = ShadowHist(self.shadow_db, self.shell)
110 110
111 111 def populate_readline_history(self):
112 112 """Populate the readline history from the raw history.
113 113
114 114 We only store one copy of the raw history, which is persisted to a json
115 115 file on disk. The readline history is repopulated from the contents of
116 116 this file."""
117 117
118 118 try:
119 119 self.shell.readline.clear_history()
120 120 except AttributeError:
121 121 pass
122 122 else:
123 123 for h in self.input_hist_raw:
124 124 if not h.isspace():
125 125 for line in h.splitlines():
126 126 self.shell.readline.add_history(line)
127 127
128 128 def save_history(self):
129 129 """Save input history to a file (via readline library)."""
130 130 hist = dict(raw=self.input_hist_raw, #[-self.shell.history_length:],
131 131 parsed=self.input_hist_parsed) #[-self.shell.history_length:])
132 132 with open(self.hist_file,'wt') as hfile:
133 133 json.dump(hist, hfile,
134 134 sort_keys=True, indent=4)
135 135
136 136 def reload_history(self):
137 137 """Reload the input history from disk file."""
138 138
139 139 with open(self.hist_file,'rt') as hfile:
140 140 hist = json.load(hfile)
141 141 self.input_hist_parsed = hist['parsed']
142 142 self.input_hist_raw = hist['raw']
143 143 if self.shell.has_readline:
144 144 self.populate_readline_history()
145 145
146 146 def get_history(self, index=None, raw=False, output=True):
147 147 """Get the history list.
148 148
149 149 Get the input and output history.
150 150
151 151 Parameters
152 152 ----------
153 153 index : n or (n1, n2) or None
154 154 If n, then the last entries. If a tuple, then all in
155 155 range(n1, n2). If None, then all entries. Raises IndexError if
156 156 the format of index is incorrect.
157 157 raw : bool
158 158 If True, return the raw input.
159 159 output : bool
160 160 If True, then return the output as well.
161 161
162 162 Returns
163 163 -------
164 164 If output is True, then return a dict of tuples, keyed by the prompt
165 165 numbers and with values of (input, output). If output is False, then
166 166 a dict, keyed by the prompt number with the values of input. Raises
167 167 IndexError if no history is found.
168 168 """
169 169 if raw:
170 170 input_hist = self.input_hist_raw
171 171 else:
172 172 input_hist = self.input_hist_parsed
173 173 if output:
174 174 output_hist = self.output_hist
175 175 n = len(input_hist)
176 176 if index is None:
177 177 start=0; stop=n
178 178 elif isinstance(index, int):
179 179 start=n-index; stop=n
180 180 elif isinstance(index, tuple) and len(index) == 2:
181 181 start=index[0]; stop=index[1]
182 182 else:
183 183 raise IndexError('Not a valid index for the input history: %r'
184 184 % index)
185 185 hist = {}
186 186 for i in range(start, stop):
187 187 if output:
188 188 hist[i] = (input_hist[i], output_hist.get(i))
189 189 else:
190 190 hist[i] = input_hist[i]
191 191 if not hist:
192 192 raise IndexError('No history for range of indices: %r' % index)
193 193 return hist
194 194
195 195 def store_inputs(self, source, source_raw=None):
196 196 """Store source and raw input in history and create input cache
197 197 variables _i*.
198 198
199 199 Parameters
200 200 ----------
201 201 source : str
202 202 Python input.
203 203
204 204 source_raw : str, optional
205 205 If given, this is the raw input without any IPython transformations
206 206 applied to it. If not given, ``source`` is used.
207 207 """
208 208 if source_raw is None:
209 209 source_raw = source
210 # do not store quit/exit commands
211 if source_raw in ['Quit', 'quit', 'Exit', 'exit', '%Quit', '%quit', '%Exit', '%exit']:
212 return
210 213 self.input_hist_parsed.append(source)
211 214 self.input_hist_raw.append(source_raw)
212 215 self.shadow_hist.add(source)
213 216
214 217 # update the auto _i variables
215 218 self._iii = self._ii
216 219 self._ii = self._i
217 220 self._i = self._i00
218 221 self._i00 = source_raw
219 222
220 223 # hackish access to user namespace to create _i1,_i2... dynamically
221 224 new_i = '_i%s' % self.shell.execution_count
222 225 to_main = {'_i': self._i,
223 226 '_ii': self._ii,
224 227 '_iii': self._iii,
225 228 new_i : self._i00 }
226 229 self.shell.user_ns.update(to_main)
227 230
228 231 def sync_inputs(self):
229 232 """Ensure raw and translated histories have same length."""
230 233 if len(self.input_hist_parsed) != len (self.input_hist_raw):
231 234 self.input_hist_raw[:] = self.input_hist_parsed
232 235
233 236 def reset(self):
234 237 """Clear all histories managed by this object."""
235 238 self.input_hist_parsed[:] = []
236 239 self.input_hist_raw[:] = []
237 240 self.output_hist.clear()
238 241 # The directory history can't be completely empty
239 242 self.dir_hist[:] = [os.getcwd()]
240 243
241 244
242 245 def magic_history(self, parameter_s = ''):
243 246 """Print input history (_i<n> variables), with most recent last.
244 247
245 248 %history -> print at most 40 inputs (some may be multi-line)\\
246 249 %history n -> print at most n inputs\\
247 250 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
248 251
249 252 By default, input history is printed without line numbers so it can be
250 253 directly pasted into an editor.
251 254
252 255 With -n, each input's number <n> is shown, and is accessible as the
253 256 automatically generated variable _i<n> as well as In[<n>]. Multi-line
254 257 statements are printed starting at a new line for easy copy/paste.
255 258
256 259 Options:
257 260
258 261 -n: print line numbers for each input.
259 262 This feature is only available if numbered prompts are in use.
260 263
261 264 -o: also print outputs for each input.
262 265
263 266 -p: print classic '>>>' python prompts before each input. This is useful
264 267 for making documentation, and in conjunction with -o, for producing
265 268 doctest-ready output.
266 269
267 270 -r: (default) print the 'raw' history, i.e. the actual commands you typed.
268 271
269 272 -t: print the 'translated' history, as IPython understands it. IPython
270 273 filters your input and converts it all into valid Python source before
271 274 executing it (things like magics or aliases are turned into function
272 275 calls, for example). With this option, you'll see the native history
273 276 instead of the user-entered version: '%cd /' will be seen as
274 277 'get_ipython().magic("%cd /")' instead of '%cd /'.
275 278
276 279 -g: treat the arg as a pattern to grep for in (full) history.
277 280 This includes the "shadow history" (almost all commands ever written).
278 281 Use '%hist -g' to show full shadow history (may be very long).
279 282 In shadow history, every index nuwber starts with 0.
280 283
281 284 -f FILENAME: instead of printing the output to the screen, redirect it to
282 285 the given file. The file is always overwritten, though IPython asks for
283 286 confirmation first if it already exists.
284 287 """
285 288
286 289 if not self.shell.displayhook.do_full_cache:
287 290 print('This feature is only available if numbered prompts are in use.')
288 291 return
289 292 opts,args = self.parse_options(parameter_s,'gnoptsrf:',mode='list')
290 293
291 294 # Check if output to specific file was requested.
292 295 try:
293 296 outfname = opts['f']
294 297 except KeyError:
295 298 outfile = IPython.utils.io.Term.cout # default
296 299 # We don't want to close stdout at the end!
297 300 close_at_end = False
298 301 else:
299 302 if os.path.exists(outfname):
300 303 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
301 304 print('Aborting.')
302 305 return
303 306
304 307 outfile = open(outfname,'w')
305 308 close_at_end = True
306 309
307 310 if 't' in opts:
308 311 input_hist = self.shell.history_manager.input_hist_parsed
309 312 elif 'r' in opts:
310 313 input_hist = self.shell.history_manager.input_hist_raw
311 314 else:
312 315 # Raw history is the default
313 316 input_hist = self.shell.history_manager.input_hist_raw
314 317
315 318 default_length = 40
316 319 pattern = None
317 320 if 'g' in opts:
318 321 init = 1
319 322 final = len(input_hist)
320 323 parts = parameter_s.split(None, 1)
321 324 if len(parts) == 1:
322 325 parts += '*'
323 326 head, pattern = parts
324 327 pattern = "*" + pattern + "*"
325 328 elif len(args) == 0:
326 329 final = len(input_hist)-1
327 330 init = max(1,final-default_length)
328 331 elif len(args) == 1:
329 332 final = len(input_hist)
330 333 init = max(1, final-int(args[0]))
331 334 elif len(args) == 2:
332 335 init, final = map(int, args)
333 336 else:
334 337 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
335 338 print(self.magic_hist.__doc__, file=IPython.utils.io.Term.cout)
336 339 return
337 340
338 341 width = len(str(final))
339 342 line_sep = ['','\n']
340 343 print_nums = 'n' in opts
341 344 print_outputs = 'o' in opts
342 345 pyprompts = 'p' in opts
343 346
344 347 found = False
345 348 if pattern is not None:
346 349 sh = self.shell.history_manager.shadowhist.all()
347 350 for idx, s in sh:
348 351 if fnmatch.fnmatch(s, pattern):
349 352 print("0%d: %s" %(idx, s.expandtabs(4)), file=outfile)
350 353 found = True
351 354
352 355 if found:
353 356 print("===", file=outfile)
354 357 print("shadow history ends, fetch by %rep <number> (must start with 0)",
355 358 file=outfile)
356 359 print("=== start of normal history ===", file=outfile)
357 360
358 361 for in_num in range(init, final):
359 362 # Print user history with tabs expanded to 4 spaces. The GUI clients
360 363 # use hard tabs for easier usability in auto-indented code, but we want
361 364 # to produce PEP-8 compliant history for safe pasting into an editor.
362 365 inline = input_hist[in_num].expandtabs(4)
363 366
364 367 if pattern is not None and not fnmatch.fnmatch(inline, pattern):
365 368 continue
366 369
367 370 multiline = int(inline.count('\n') > 1)
368 371 if print_nums:
369 372 print('%s:%s' % (str(in_num).ljust(width), line_sep[multiline]),
370 373 file=outfile)
371 374 if pyprompts:
372 375 print('>>>', file=outfile)
373 376 if multiline:
374 377 lines = inline.splitlines()
375 378 print('\n... '.join(lines), file=outfile)
376 379 print('... ', file=outfile)
377 380 else:
378 381 print(inline, end='', file=outfile)
379 382 else:
380 383 print(inline, end='', file=outfile)
381 384 if print_outputs:
382 385 output = self.shell.history_manager.output_hist.get(in_num)
383 386 if output is not None:
384 387 print(repr(output), file=outfile)
385 388
386 389 if close_at_end:
387 390 outfile.close()
388 391
389 392
390 393 def magic_hist(self, parameter_s=''):
391 394 """Alternate name for %history."""
392 395 return self.magic_history(parameter_s)
393 396
394 397
395 398 def rep_f(self, arg):
396 399 r""" Repeat a command, or get command to input line for editing
397 400
398 401 - %rep (no arguments):
399 402
400 403 Place a string version of last computation result (stored in the special '_'
401 404 variable) to the next input prompt. Allows you to create elaborate command
402 405 lines without using copy-paste::
403 406
404 407 $ l = ["hei", "vaan"]
405 408 $ "".join(l)
406 409 ==> heivaan
407 410 $ %rep
408 411 $ heivaan_ <== cursor blinking
409 412
410 413 %rep 45
411 414
412 415 Place history line 45 to next input prompt. Use %hist to find out the
413 416 number.
414 417
415 418 %rep 1-4 6-7 3
416 419
417 420 Repeat the specified lines immediately. Input slice syntax is the same as
418 421 in %macro and %save.
419 422
420 423 %rep foo
421 424
422 425 Place the most recent line that has the substring "foo" to next input.
423 426 (e.g. 'svn ci -m foobar').
424 427 """
425 428
426 429 opts,args = self.parse_options(arg,'',mode='list')
427 430 if not args:
428 431 self.set_next_input(str(self.shell.user_ns["_"]))
429 432 return
430 433
431 434 if len(args) == 1 and not '-' in args[0]:
432 435 arg = args[0]
433 436 if len(arg) > 1 and arg.startswith('0'):
434 437 # get from shadow hist
435 438 num = int(arg[1:])
436 439 line = self.shell.shadowhist.get(num)
437 440 self.set_next_input(str(line))
438 441 return
439 442 try:
440 443 num = int(args[0])
441 444 self.set_next_input(str(self.shell.input_hist_raw[num]).rstrip())
442 445 return
443 446 except ValueError:
444 447 pass
445 448
446 449 for h in reversed(self.shell.input_hist_raw):
447 450 if 'rep' in h:
448 451 continue
449 452 if fnmatch.fnmatch(h,'*' + arg + '*'):
450 453 self.set_next_input(str(h).rstrip())
451 454 return
452 455
453 456 try:
454 457 lines = self.extract_input_slices(args, True)
455 458 print("lines", lines)
456 459 self.run_cell(lines)
457 460 except ValueError:
458 461 print("Not found in recent history:", args)
459 462
460 463
461 464 _sentinel = object()
462 465
463 466 class ShadowHist(object):
464 467 def __init__(self, db, shell):
465 468 # cmd => idx mapping
466 469 self.curidx = 0
467 470 self.db = db
468 471 self.disabled = False
469 472 self.shell = shell
470 473
471 474 def inc_idx(self):
472 475 idx = self.db.get('shadowhist_idx', 1)
473 476 self.db['shadowhist_idx'] = idx + 1
474 477 return idx
475 478
476 479 def add(self, ent):
477 480 if self.disabled:
478 481 return
479 482 try:
480 483 old = self.db.hget('shadowhist', ent, _sentinel)
481 484 if old is not _sentinel:
482 485 return
483 486 newidx = self.inc_idx()
484 487 #print("new", newidx) # dbg
485 488 self.db.hset('shadowhist',ent, newidx)
486 489 except:
487 490 self.shell.showtraceback()
488 491 print("WARNING: disabling shadow history")
489 492 self.disabled = True
490 493
491 494 def all(self):
492 495 d = self.db.hdict('shadowhist')
493 496 items = [(i,s) for (s,i) in d.iteritems()]
494 497 items.sort()
495 498 return items
496 499
497 500 def get(self, idx):
498 501 all = self.all()
499 502
500 503 for k, v in all:
501 504 if k == idx:
502 505 return v
503 506
504 507
505 508 def init_ipython(ip):
506 509 ip.define_magic("rep",rep_f)
507 510 ip.define_magic("hist",magic_hist)
508 511 ip.define_magic("history",magic_history)
509 512
510 513 # XXX - ipy_completers are in quarantine, need to be updated to new apis
511 514 #import ipy_completers
512 515 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
General Comments 0
You need to be logged in to leave comments. Login now