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