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