##// END OF EJS Templates
Allow history to store multiple outputs for a single input line.
Thomas Kluyver -
Show More
@@ -275,16 +275,13 b' class DisplayHook(Configurable):'
275 new_result = '_'+`self.prompt_count`
275 new_result = '_'+`self.prompt_count`
276 to_main[new_result] = result
276 to_main[new_result] = result
277 self.shell.user_ns.update(to_main)
277 self.shell.user_ns.update(to_main)
278 self.shell.user_ns['_oh'][self.prompt_count] = result
278 # This is a defaultdict of lists, so we can always append
279 self.shell.user_ns['_oh'][self.prompt_count].append(result)
279
280
280 def log_output(self, format_dict):
281 def log_output(self, format_dict):
281 """Log the output."""
282 """Log the output."""
282 if self.shell.logger.log_output:
283 if self.shell.logger.log_output:
283 self.shell.logger.log_write(format_dict['text/plain'], 'output')
284 self.shell.logger.log_write(format_dict['text/plain'], 'output')
284 # Write output to the database. Does nothing unless history
285 # output logging is enabled.
286 self.shell.history_manager.store_output(self.prompt_count,
287 format_dict['text/plain'])
288
285
289 def finish_displayhook(self):
286 def finish_displayhook(self):
290 """Finish up all displayhook activities."""
287 """Finish up all displayhook activities."""
@@ -14,10 +14,13 b' from __future__ import print_function'
14
14
15 # Stdlib imports
15 # Stdlib imports
16 import datetime
16 import datetime
17 import json
17 import os
18 import os
18 import re
19 import re
19 import sqlite3
20 import sqlite3
20
21
22 from collections import defaultdict
23
21 # Our own packages
24 # Our own packages
22 from IPython.config.configurable import Configurable
25 from IPython.config.configurable import Configurable
23 import IPython.utils.io
26 import IPython.utils.io
@@ -45,7 +48,7 b' class HistoryManager(Configurable):'
45 # A list of directories visited during session
48 # A list of directories visited during session
46 dir_hist = List()
49 dir_hist = List()
47 # A dict of output history, keyed with ints from the shell's execution count
50 # A dict of output history, keyed with ints from the shell's execution count
48 output_hist = Dict()
51 output_hist = Instance(defaultdict)
49 # String holding the path to the history file
52 # String holding the path to the history file
50 hist_file = Unicode()
53 hist_file = Unicode()
51 # The SQLite database
54 # The SQLite database
@@ -94,6 +97,7 b' class HistoryManager(Configurable):'
94 self.new_session()
97 self.new_session()
95
98
96 self._i00, self._i, self._ii, self._iii = '','','',''
99 self._i00, self._i, self._ii, self._iii = '','','',''
100 self.output_hist = defaultdict(list)
97
101
98 self._exit_commands = set(['Quit', 'quit', 'Exit', 'exit', '%Quit',
102 self._exit_commands = set(['Quit', 'quit', 'Exit', 'exit', '%Quit',
99 '%quit', '%Exit', '%exit'])
103 '%quit', '%Exit', '%exit'])
@@ -179,8 +183,10 b' class HistoryManager(Configurable):'
179 toget = "history.%s, output_history.output" % toget
183 toget = "history.%s, output_history.output" % toget
180 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
184 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
181 (toget, sqlfrom) + sql, params)
185 (toget, sqlfrom) + sql, params)
182 if output: # Regroup into 3-tuples
186 if output: # Regroup into 3-tuples, and parse JSON
183 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
187 loads = lambda out: json.loads(out) if out else None
188 return ((ses, lin, (inp, loads(out))) \
189 for ses, lin, inp, out in cur)
184 return cur
190 return cur
185
191
186
192
@@ -221,7 +227,8 b' class HistoryManager(Configurable):'
221
227
222 for i in range(start, stop):
228 for i in range(start, stop):
223 if output:
229 if output:
224 line = (input_hist[i], repr(self.output_hist.get(i)))
230 output_item = [repr(x) for x in self.output_hist[i]]
231 line = (input_hist[i], output_item)
225 else:
232 else:
226 line = input_hist[i]
233 line = input_hist[i]
227 yield (0, i, line)
234 yield (0, i, line)
@@ -324,9 +331,10 b' class HistoryManager(Configurable):'
324 new_i : self._i00 }
331 new_i : self._i00 }
325 self.shell.user_ns.update(to_main)
332 self.shell.user_ns.update(to_main)
326
333
327 def store_output(self, line_num, output):
334 def store_output(self, line_num):
328 if not self.db_log_output:
335 if (not self.db_log_output) or not self.output_hist[line_num]:
329 return
336 return
337 output = json.dumps([repr(x) for x in self.output_hist[line_num]])
330 db_row = (self.session_number, line_num, output)
338 db_row = (self.session_number, line_num, output)
331 if self.db_cache_size > 1:
339 if self.db_cache_size > 1:
332 self.db_output_cache.append(db_row)
340 self.db_output_cache.append(db_row)
@@ -524,7 +532,7 b" def magic_history(self, parameter_s = ''):"
524 inline = "\n... ".join(inline.splitlines()) + "\n..."
532 inline = "\n... ".join(inline.splitlines()) + "\n..."
525 print(inline, file=outfile)
533 print(inline, file=outfile)
526 if get_output and output:
534 if get_output and output:
527 print(output, file=outfile)
535 print("\n".join(output), file=outfile)
528
536
529 if close_at_end:
537 if close_at_end:
530 outfile.close()
538 outfile.close()
@@ -2119,8 +2119,11 b' class InteractiveShell(Configurable, Magic):'
2119
2119
2120 # Single-block input should behave like an interactive prompt
2120 # Single-block input should behave like an interactive prompt
2121 if len(blocks) == 1:
2121 if len(blocks) == 1:
2122 # since we return here, we need to update the execution count
2123 out = self.run_source(blocks[0])
2122 out = self.run_source(blocks[0])
2123 # Write output to the database. Does nothing unless
2124 # history output logging is enabled.
2125 self.history_manager.store_output(self.execution_count)
2126 # since we return here, we need to update the execution count
2124 self.execution_count += 1
2127 self.execution_count += 1
2125 return out
2128 return out
2126
2129
@@ -2148,6 +2151,9 b' class InteractiveShell(Configurable, Magic):'
2148 # processed input in history
2151 # processed input in history
2149 self.run_source(ipy_cell, symbol='exec')
2152 self.run_source(ipy_cell, symbol='exec')
2150
2153
2154 # Write output to the database. Does nothing unless
2155 # history output logging is enabled.
2156 self.history_manager.store_output(self.execution_count)
2151 # Each cell is a *single* input, regardless of how many lines it has
2157 # Each cell is a *single* input, regardless of how many lines it has
2152 self.execution_count += 1
2158 self.execution_count += 1
2153
2159
@@ -38,7 +38,8 b' def test_history():'
38
38
39 ip.history_manager.db_log_output = True
39 ip.history_manager.db_log_output = True
40 # Doesn't match the input, but we'll just check it's stored.
40 # Doesn't match the input, but we'll just check it's stored.
41 ip.history_manager.store_output(3, "spam")
41 ip.history_manager.output_hist[3].append("spam")
42 ip.history_manager.store_output(3)
42
43
43 nt.assert_equal(ip.history_manager.input_hist_raw, [''] + hist)
44 nt.assert_equal(ip.history_manager.input_hist_raw, [''] + hist)
44
45
@@ -59,7 +60,7 b' def test_history():'
59
60
60 # Check get_hist_tail
61 # Check get_hist_tail
61 gothist = ip.history_manager.get_hist_tail(4, output=True)
62 gothist = ip.history_manager.get_hist_tail(4, output=True)
62 expected = [(1, 3, (hist[-1], "spam")),
63 expected = [(1, 3, (hist[-1], [repr("spam")])),
63 (2, 1, (newcmds[0], None)),
64 (2, 1, (newcmds[0], None)),
64 (2, 2, (newcmds[1], None)),
65 (2, 2, (newcmds[1], None)),
65 (2, 3, (newcmds[2], None)),]
66 (2, 3, (newcmds[2], None)),]
@@ -69,7 +70,7 b' def test_history():'
69 gothist = ip.history_manager.get_hist_search("*test*")
70 gothist = ip.history_manager.get_hist_search("*test*")
70 nt.assert_equal(list(gothist), [(1,2,hist[1])] )
71 nt.assert_equal(list(gothist), [(1,2,hist[1])] )
71 gothist = ip.history_manager.get_hist_search("b*", output=True)
72 gothist = ip.history_manager.get_hist_search("b*", output=True)
72 nt.assert_equal(list(gothist), [(1,3,(hist[2],"spam"))] )
73 nt.assert_equal(list(gothist), [(1,3,(hist[2],[repr("spam")]))] )
73
74
74 # Cross testing: check that magic %save can get previous session.
75 # Cross testing: check that magic %save can get previous session.
75 testfilename = os.path.realpath(os.path.join(tmpdir, "test.py"))
76 testfilename = os.path.realpath(os.path.join(tmpdir, "test.py"))
General Comments 0
You need to be logged in to leave comments. Login now