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