##// 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 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 15 # Stdlib imports
5 16 import fnmatch
6 17 import os
18 import sys
7 19
20 # Our own packages
8 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 26 from IPython.utils.io import ask_yes_no
10 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 162 def magic_history(self, parameter_s = ''):
14 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 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 208 return
60 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 218 else:
70 219 if os.path.exists(outfname):
71 220 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
72 print 'Aborting.'
221 print('Aborting.')
73 222 return
74 223
75 224 outfile = open(outfname,'w')
@@ -103,7 +252,7 b" def magic_history(self, parameter_s = ''):"
103 252 init, final = map(int, args)
104 253 else:
105 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 256 return
108 257
109 258 width = len(str(final))
@@ -117,14 +266,14 b" def magic_history(self, parameter_s = ''):"
117 266 sh = self.shell.shadowhist.all()
118 267 for idx, s in sh:
119 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 270 found = True
122 271
123 272 if found:
124 print >> outfile, "==="
125 print >> outfile, \
126 "shadow history ends, fetch by %rep <number> (must start with 0)"
127 print >> outfile, "=== start of normal history ==="
273 print("===", file=outfile)
274 print("shadow history ends, fetch by %rep <number> (must start with 0)",
275 file=outfile)
276 print("=== start of normal history ===", file=outfile)
128 277
129 278 for in_num in range(init, final):
130 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 287 multiline = int(inline.count('\n') > 1)
139 288 if print_nums:
140 print >> outfile, \
141 '%s:%s' % (str(in_num).ljust(width), line_sep[multiline]),
289 print('%s:%s' % (str(in_num).ljust(width), line_sep[multiline]),
290 file=outfile)
142 291 if pyprompts:
143 print >> outfile, '>>>',
292 print('>>>', file=outfile)
144 293 if multiline:
145 294 lines = inline.splitlines()
146 print >> outfile, '\n... '.join(lines)
147 print >> outfile, '... '
295 print('\n... '.join(lines), file=outfile)
296 print('... ', file=outfile)
148 297 else:
149 print >> outfile, inline,
298 print(inline, end='', file=outfile)
150 299 else:
151 print >> outfile, inline,
300 print(inline,end='', file=outfile)
152 301 if print_outputs:
153 302 output = self.shell.output_hist.get(in_num)
154 303 if output is not None:
155 print >> outfile, repr(output)
304 print(repr(output), file=outfile)
156 305
157 306 if close_at_end:
158 307 outfile.close()
@@ -223,10 +372,10 b' def rep_f(self, arg):'
223 372
224 373 try:
225 374 lines = self.extract_input_slices(args, True)
226 print "lines",lines
375 print("lines", lines)
227 376 self.runlines(lines)
228 377 except ValueError:
229 print "Not found in recent history:", args
378 print("Not found in recent history:", args)
230 379
231 380
232 381 _sentinel = object()
@@ -251,11 +400,11 b' class ShadowHist(object):'
251 400 if old is not _sentinel:
252 401 return
253 402 newidx = self.inc_idx()
254 #print "new",newidx # dbg
403 #print("new", newidx) # dbg
255 404 self.db.hset('shadowhist',ent, newidx)
256 405 except:
257 406 ipapi.get().showtraceback()
258 print "WARNING: disabling shadow history"
407 print("WARNING: disabling shadow history")
259 408 self.disabled = True
260 409
261 410 def all(self):
@@ -268,7 +417,6 b' class ShadowHist(object):'
268 417 all = self.all()
269 418
270 419 for k, v in all:
271 #print k,v
272 420 if k == idx:
273 421 return v
274 422
@@ -45,6 +45,7 b' from IPython.core.displayhook import DisplayHook'
45 45 from IPython.core.error import TryNext, UsageError
46 46 from IPython.core.extensions import ExtensionManager
47 47 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
48 from IPython.core.history import HistoryManager
48 49 from IPython.core.inputlist import InputList
49 50 from IPython.core.inputsplitter import IPythonInputSplitter
50 51 from IPython.core.logger import Logger
@@ -216,7 +217,8 b' class InteractiveShell(Configurable, Magic):'
216 217 extension_manager = Instance('IPython.core.extensions.ExtensionManager')
217 218 plugin_manager = Instance('IPython.core.plugin.PluginManager')
218 219 payload_manager = Instance('IPython.core.payload.PayloadManager')
219
220 history_manager = Instance('IPython.core.history.HistoryManager')
221
220 222 # Private interface
221 223 _post_execute = set()
222 224
@@ -261,7 +263,6 b' class InteractiveShell(Configurable, Magic):'
261 263 self.init_builtins()
262 264
263 265 # pre_config_initialization
264 self.init_shadow_hist()
265 266
266 267 # The next section should contain everything that was in ipmaker.
267 268 self.init_logstart()
@@ -1211,61 +1212,15 b' class InteractiveShell(Configurable, Magic):'
1211 1212 #-------------------------------------------------------------------------
1212 1213
1213 1214 def init_history(self):
1214 # List of input with multi-line handling.
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)
1215 self.history_manager = HistoryManager(shell=self)
1251 1216
1252 1217 def savehist(self):
1253 1218 """Save input history to a file (via readline library)."""
1254
1255 try:
1256 self.readline.write_history_file(self.histfile)
1257 except:
1258 print 'Unable to save IPython command history to file: ' + \
1259 `self.histfile`
1260
1219 self.history_manager.save_hist()
1220
1261 1221 def reloadhist(self):
1262 1222 """Reload the input history from disk file."""
1263
1264 try:
1265 self.readline.clear_history()
1266 self.readline.read_history_file(self.shell.histfile)
1267 except AttributeError:
1268 pass
1223 self.history_manager.reload_hist()
1269 1224
1270 1225 def history_saving_wrapper(self, func):
1271 1226 """ Wrap func for readline history saving
@@ -1286,55 +1241,6 b' class InteractiveShell(Configurable, Magic):'
1286 1241 readline.read_history_file(self.histfile)
1287 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 1245 # Things related to exception handling and tracebacks (not debugging)
1340 1246 #-------------------------------------------------------------------------
@@ -2200,6 +2106,20 b' class InteractiveShell(Configurable, Magic):'
2200 2106 self.input_hist_raw.append(cell)
2201 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 2123 # All user code execution must happen with our context managers active
2204 2124 with nested(self.builtin_trap, self.display_trap):
2205 2125 # Single-block input should behave like an interactive prompt
General Comments 0
You need to be logged in to leave comments. Login now