##// END OF EJS Templates
Reworking magic %hist, %macro and %edit commands to work with new history system.
Thomas Kluyver -
Show More
@@ -66,8 +66,11 b' class HistoryManager(object):'
66 # call).
66 # call).
67 _exit_commands = None
67 _exit_commands = None
68
68
69 def __init__(self, shell):
69 def __init__(self, shell, load_history=False):
70 """Create a new history manager associated with a shell instance.
70 """Create a new history manager associated with a shell instance.
71
72 If load_history is true, it will load the history from file and set the
73 session offset so that the next line typed can be retrieved as #1.
71 """
74 """
72 # We need a pointer back to the shell for various tasks.
75 # We need a pointer back to the shell for various tasks.
73 self.shell = shell
76 self.shell = shell
@@ -78,6 +81,9 b' class HistoryManager(object):'
78 # pre-processing. This will allow users to retrieve the input just as
81 # pre-processing. This will allow users to retrieve the input just as
79 # it was exactly typed in by the user, with %hist -r.
82 # it was exactly typed in by the user, with %hist -r.
80 self.input_hist_raw = []
83 self.input_hist_raw = []
84
85 # Offset so the first line of the current session is #1
86 self.session_offset = -1
81
87
82 # list of visited directories
88 # list of visited directories
83 try:
89 try:
@@ -105,8 +111,9 b' class HistoryManager(object):'
105
111
106 # Object is fully initialized, we can now call methods on it.
112 # Object is fully initialized, we can now call methods on it.
107
113
108 # Fill the history zero entry, user counter starts at 1
114 if load_history:
109 self.store_inputs('\n', '\n')
115 self.reload_history()
116 self.session_offset = len(self.input_hist_raw) -1
110
117
111 # Create and start the autosaver.
118 # Create and start the autosaver.
112 self.autosave_flag = threading.Event()
119 self.autosave_flag = threading.Event()
@@ -115,6 +122,7 b' class HistoryManager(object):'
115 # Register the autosave handler to be triggered as a post execute
122 # Register the autosave handler to be triggered as a post execute
116 # callback.
123 # callback.
117 self.shell.register_post_execute(self.autosave_if_due)
124 self.shell.register_post_execute(self.autosave_if_due)
125
118
126
119 def _init_shadow_hist(self):
127 def _init_shadow_hist(self):
120 try:
128 try:
@@ -181,7 +189,7 b' class HistoryManager(object):'
181 Parameters
189 Parameters
182 ----------
190 ----------
183 index : n or (n1, n2) or None
191 index : n or (n1, n2) or None
184 If n, then the last entries. If a tuple, then all in
192 If n, then the last n entries. If a tuple, then all in
185 range(n1, n2). If None, then all entries. Raises IndexError if
193 range(n1, n2). If None, then all entries. Raises IndexError if
186 the format of index is incorrect.
194 the format of index is incorrect.
187 raw : bool
195 raw : bool
@@ -193,8 +201,7 b' class HistoryManager(object):'
193 -------
201 -------
194 If output is True, then return a dict of tuples, keyed by the prompt
202 If output is True, then return a dict of tuples, keyed by the prompt
195 numbers and with values of (input, output). If output is False, then
203 numbers and with values of (input, output). If output is False, then
196 a dict, keyed by the prompt number with the values of input. Raises
204 a dict, keyed by the prompt number with the values of input.
197 IndexError if no history is found.
198 """
205 """
199 if raw:
206 if raw:
200 input_hist = self.input_hist_raw
207 input_hist = self.input_hist_raw
@@ -202,24 +209,25 b' class HistoryManager(object):'
202 input_hist = self.input_hist_parsed
209 input_hist = self.input_hist_parsed
203 if output:
210 if output:
204 output_hist = self.output_hist
211 output_hist = self.output_hist
212
205 n = len(input_hist)
213 n = len(input_hist)
214 offset = self.session_offset
206 if index is None:
215 if index is None:
207 start=0; stop=n
216 start=offset+1; stop=n
208 elif isinstance(index, int):
217 elif isinstance(index, int):
209 start=n-index; stop=n
218 start=n-index; stop=n
210 elif isinstance(index, tuple) and len(index) == 2:
219 elif len(index) == 2:
211 start=index[0]; stop=index[1]
220 start = index[0] + offset
221 stop = index[1] + offset
212 else:
222 else:
213 raise IndexError('Not a valid index for the input history: %r'
223 raise IndexError('Not a valid index for the input history: %r'
214 % index)
224 % index)
215 hist = {}
225 hist = {}
216 for i in range(start, stop):
226 for i in range(start, stop):
217 if output:
227 if output:
218 hist[i] = (input_hist[i], output_hist.get(i))
228 hist[i-offset] = (input_hist[i], output_hist.get(i-offset))
219 else:
229 else:
220 hist[i] = input_hist[i]
230 hist[i-offset] = input_hist[i]
221 if not hist:
222 raise IndexError('No history for range of indices: %r' % index)
223 return hist
231 return hist
224
232
225 def store_inputs(self, source, source_raw=None):
233 def store_inputs(self, source, source_raw=None):
@@ -364,6 +372,9 b" def magic_history(self, parameter_s = ''):"
364 print('This feature is only available if numbered prompts are in use.')
372 print('This feature is only available if numbered prompts are in use.')
365 return
373 return
366 opts,args = self.parse_options(parameter_s,'gnoptsrf:',mode='list')
374 opts,args = self.parse_options(parameter_s,'gnoptsrf:',mode='list')
375
376 # For brevity
377 history_manager = self.shell.history_manager
367
378
368 # Check if output to specific file was requested.
379 # Check if output to specific file was requested.
369 try:
380 try:
@@ -380,47 +391,41 b" def magic_history(self, parameter_s = ''):"
380
391
381 outfile = open(outfname,'w')
392 outfile = open(outfname,'w')
382 close_at_end = True
393 close_at_end = True
383
394
384 if 't' in opts:
395 print_nums = 'n' in opts
385 input_hist = self.shell.history_manager.input_hist_parsed
396 print_outputs = 'o' in opts
386 elif 'r' in opts:
397 pyprompts = 'p' in opts
387 input_hist = self.shell.history_manager.input_hist_raw
398 # Raw history is the default
388 else:
399 raw = not('t' in opts)
389 # Raw history is the default
390 input_hist = self.shell.history_manager.input_hist_raw
391
400
392 default_length = 40
401 default_length = 40
393 pattern = None
402 pattern = None
394 if 'g' in opts:
403 if 'g' in opts:
395 init = 1
404 index = None
396 final = len(input_hist)
397 parts = parameter_s.split(None, 1)
405 parts = parameter_s.split(None, 1)
398 if len(parts) == 1:
406 if len(parts) == 1:
399 parts += '*'
407 parts += '*'
400 head, pattern = parts
408 head, pattern = parts
401 pattern = "*" + pattern + "*"
409 pattern = "*" + pattern + "*"
402 elif len(args) == 0:
410 elif len(args) == 0:
403 final = len(input_hist)-1
411 index = None
404 init = max(1,final-default_length)
405 elif len(args) == 1:
412 elif len(args) == 1:
406 final = len(input_hist)
413 index = int(args[0])
407 init = max(1, final-int(args[0]))
408 elif len(args) == 2:
414 elif len(args) == 2:
409 init, final = map(int, args)
415 index = map(int, args)
410 else:
416 else:
411 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
417 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
412 print(self.magic_hist.__doc__, file=IPython.utils.io.Term.cout)
418 print(self.magic_hist.__doc__, file=IPython.utils.io.Term.cout)
413 return
419 return
420
421 hist = history_manager.get_history(index, raw, print_outputs)
414
422
415 width = len(str(final))
423 width = len(str(max(hist.iterkeys())))
416 line_sep = ['','\n']
424 line_sep = ['','\n']
417 print_nums = 'n' in opts
418 print_outputs = 'o' in opts
419 pyprompts = 'p' in opts
420
425
421 found = False
426 found = False
422 if pattern is not None:
427 if pattern is not None:
423 sh = self.shell.history_manager.shadowhist.all()
428 sh = history_manager.shadow_hist.all()
424 for idx, s in sh:
429 for idx, s in sh:
425 if fnmatch.fnmatch(s, pattern):
430 if fnmatch.fnmatch(s, pattern):
426 print("0%d: %s" %(idx, s.expandtabs(4)), file=outfile)
431 print("0%d: %s" %(idx, s.expandtabs(4)), file=outfile)
@@ -432,41 +437,39 b" def magic_history(self, parameter_s = ''):"
432 file=outfile)
437 file=outfile)
433 print("=== start of normal history ===", file=outfile)
438 print("=== start of normal history ===", file=outfile)
434
439
435 for in_num in range(init, final):
440 for in_num, inline in sorted(hist.iteritems()):
436 # Print user history with tabs expanded to 4 spaces. The GUI clients
441 # Print user history with tabs expanded to 4 spaces. The GUI clients
437 # use hard tabs for easier usability in auto-indented code, but we want
442 # use hard tabs for easier usability in auto-indented code, but we want
438 # to produce PEP-8 compliant history for safe pasting into an editor.
443 # to produce PEP-8 compliant history for safe pasting into an editor.
439 inline = input_hist[in_num].expandtabs(4).rstrip()+'\n'
444 if print_outputs:
445 inline, output = inline
446 inline = inline.expandtabs(4).rstrip()
440
447
441 if pattern is not None and not fnmatch.fnmatch(inline, pattern):
448 if pattern is not None and not fnmatch.fnmatch(inline, pattern):
442 continue
449 continue
443
450
444 multiline = int(inline.count('\n') > 1)
451 multiline = "\n" in inline
445 if print_nums:
452 if print_nums:
446 print('%s:%s' % (str(in_num).ljust(width), line_sep[multiline]),
453 print('%s:%s' % (str(in_num).ljust(width), line_sep[multiline]),
447 file=outfile)
454 file=outfile, end='')
448 if pyprompts:
455 if pyprompts:
449 print('>>>', file=outfile)
456 inline = ">>> " + inline
450 if multiline:
457 if multiline:
451 lines = inline.splitlines()
458 lines = inline.splitlines()
452 print('\n... '.join(lines), file=outfile)
459 print('\n... '.join(lines), file=outfile)
453 print('... ', file=outfile)
460 print('... ', file=outfile)
454 else:
461 else:
455 print(inline, end='', file=outfile)
462 print(inline, file=outfile)
456 else:
463 else:
457 print(inline, end='', file=outfile)
464 print(inline, file=outfile)
458 if print_outputs:
465 if print_outputs and output:
459 output = self.shell.history_manager.output_hist.get(in_num)
466 print(repr(output), file=outfile)
460 if output is not None:
461 print(repr(output), file=outfile)
462
467
463 if close_at_end:
468 if close_at_end:
464 outfile.close()
469 outfile.close()
465
470
466
471 # %hist is an alternative name
467 def magic_hist(self, parameter_s=''):
472 magic_hist = magic_history
468 """Alternate name for %history."""
469 return self.magic_history(parameter_s)
470
473
471
474
472 def rep_f(self, arg):
475 def rep_f(self, arg):
@@ -1248,7 +1248,7 b' class InteractiveShell(Configurable, Magic):'
1248
1248
1249 def init_history(self):
1249 def init_history(self):
1250 """Sets up the command history, and starts regular autosaves."""
1250 """Sets up the command history, and starts regular autosaves."""
1251 self.history_manager = HistoryManager(shell=self)
1251 self.history_manager = HistoryManager(shell=self, load_history=True)
1252
1252
1253 def save_history(self):
1253 def save_history(self):
1254 """Save input history to a file (via readline library)."""
1254 """Save input history to a file (via readline library)."""
@@ -1560,11 +1560,8 b' class InteractiveShell(Configurable, Magic):'
1560 readline.set_completer_delims(delims)
1560 readline.set_completer_delims(delims)
1561 # otherwise we end up with a monster history after a while:
1561 # otherwise we end up with a monster history after a while:
1562 readline.set_history_length(self.history_length)
1562 readline.set_history_length(self.history_length)
1563 try:
1563
1564 #print '*** Reading readline history' # dbg
1564 self.history_manager.populate_readline_history()
1565 self.reload_history()
1566 except IOError:
1567 pass # It doesn't exist yet.
1568
1565
1569 # Configure auto-indent for all platforms
1566 # Configure auto-indent for all platforms
1570 self.set_autoindent(self.autoindent)
1567 self.set_autoindent(self.autoindent)
@@ -19,9 +19,9 b' class Macro(IPyAutocall):'
19 Args to macro are available in _margv list if you need them.
19 Args to macro are available in _margv list if you need them.
20 """
20 """
21
21
22 def __init__(self,data):
22 def __init__(self,code):
23 """store the macro value, as a single string which can be executed"""
23 """store the macro value, as a single string which can be executed"""
24 self.value = '\n'.join(data).rstrip()+'\n'
24 self.value = code.rstrip()+'\n'
25
25
26 def __str__(self):
26 def __str__(self):
27 return self.value
27 return self.value
@@ -184,11 +184,7 b' python-profiler package from non-free.""")'
184 N:M -> standard python form, means including items N...(M-1).
184 N:M -> standard python form, means including items N...(M-1).
185
185
186 N-M -> include items N..M (closed endpoint)."""
186 N-M -> include items N..M (closed endpoint)."""
187
187 history_manager = self.shell.history_manager
188 if raw:
189 hist = self.shell.history_manager.input_hist_raw
190 else:
191 hist = self.shell.history_manager.input_hist_parsed
192
188
193 cmds = []
189 cmds = []
194 for chunk in slices:
190 for chunk in slices:
@@ -200,7 +196,8 b' python-profiler package from non-free.""")'
200 else:
196 else:
201 ini = int(chunk)
197 ini = int(chunk)
202 fin = ini+1
198 fin = ini+1
203 cmds.append('\n'.join(hist[ini:fin]))
199 hist = history_manager.get_history((ini,fin), raw=raw, output=False)
200 cmds.append('\n'.join(hist[i] for i in sorted(hist.iterkeys())))
204 return cmds
201 return cmds
205
202
206 def arg_err(self,func):
203 def arg_err(self,func):
@@ -2044,7 +2041,7 b' Currently the magic system has the following functions:\\n"""'
2044
2041
2045 #print 'rng',ranges # dbg
2042 #print 'rng',ranges # dbg
2046 lines = self.extract_input_slices(ranges,opts.has_key('r'))
2043 lines = self.extract_input_slices(ranges,opts.has_key('r'))
2047 macro = Macro(lines)
2044 macro = Macro("\n".join(lines))
2048 self.shell.define_macro(name, macro)
2045 self.shell.define_macro(name, macro)
2049 print 'Macro `%s` created. To execute, type its name (without quotes).' % name
2046 print 'Macro `%s` created. To execute, type its name (without quotes).' % name
2050 print 'Macro contents:'
2047 print 'Macro contents:'
@@ -2294,7 +2291,7 b' Currently the magic system has the following functions:\\n"""'
2294 # This means that you can't edit files whose names begin with
2291 # This means that you can't edit files whose names begin with
2295 # numbers this way. Tough.
2292 # numbers this way. Tough.
2296 ranges = args.split()
2293 ranges = args.split()
2297 data = ''.join(self.extract_input_slices(ranges,opts_r))
2294 data = '\n'.join(self.extract_input_slices(ranges,opts_r))
2298 elif args.endswith('.py'):
2295 elif args.endswith('.py'):
2299 filename = make_filename(args)
2296 filename = make_filename(args)
2300 data = ''
2297 data = ''
General Comments 0
You need to be logged in to leave comments. Login now