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