##// END OF EJS Templates
History refactored and saved to json file...
Satrajit Ghosh -
Show More
@@ -0,0 +1,48 b''
1 """Tests for the IPython tab-completion machinery.
2 """
3 #-----------------------------------------------------------------------------
4 # Module imports
5 #-----------------------------------------------------------------------------
6
7 # stdlib
8 import os
9 import sys
10 import unittest
11
12 # third party
13 import nose.tools as nt
14
15 # our own packages
16 from IPython.utils.tempdir import TemporaryDirectory
17 from IPython.core.history import HistoryManager
18
19 def test_history():
20
21 ip = get_ipython()
22 with TemporaryDirectory() as tmpdir:
23 #tmpdir = '/software/temp'
24 histfile = os.path.realpath(os.path.join(tmpdir, 'history.json'))
25 # Ensure that we restore the history management that we mess with in
26 # this test doesn't affect the IPython instance used by the test suite
27 # beyond this test.
28 hist_manager_ori = ip.history_manager
29 try:
30 ip.history_manager = HistoryManager(ip)
31 ip.history_manager.hist_file = histfile
32 print 'test',histfile
33 hist = ['a=1\n', 'def f():\n test = 1\n return test\n', 'b=2\n']
34 # test save and load
35 ip.history_manager.input_hist_raw[:] = []
36 for h in hist:
37 ip.history_manager.input_hist_raw.append(h)
38 ip.save_history()
39 ip.history_manager.input_hist_raw[:] = []
40 ip.reload_history()
41 print type(ip.history_manager.input_hist_raw)
42 print ip.history_manager.input_hist_raw
43 nt.assert_equal(len(ip.history_manager.input_hist_raw), len(hist))
44 for i,h in enumerate(hist):
45 nt.assert_equal(hist[i], ip.history_manager.input_hist_raw[i])
46 finally:
47 # Restore history manager
48 ip.history_manager = hist_manager_ori
@@ -90,6 +90,8 b' c = get_config()'
90 90
91 91 # c.InteractiveShell.quiet = False
92 92
93 # c.InteractiveShell.history_length = 10000
94
93 95 # Readline
94 96 # c.InteractiveShell.readline_use = True
95 97
@@ -173,7 +173,7 b' class DisplayHook(Configurable):'
173 173 """Should we silence the display hook because of ';'?"""
174 174 # do not print output if input ends in ';'
175 175 try:
176 if self.shell.input_hist[self.prompt_count].endswith(';\n'):
176 if self.shell.history_manager.input_hist_parsed[self.prompt_count].endswith(';\n'):
177 177 return True
178 178 except IndexError:
179 179 # some uses of ipshellembed may fail here
@@ -14,6 +14,7 b' from __future__ import print_function'
14 14
15 15 # Stdlib imports
16 16 import fnmatch
17 import json
17 18 import os
18 19 import sys
19 20
@@ -37,7 +38,7 b' class HistoryManager(object):'
37 38 # An instance of the IPython shell we are attached to
38 39 shell = None
39 40 # An InputList instance to hold processed history
40 input_hist = None
41 input_hist_parsed = None
41 42 # An InputList instance to hold raw history (as typed by user)
42 43 input_hist_raw = None
43 44 # A list of directories visited during session
@@ -64,11 +65,11 b' class HistoryManager(object):'
64 65 self.shell = shell
65 66
66 67 # List of input with multi-line handling.
67 self.input_hist = InputList()
68 self.input_hist_parsed = []
68 69 # This one will hold the 'raw' input history, without any
69 70 # pre-processing. This will allow users to retrieve the input just as
70 71 # it was exactly typed in by the user, with %hist -r.
71 self.input_hist_raw = InputList()
72 self.input_hist_raw = []
72 73
73 74 # list of visited directories
74 75 try:
@@ -84,7 +85,7 b' class HistoryManager(object):'
84 85 histfname = 'history-%s' % shell.profile
85 86 else:
86 87 histfname = 'history'
87 self.hist_file = os.path.join(shell.ipython_dir, histfname)
88 self.hist_file = os.path.join(shell.ipython_dir, histfname + '.json')
88 89
89 90 # Objects related to shadow history management
90 91 self._init_shadow_hist()
@@ -95,18 +96,7 b' class HistoryManager(object):'
95 96
96 97 # Fill the history zero entry, user counter starts at 1
97 98 self.store_inputs('\n', '\n')
98
99 # For backwards compatibility, we must put these back in the shell
100 # object, until we've removed all direct uses of the history objects in
101 # the shell itself.
102 shell.input_hist = self.input_hist
103 shell.input_hist_raw = self.input_hist_raw
104 shell.output_hist = self.output_hist
105 shell.dir_hist = self.dir_hist
106 shell.histfile = self.hist_file
107 shell.shadowhist = self.shadow_hist
108 shell.db = self.shadow_db
109
99
110 100 def _init_shadow_hist(self):
111 101 try:
112 102 self.shadow_db = PickleShareDB(os.path.join(
@@ -119,24 +109,41 b' class HistoryManager(object):'
119 109 sys.exit()
120 110 self.shadow_hist = ShadowHist(self.shadow_db, self.shell)
121 111
122 def save_hist(self):
123 """Save input history to a file (via readline library)."""
112 def populate_readline_history(self):
113 """Populate the readline history from the raw history.
124 114
125 try:
126 self.shell.readline.write_history_file(self.hist_file)
127 except:
128 print('Unable to save IPython command history to file: ' +
129 `self.hist_file`)
130
131 def reload_hist(self):
132 """Reload the input history from disk file."""
115 We only store one copy of the raw history, which is persisted to a json
116 file on disk. The readline history is repopulated from the contents of
117 this file."""
133 118
134 119 try:
135 120 self.shell.readline.clear_history()
136 self.shell.readline.read_history_file(self.hist_file)
137 121 except AttributeError:
138 122 pass
123 else:
124 for h in self.input_hist_raw:
125 if not h.isspace():
126 for line in h.splitlines():
127 self.shell.readline.add_history(line)
128
129 def save_history(self):
130 """Save input history to a file (via readline library)."""
131 hist = dict(raw=self.input_hist_raw, #[-self.shell.history_length:],
132 parsed=self.input_hist_parsed) #[-self.shell.history_length:])
133 with open(self.hist_file,'wt') as hfile:
134 json.dump(hist, hfile,
135 sort_keys=True, indent=4)
136
137 def reload_history(self):
138 """Reload the input history from disk file."""
139 139
140 with open(self.hist_file,'rt') as hfile:
141 hist = json.load(hfile)
142 self.input_hist_parsed = hist['parsed']
143 self.input_hist_raw = hist['raw']
144 if self.shell.has_readline:
145 self.populate_readline_history()
146
140 147 def get_history(self, index=None, raw=False, output=True):
141 148 """Get the history list.
142 149
@@ -163,7 +170,7 b' class HistoryManager(object):'
163 170 if raw:
164 171 input_hist = self.input_hist_raw
165 172 else:
166 input_hist = self.input_hist
173 input_hist = self.input_hist_parsed
167 174 if output:
168 175 output_hist = self.output_hist
169 176 n = len(input_hist)
@@ -201,7 +208,7 b' class HistoryManager(object):'
201 208 """
202 209 if source_raw is None:
203 210 source_raw = source
204 self.input_hist.append(source)
211 self.input_hist_parsed.append(source)
205 212 self.input_hist_raw.append(source_raw)
206 213 self.shadow_hist.add(source)
207 214
@@ -221,12 +228,12 b' class HistoryManager(object):'
221 228
222 229 def sync_inputs(self):
223 230 """Ensure raw and translated histories have same length."""
224 if len(self.input_hist) != len (self.input_hist_raw):
225 self.input_hist_raw = InputList(self.input_hist)
231 if len(self.input_hist_parsed) != len (self.input_hist_raw):
232 self.input_hist_raw = InputList(self.input_hist_parsed)
226 233
227 234 def reset(self):
228 235 """Clear all histories managed by this object."""
229 self.input_hist[:] = []
236 self.input_hist_parsed[:] = []
230 237 self.input_hist_raw[:] = []
231 238 self.output_hist.clear()
232 239 # The directory history can't be completely empty
@@ -299,12 +306,12 b" def magic_history(self, parameter_s = ''):"
299 306 close_at_end = True
300 307
301 308 if 't' in opts:
302 input_hist = self.shell.input_hist
309 input_hist = self.shell.history_manager.input_hist_parsed
303 310 elif 'r' in opts:
304 input_hist = self.shell.input_hist_raw
311 input_hist = self.shell.history_manager.input_hist_raw
305 312 else:
306 313 # Raw history is the default
307 input_hist = self.shell.input_hist_raw
314 input_hist = self.shell.history_manager.input_hist_raw
308 315
309 316 default_length = 40
310 317 pattern = None
@@ -337,7 +344,7 b" def magic_history(self, parameter_s = ''):"
337 344
338 345 found = False
339 346 if pattern is not None:
340 sh = self.shell.shadowhist.all()
347 sh = self.shell.history_manager.shadowhist.all()
341 348 for idx, s in sh:
342 349 if fnmatch.fnmatch(s, pattern):
343 350 print("0%d: %s" %(idx, s.expandtabs(4)), file=outfile)
@@ -373,7 +380,7 b" def magic_history(self, parameter_s = ''):"
373 380 else:
374 381 print(inline, end='', file=outfile)
375 382 if print_outputs:
376 output = self.shell.output_hist.get(in_num)
383 output = self.shell.history_manager.output_hist.get(in_num)
377 384 if output is not None:
378 385 print(repr(output), file=outfile)
379 386
@@ -45,7 +45,6 b' from IPython.core.error import TryNext, UsageError'
45 45 from IPython.core.extensions import ExtensionManager
46 46 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
47 47 from IPython.core.history import HistoryManager
48 from IPython.core.inputlist import InputList
49 48 from IPython.core.inputsplitter import IPythonInputSplitter
50 49 from IPython.core.logger import Logger
51 50 from IPython.core.magic import Magic
@@ -176,6 +175,8 b' class InteractiveShell(Configurable, Magic):'
176 175 prompts_pad_left = CBool(True, config=True)
177 176 quiet = CBool(False, config=True)
178 177
178 history_length = Int(10000, config=True)
179
179 180 # The readline stuff will eventually be moved to the terminal subclass
180 181 # but for now, we can't do that as readline is welded in everywhere.
181 182 readline_use = CBool(True, config=True)
@@ -293,6 +294,14 b' class InteractiveShell(Configurable, Magic):'
293 294 self.hooks.late_startup_hook()
294 295 atexit.register(self.atexit_operations)
295 296
297 # While we're trying to have each part of the code directly access what it
298 # needs without keeping redundant references to objects, we have too much
299 # legacy code that expects ip.db to exist, so let's make it a property that
300 # retrieves the underlying object from our new history manager.
301 @property
302 def db(self):
303 return self.history_manager.shadow_db
304
296 305 @classmethod
297 306 def instance(cls, *args, **kwargs):
298 307 """Returns a global InteractiveShell instance."""
@@ -947,16 +956,16 b' class InteractiveShell(Configurable, Magic):'
947 956 warn('help() not available - check site.py')
948 957
949 958 # make global variables for user access to the histories
950 ns['_ih'] = self.input_hist
951 ns['_oh'] = self.output_hist
952 ns['_dh'] = self.dir_hist
959 ns['_ih'] = self.history_manager.input_hist_parsed
960 ns['_oh'] = self.history_manager.output_hist
961 ns['_dh'] = self.history_manager.dir_hist
953 962
954 963 ns['_sh'] = shadowns
955 964
956 965 # user aliases to input and output histories. These shouldn't show up
957 966 # in %who, as they can have very large reprs.
958 ns['In'] = self.input_hist
959 ns['Out'] = self.output_hist
967 ns['In'] = self.history_manager.input_hist_parsed
968 ns['Out'] = self.history_manager.output_hist
960 969
961 970 # Store myself as the public api!!!
962 971 ns['get_ipython'] = self.get_ipython
@@ -1228,19 +1237,13 b' class InteractiveShell(Configurable, Magic):'
1228 1237 def init_history(self):
1229 1238 self.history_manager = HistoryManager(shell=self)
1230 1239
1231 def save_hist(self):
1240 def save_history(self):
1232 1241 """Save input history to a file (via readline library)."""
1233 self.history_manager.save_hist()
1242 self.history_manager.save_history()
1234 1243
1235 # For backwards compatibility
1236 savehist = save_hist
1237
1238 def reload_hist(self):
1244 def reload_history(self):
1239 1245 """Reload the input history from disk file."""
1240 self.history_manager.reload_hist()
1241
1242 # For backwards compatibility
1243 reloadhist = reload_hist
1246 self.history_manager.reload_history()
1244 1247
1245 1248 def history_saving_wrapper(self, func):
1246 1249 """ Wrap func for readline history saving
@@ -1254,11 +1257,11 b' class InteractiveShell(Configurable, Magic):'
1254 1257 return func
1255 1258
1256 1259 def wrapper():
1257 self.save_hist()
1260 self.save_history()
1258 1261 try:
1259 1262 func()
1260 1263 finally:
1261 readline.read_history_file(self.histfile)
1264 self.reload_history()
1262 1265 return wrapper
1263 1266
1264 1267 #-------------------------------------------------------------------------
@@ -1488,8 +1491,6 b' class InteractiveShell(Configurable, Magic):'
1488 1491 self.has_readline = False
1489 1492 self.readline = None
1490 1493 # Set a number of methods that depend on readline to be no-op
1491 self.save_hist = no_op
1492 self.reload_hist = no_op
1493 1494 self.set_readline_completer = no_op
1494 1495 self.set_custom_completer = no_op
1495 1496 self.set_completer_frame = no_op
@@ -1541,17 +1542,13 b' class InteractiveShell(Configurable, Magic):'
1541 1542 delims = delims.replace(ESC_MAGIC, '')
1542 1543 readline.set_completer_delims(delims)
1543 1544 # otherwise we end up with a monster history after a while:
1544 readline.set_history_length(1000)
1545 readline.set_history_length(self.history_length)
1545 1546 try:
1546 1547 #print '*** Reading readline history' # dbg
1547 readline.read_history_file(self.histfile)
1548 self.reload_history()
1548 1549 except IOError:
1549 1550 pass # It doesn't exist yet.
1550 1551
1551 # If we have readline, we want our history saved upon ipython
1552 # exiting.
1553 atexit.register(self.save_hist)
1554
1555 1552 # Configure auto-indent for all platforms
1556 1553 self.set_autoindent(self.autoindent)
1557 1554
@@ -2109,7 +2106,8 b' class InteractiveShell(Configurable, Magic):'
2109 2106 list.append(self, val)
2110 2107
2111 2108 import new
2112 self.input_hist.append = types.MethodType(myapp, self.input_hist)
2109 self.history_manager.input_hist_parsed.append = types.MethodType(myapp,
2110 self.history_manager.input_hist_parsed)
2113 2111 # End dbg
2114 2112
2115 2113 # All user code execution must happen with our context managers active
@@ -2521,6 +2519,9 b' class InteractiveShell(Configurable, Magic):'
2521 2519 except OSError:
2522 2520 pass
2523 2521
2522
2523 self.save_history()
2524
2524 2525 # Clear all user namespaces to release all references cleanly.
2525 2526 self.reset()
2526 2527
@@ -186,9 +186,9 b' python-profiler package from non-free.""")'
186 186 N-M -> include items N..M (closed endpoint)."""
187 187
188 188 if raw:
189 hist = self.shell.input_hist_raw
189 hist = self.shell.history_manager.input_hist_raw
190 190 else:
191 hist = self.shell.input_hist
191 hist = self.shell.history_manager.input_hist_parsed
192 192
193 193 cmds = []
194 194 for chunk in slices:
@@ -200,7 +200,7 b' python-profiler package from non-free.""")'
200 200 else:
201 201 ini = int(chunk)
202 202 fin = ini+1
203 cmds.append(hist[ini:fin])
203 cmds.append(''.join(hist[ini:fin]))
204 204 return cmds
205 205
206 206 def arg_err(self,func):
@@ -1106,19 +1106,19 b' Currently the magic system has the following functions:\\n"""'
1106 1106 logger.timestamp = False
1107 1107
1108 1108 if log_raw_input:
1109 input_hist = self.shell.input_hist_raw
1109 input_hist = self.shell.history_manager.input_hist_raw
1110 1110 else:
1111 input_hist = self.shell.input_hist
1111 input_hist = self.shell.history_manager.input_hist_parsed
1112 1112
1113 1113 if log_output:
1114 1114 log_write = logger.log_write
1115 output_hist = self.shell.output_hist
1115 output_hist = self.shell.history_manager.output_hist
1116 1116 for n in range(1,len(input_hist)-1):
1117 1117 log_write(input_hist[n].rstrip())
1118 1118 if n in output_hist:
1119 1119 log_write(repr(output_hist[n]),'output')
1120 1120 else:
1121 logger.log_write(input_hist[1:])
1121 logger.log_write(''.join(input_hist[1:]))
1122 1122 if timestamp:
1123 1123 # re-enable timestamping
1124 1124 logger.timestamp = True
@@ -1551,7 +1551,7 b' Currently the magic system has the following functions:\\n"""'
1551 1551
1552 1552 stats = None
1553 1553 try:
1554 self.shell.save_hist()
1554 self.shell.save_history()
1555 1555
1556 1556 if opts.has_key('p'):
1557 1557 stats = self.magic_prun('',0,opts,arg_lst,prog_ns)
@@ -1670,7 +1670,7 b' Currently the magic system has the following functions:\\n"""'
1670 1670 # contained therein.
1671 1671 del sys.modules[main_mod_name]
1672 1672
1673 self.shell.reload_hist()
1673 self.shell.reload_history()
1674 1674
1675 1675 return stats
1676 1676
@@ -2980,8 +2980,8 b' Defaulting color scheme to \'NoColor\'"""'
2980 2980 else:
2981 2981 start_magic = start
2982 2982 # Look through the input history in reverse
2983 for n in range(len(self.shell.input_hist)-2,0,-1):
2984 input = self.shell.input_hist[n]
2983 for n in range(len(self.shell.history_manager.input_hist_parsed)-2,0,-1):
2984 input = self.shell.history_manager.input_hist_parsed[n]
2985 2985 # skip plain 'r' lines so we don't recurse to infinity
2986 2986 if input != '_ip.magic("r")\n' and \
2987 2987 (input.startswith(start) or input.startswith(start_magic)):
@@ -332,14 +332,14 b' class TerminalInteractiveShell(InteractiveShell):'
332 332 if self.has_readline and self.readline_use:
333 333 histlen = self.readline.get_current_history_length()
334 334 if histlen > 1:
335 newhist = self.input_hist_raw[-1].rstrip()
335 newhist = self.history_manager.input_hist_raw[-1].rstrip()
336 336 self.readline.remove_history_item(histlen-1)
337 337 self.readline.replace_history_item(histlen-2,
338 338 newhist.encode(self.stdin_encoding))
339 339 else:
340 self.input_hist_raw.append('%s\n' % line)
340 self.history_manager.input_hist_raw.append('%s\n' % line)
341 341 elif not continue_prompt:
342 self.input_hist_raw.append('\n')
342 self.history_manager.input_hist_raw.append('\n')
343 343 try:
344 344 lineout = self.prefilter_manager.prefilter_lines(line,continue_prompt)
345 345 except:
@@ -50,8 +50,8 b' def clear_f(self,arg):'
50 50 del user_ns[key]
51 51 except: pass
52 52 # must be done in-place
53 self.input_hist[:] = ['\n'] * pc
54 self.input_hist_raw[:] = ['\n'] * pc
53 self.history_manager.input_hist_parsed[:] = ['\n'] * pc
54 self.history_manager.input_hist_raw[:] = ['\n'] * pc
55 55
56 56 elif target == 'array':
57 57 # Support cleaning up numpy arrays
@@ -118,7 +118,7 b" def jot_obj(self, obj, name, comment=''):"
118 118
119 119 # which one works better?
120 120 #all = ip.shadowhist.all()
121 all = ip.shell.input_hist
121 all = ip.shell.history_manager.input_hist_parsed
122 122
123 123 # We may actually want to make snapshot of files that are run-ned.
124 124
General Comments 0
You need to be logged in to leave comments. Login now