##// END OF EJS Templates
Created HistoryManager to better organize history control....
Fernando Perez -
Show More
@@ -1,14 +1,163 b''
1 # -*- coding: utf-8 -*-
2 """ History related magics and functionality """
1 """ History related magics and functionality """
2 #-----------------------------------------------------------------------------
3 # Copyright (C) 2010 The IPython Development Team.
4 #
5 # Distributed under the terms of the BSD License.
6 #
7 # The full license is in the file COPYING.txt, distributed with this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13 from __future__ import print_function
3
14
4 # Stdlib imports
15 # Stdlib imports
5 import fnmatch
16 import fnmatch
6 import os
17 import os
18 import sys
7
19
20 # Our own packages
8 import IPython.utils.io
21 import IPython.utils.io
22
23 from IPython.core import ipapi
24 from IPython.core.inputlist import InputList
25 from IPython.utils.pickleshare import PickleShareDB
9 from IPython.utils.io import ask_yes_no
26 from IPython.utils.io import ask_yes_no
10 from IPython.utils.warn import warn
27 from IPython.utils.warn import warn
11 from IPython.core import ipapi
28
29 #-----------------------------------------------------------------------------
30 # Classes and functions
31 #-----------------------------------------------------------------------------
32
33 class HistoryManager(object):
34 """A class to organize all history-related functionality in one place.
35 """
36 def __init__(self, shell):
37 """Create a new history manager associated with a shell instance.
38 """
39 self.shell = shell
40
41 # List of input with multi-line handling.
42 self.input_hist = InputList()
43 # This one will hold the 'raw' input history, without any
44 # pre-processing. This will allow users to retrieve the input just as
45 # it was exactly typed in by the user, with %hist -r.
46 self.input_hist_raw = InputList()
47
48 # list of visited directories
49 try:
50 self.dir_hist = [os.getcwd()]
51 except OSError:
52 self.dir_hist = []
53
54 # dict of output history
55 self.output_hist = {}
56
57 # Now the history file
58 if shell.profile:
59 histfname = 'history-%s' % shell.profile
60 else:
61 histfname = 'history'
62 self.hist_file = os.path.join(shell.ipython_dir, histfname)
63
64 # Fill the history zero entry, user counter starts at 1
65 self.input_hist.append('\n')
66 self.input_hist_raw.append('\n')
67
68 # Objects related to shadow history management
69 self._init_shadow_hist()
70
71 # For backwards compatibility, we must put these back in the shell
72 # object, until we've removed all direct uses of the history objects in
73 # the shell itself.
74 shell.input_hist = self.input_hist
75 shell.input_hist_raw = self.input_hist_raw
76 shell.output_hist = self.output_hist
77 shell.dir_hist = self.dir_hist
78 shell.histfile = self.hist_file
79 shell.shadowhist = self.shadow_hist
80 shell.db = self.shadow_db
81
82 def _init_shadow_hist(self):
83 try:
84 self.shadow_db = PickleShareDB(os.path.join(
85 self.shell.ipython_dir, 'db'))
86 except UnicodeDecodeError:
87 print("Your ipython_dir can't be decoded to unicode!")
88 print("Please set HOME environment variable to something that")
89 print(r"only has ASCII characters, e.g. c:\home")
90 print("Now it is", self.ipython_dir)
91 sys.exit()
92 self.shadow_hist = ShadowHist(self.shadow_db)
93
94 def save_hist(self):
95 """Save input history to a file (via readline library)."""
96
97 try:
98 self.shell.readline.write_history_file(self.hist_file)
99 except:
100 print('Unable to save IPython command history to file: ' +
101 `self.hist_file`)
102
103 def reload_hist(self):
104 """Reload the input history from disk file."""
105
106 try:
107 self.shell.readline.clear_history()
108 self.shell.readline.read_history_file(self.hist_file)
109 except AttributeError:
110 pass
111
112 def get_history(self, index=None, raw=False, output=True):
113 """Get the history list.
114
115 Get the input and output history.
116
117 Parameters
118 ----------
119 index : n or (n1, n2) or None
120 If n, then the last entries. If a tuple, then all in
121 range(n1, n2). If None, then all entries. Raises IndexError if
122 the format of index is incorrect.
123 raw : bool
124 If True, return the raw input.
125 output : bool
126 If True, then return the output as well.
127
128 Returns
129 -------
130 If output is True, then return a dict of tuples, keyed by the prompt
131 numbers and with values of (input, output). If output is False, then
132 a dict, keyed by the prompt number with the values of input. Raises
133 IndexError if no history is found.
134 """
135 if raw:
136 input_hist = self.input_hist_raw
137 else:
138 input_hist = self.input_hist
139 if output:
140 output_hist = self.output_hist
141 n = len(input_hist)
142 if index is None:
143 start=0; stop=n
144 elif isinstance(index, int):
145 start=n-index; stop=n
146 elif isinstance(index, tuple) and len(index) == 2:
147 start=index[0]; stop=index[1]
148 else:
149 raise IndexError('Not a valid index for the input history: %r'
150 % index)
151 hist = {}
152 for i in range(start, stop):
153 if output:
154 hist[i] = (input_hist[i], output_hist.get(i))
155 else:
156 hist[i] = input_hist[i]
157 if len(hist)==0:
158 raise IndexError('No history for range of indices: %r' % index)
159 return hist
160
12
161
13 def magic_history(self, parameter_s = ''):
162 def magic_history(self, parameter_s = ''):
14 """Print input history (_i<n> variables), with most recent last.
163 """Print input history (_i<n> variables), with most recent last.
@@ -55,7 +204,7 b" def magic_history(self, parameter_s = ''):"
55 """
204 """
56
205
57 if not self.shell.displayhook.do_full_cache:
206 if not self.shell.displayhook.do_full_cache:
58 print 'This feature is only available if numbered prompts are in use.'
207 print('This feature is only available if numbered prompts are in use.')
59 return
208 return
60 opts,args = self.parse_options(parameter_s,'gnoptsrf:',mode='list')
209 opts,args = self.parse_options(parameter_s,'gnoptsrf:',mode='list')
61
210
@@ -69,7 +218,7 b" def magic_history(self, parameter_s = ''):"
69 else:
218 else:
70 if os.path.exists(outfname):
219 if os.path.exists(outfname):
71 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
220 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
72 print 'Aborting.'
221 print('Aborting.')
73 return
222 return
74
223
75 outfile = open(outfname,'w')
224 outfile = open(outfname,'w')
@@ -103,7 +252,7 b" def magic_history(self, parameter_s = ''):"
103 init, final = map(int, args)
252 init, final = map(int, args)
104 else:
253 else:
105 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
254 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
106 print >> IPython.utils.io.Term.cout, self.magic_hist.__doc__
255 print(self.magic_hist.__doc__, file=IPython.utils.io.Term.cout)
107 return
256 return
108
257
109 width = len(str(final))
258 width = len(str(final))
@@ -117,14 +266,14 b" def magic_history(self, parameter_s = ''):"
117 sh = self.shell.shadowhist.all()
266 sh = self.shell.shadowhist.all()
118 for idx, s in sh:
267 for idx, s in sh:
119 if fnmatch.fnmatch(s, pattern):
268 if fnmatch.fnmatch(s, pattern):
120 print >> outfile, "0%d: %s" %(idx, s.expandtabs(4))
269 print("0%d: %s" %(idx, s.expandtabs(4)), file=outfile)
121 found = True
270 found = True
122
271
123 if found:
272 if found:
124 print >> outfile, "==="
273 print("===", file=outfile)
125 print >> outfile, \
274 print("shadow history ends, fetch by %rep <number> (must start with 0)",
126 "shadow history ends, fetch by %rep <number> (must start with 0)"
275 file=outfile)
127 print >> outfile, "=== start of normal history ==="
276 print("=== start of normal history ===", file=outfile)
128
277
129 for in_num in range(init, final):
278 for in_num in range(init, final):
130 # Print user history with tabs expanded to 4 spaces. The GUI clients
279 # Print user history with tabs expanded to 4 spaces. The GUI clients
@@ -137,22 +286,22 b" def magic_history(self, parameter_s = ''):"
137
286
138 multiline = int(inline.count('\n') > 1)
287 multiline = int(inline.count('\n') > 1)
139 if print_nums:
288 if print_nums:
140 print >> outfile, \
289 print('%s:%s' % (str(in_num).ljust(width), line_sep[multiline]),
141 '%s:%s' % (str(in_num).ljust(width), line_sep[multiline]),
290 file=outfile)
142 if pyprompts:
291 if pyprompts:
143 print >> outfile, '>>>',
292 print('>>>', file=outfile)
144 if multiline:
293 if multiline:
145 lines = inline.splitlines()
294 lines = inline.splitlines()
146 print >> outfile, '\n... '.join(lines)
295 print('\n... '.join(lines), file=outfile)
147 print >> outfile, '... '
296 print('... ', file=outfile)
148 else:
297 else:
149 print >> outfile, inline,
298 print(inline, end='', file=outfile)
150 else:
299 else:
151 print >> outfile, inline,
300 print(inline,end='', file=outfile)
152 if print_outputs:
301 if print_outputs:
153 output = self.shell.output_hist.get(in_num)
302 output = self.shell.output_hist.get(in_num)
154 if output is not None:
303 if output is not None:
155 print >> outfile, repr(output)
304 print(repr(output), file=outfile)
156
305
157 if close_at_end:
306 if close_at_end:
158 outfile.close()
307 outfile.close()
@@ -223,10 +372,10 b' def rep_f(self, arg):'
223
372
224 try:
373 try:
225 lines = self.extract_input_slices(args, True)
374 lines = self.extract_input_slices(args, True)
226 print "lines",lines
375 print("lines", lines)
227 self.runlines(lines)
376 self.runlines(lines)
228 except ValueError:
377 except ValueError:
229 print "Not found in recent history:", args
378 print("Not found in recent history:", args)
230
379
231
380
232 _sentinel = object()
381 _sentinel = object()
@@ -251,11 +400,11 b' class ShadowHist(object):'
251 if old is not _sentinel:
400 if old is not _sentinel:
252 return
401 return
253 newidx = self.inc_idx()
402 newidx = self.inc_idx()
254 #print "new",newidx # dbg
403 #print("new", newidx) # dbg
255 self.db.hset('shadowhist',ent, newidx)
404 self.db.hset('shadowhist',ent, newidx)
256 except:
405 except:
257 ipapi.get().showtraceback()
406 ipapi.get().showtraceback()
258 print "WARNING: disabling shadow history"
407 print("WARNING: disabling shadow history")
259 self.disabled = True
408 self.disabled = True
260
409
261 def all(self):
410 def all(self):
@@ -268,7 +417,6 b' class ShadowHist(object):'
268 all = self.all()
417 all = self.all()
269
418
270 for k, v in all:
419 for k, v in all:
271 #print k,v
272 if k == idx:
420 if k == idx:
273 return v
421 return v
274
422
@@ -45,6 +45,7 b' from IPython.core.displayhook import DisplayHook'
45 from IPython.core.error import TryNext, UsageError
45 from IPython.core.error import TryNext, UsageError
46 from IPython.core.extensions import ExtensionManager
46 from IPython.core.extensions import ExtensionManager
47 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
47 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
48 from IPython.core.history import HistoryManager
48 from IPython.core.inputlist import InputList
49 from IPython.core.inputlist import InputList
49 from IPython.core.inputsplitter import IPythonInputSplitter
50 from IPython.core.inputsplitter import IPythonInputSplitter
50 from IPython.core.logger import Logger
51 from IPython.core.logger import Logger
@@ -216,7 +217,8 b' class InteractiveShell(Configurable, Magic):'
216 extension_manager = Instance('IPython.core.extensions.ExtensionManager')
217 extension_manager = Instance('IPython.core.extensions.ExtensionManager')
217 plugin_manager = Instance('IPython.core.plugin.PluginManager')
218 plugin_manager = Instance('IPython.core.plugin.PluginManager')
218 payload_manager = Instance('IPython.core.payload.PayloadManager')
219 payload_manager = Instance('IPython.core.payload.PayloadManager')
219
220 history_manager = Instance('IPython.core.history.HistoryManager')
221
220 # Private interface
222 # Private interface
221 _post_execute = set()
223 _post_execute = set()
222
224
@@ -261,7 +263,6 b' class InteractiveShell(Configurable, Magic):'
261 self.init_builtins()
263 self.init_builtins()
262
264
263 # pre_config_initialization
265 # pre_config_initialization
264 self.init_shadow_hist()
265
266
266 # The next section should contain everything that was in ipmaker.
267 # The next section should contain everything that was in ipmaker.
267 self.init_logstart()
268 self.init_logstart()
@@ -1211,61 +1212,15 b' class InteractiveShell(Configurable, Magic):'
1211 #-------------------------------------------------------------------------
1212 #-------------------------------------------------------------------------
1212
1213
1213 def init_history(self):
1214 def init_history(self):
1214 # List of input with multi-line handling.
1215 self.history_manager = HistoryManager(shell=self)
1215 self.input_hist = InputList()
1216 # This one will hold the 'raw' input history, without any
1217 # pre-processing. This will allow users to retrieve the input just as
1218 # it was exactly typed in by the user, with %hist -r.
1219 self.input_hist_raw = InputList()
1220
1221 # list of visited directories
1222 try:
1223 self.dir_hist = [os.getcwd()]
1224 except OSError:
1225 self.dir_hist = []
1226
1227 # dict of output history
1228 self.output_hist = {}
1229
1230 # Now the history file
1231 if self.profile:
1232 histfname = 'history-%s' % self.profile
1233 else:
1234 histfname = 'history'
1235 self.histfile = os.path.join(self.ipython_dir, histfname)
1236
1237 # Fill the history zero entry, user counter starts at 1
1238 self.input_hist.append('\n')
1239 self.input_hist_raw.append('\n')
1240
1241 def init_shadow_hist(self):
1242 try:
1243 self.db = pickleshare.PickleShareDB(self.ipython_dir + "/db")
1244 except exceptions.UnicodeDecodeError:
1245 print "Your ipython_dir can't be decoded to unicode!"
1246 print "Please set HOME environment variable to something that"
1247 print r"only has ASCII characters, e.g. c:\home"
1248 print "Now it is", self.ipython_dir
1249 sys.exit()
1250 self.shadowhist = ipcorehist.ShadowHist(self.db)
1251
1216
1252 def savehist(self):
1217 def savehist(self):
1253 """Save input history to a file (via readline library)."""
1218 """Save input history to a file (via readline library)."""
1254
1219 self.history_manager.save_hist()
1255 try:
1220
1256 self.readline.write_history_file(self.histfile)
1257 except:
1258 print 'Unable to save IPython command history to file: ' + \
1259 `self.histfile`
1260
1261 def reloadhist(self):
1221 def reloadhist(self):
1262 """Reload the input history from disk file."""
1222 """Reload the input history from disk file."""
1263
1223 self.history_manager.reload_hist()
1264 try:
1265 self.readline.clear_history()
1266 self.readline.read_history_file(self.shell.histfile)
1267 except AttributeError:
1268 pass
1269
1224
1270 def history_saving_wrapper(self, func):
1225 def history_saving_wrapper(self, func):
1271 """ Wrap func for readline history saving
1226 """ Wrap func for readline history saving
@@ -1286,55 +1241,6 b' class InteractiveShell(Configurable, Magic):'
1286 readline.read_history_file(self.histfile)
1241 readline.read_history_file(self.histfile)
1287 return wrapper
1242 return wrapper
1288
1243
1289 def get_history(self, index=None, raw=False, output=True):
1290 """Get the history list.
1291
1292 Get the input and output history.
1293
1294 Parameters
1295 ----------
1296 index : n or (n1, n2) or None
1297 If n, then the last entries. If a tuple, then all in
1298 range(n1, n2). If None, then all entries. Raises IndexError if
1299 the format of index is incorrect.
1300 raw : bool
1301 If True, return the raw input.
1302 output : bool
1303 If True, then return the output as well.
1304
1305 Returns
1306 -------
1307 If output is True, then return a dict of tuples, keyed by the prompt
1308 numbers and with values of (input, output). If output is False, then
1309 a dict, keyed by the prompt number with the values of input. Raises
1310 IndexError if no history is found.
1311 """
1312 if raw:
1313 input_hist = self.input_hist_raw
1314 else:
1315 input_hist = self.input_hist
1316 if output:
1317 output_hist = self.user_ns['Out']
1318 n = len(input_hist)
1319 if index is None:
1320 start=0; stop=n
1321 elif isinstance(index, int):
1322 start=n-index; stop=n
1323 elif isinstance(index, tuple) and len(index) == 2:
1324 start=index[0]; stop=index[1]
1325 else:
1326 raise IndexError('Not a valid index for the input history: %r'
1327 % index)
1328 hist = {}
1329 for i in range(start, stop):
1330 if output:
1331 hist[i] = (input_hist[i], output_hist.get(i))
1332 else:
1333 hist[i] = input_hist[i]
1334 if len(hist)==0:
1335 raise IndexError('No history for range of indices: %r' % index)
1336 return hist
1337
1338 #-------------------------------------------------------------------------
1244 #-------------------------------------------------------------------------
1339 # Things related to exception handling and tracebacks (not debugging)
1245 # Things related to exception handling and tracebacks (not debugging)
1340 #-------------------------------------------------------------------------
1246 #-------------------------------------------------------------------------
@@ -2200,6 +2106,20 b' class InteractiveShell(Configurable, Magic):'
2200 self.input_hist_raw.append(cell)
2106 self.input_hist_raw.append(cell)
2201 self.input_hist.append(ipy_cell)
2107 self.input_hist.append(ipy_cell)
2202
2108
2109
2110 # dbg code!!!
2111 def myapp(self, val): # dbg
2112 import traceback as tb
2113 stack = ''.join(tb.format_stack())
2114 print 'Value:', val
2115 print 'Stack:\n', stack
2116 list.append(self, val)
2117
2118 import new
2119 self.input_hist.append = new.instancemethod(myapp, self.input_hist,
2120 list)
2121 # End dbg
2122
2203 # All user code execution must happen with our context managers active
2123 # All user code execution must happen with our context managers active
2204 with nested(self.builtin_trap, self.display_trap):
2124 with nested(self.builtin_trap, self.display_trap):
2205 # Single-block input should behave like an interactive prompt
2125 # Single-block input should behave like an interactive prompt
General Comments 0
You need to be logged in to leave comments. Login now