##// END OF EJS Templates
Store history sessions in table with start and end time, number of commands, and name/remark.
Thomas Kluyver -
Show More
@@ -13,6 +13,7 b''
13 from __future__ import print_function
13 from __future__ import print_function
14
14
15 # Stdlib imports
15 # Stdlib imports
16 import datetime
16 import os
17 import os
17 import re
18 import re
18 import sqlite3
19 import sqlite3
@@ -90,6 +91,7 b' class HistoryManager(Configurable):'
90 histfname = 'history'
91 histfname = 'history'
91 self.hist_file = os.path.join(shell.ipython_dir, histfname + '.sqlite')
92 self.hist_file = os.path.join(shell.ipython_dir, histfname + '.sqlite')
92 self.init_db()
93 self.init_db()
94 self.new_session()
93
95
94 self._i00, self._i, self._ii, self._iii = '','','',''
96 self._i00, self._i, self._ii, self._iii = '','','',''
95
97
@@ -97,8 +99,11 b' class HistoryManager(Configurable):'
97 '%quit', '%Exit', '%exit'])
99 '%quit', '%Exit', '%exit'])
98
100
99 def init_db(self):
101 def init_db(self):
100 """Connect to the database and get new session number."""
102 """Connect to the database, and create tables if necessary."""
101 self.db = sqlite3.connect(self.hist_file)
103 self.db = sqlite3.connect(self.hist_file)
104 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
105 primary key autoincrement, start timestamp,
106 end timestamp, num_cmds integer, remark text)""")
102 self.db.execute("""CREATE TABLE IF NOT EXISTS history
107 self.db.execute("""CREATE TABLE IF NOT EXISTS history
103 (session integer, line integer, source text, source_raw text,
108 (session integer, line integer, source text, source_raw text,
104 PRIMARY KEY (session, line))""")
109 PRIMARY KEY (session, line))""")
@@ -107,23 +112,47 b' class HistoryManager(Configurable):'
107 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
112 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
108 (session integer, line integer, output text,
113 (session integer, line integer, output text,
109 PRIMARY KEY (session, line))""")
114 PRIMARY KEY (session, line))""")
110 cur = self.db.execute("""SELECT name FROM sqlite_master WHERE
111 type='table' AND name='singletons'""")
112 if not cur.fetchone():
113 self.db.execute("""CREATE TABLE singletons
114 (name text PRIMARY KEY, value)""")
115 self.db.execute("""INSERT INTO singletons VALUES
116 ('session_number', 1)""")
117 self.db.commit()
118 cur = self.db.execute("""SELECT value FROM singletons WHERE
119 name='session_number'""")
120 self.session_number = cur.fetchone()[0]
121
122 #Increment by one for next session.
123 self.db.execute("""UPDATE singletons SET value=? WHERE
124 name='session_number'""", (self.session_number+1,))
125 self.db.commit()
115 self.db.commit()
126
116
117 def new_session(self):
118 """Get a new session number."""
119 with self.db:
120 cur = self.db.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
121 NULL, "") """, (datetime.datetime.now(),))
122 self.session_number = cur.lastrowid
123
124 def end_session(self):
125 """Close the database session, filling in the end time and line count."""
126 self.writeout_cache()
127 with self.db:
128 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
129 session==?""", (datetime.datetime.now(),
130 len(self.input_hist_parsed)-1, self.session_number))
131 self.session_number = 0
132
133 def name_session(self, name):
134 """Give the current session a name in the history database."""
135 with self.db:
136 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
137 (name, self.session_number))
138
139 def reset(self, new_session=True):
140 """Clear the session history, releasing all object references, and
141 optionally open a new session."""
142 if self.session_number:
143 self.end_session()
144 self.input_hist_parsed[:] = [""]
145 self.input_hist_raw[:] = [""]
146 self.output_hist.clear()
147 # The directory history can't be completely empty
148 self.dir_hist[:] = [os.getcwd()]
149
150 if new_session:
151 self.new_session()
152
153 ## -------------------------------
154 ## Methods for retrieving history:
155 ## -------------------------------
127 def _get_hist_sql(self, sql, params, raw=True, output=False):
156 def _get_hist_sql(self, sql, params, raw=True, output=False):
128 """Prepares and runs an SQL query for the history database.
157 """Prepares and runs an SQL query for the history database.
129
158
@@ -245,7 +274,10 b' class HistoryManager(Configurable):'
245 for sess, s, e in extract_hist_ranges(rangestr):
274 for sess, s, e in extract_hist_ranges(rangestr):
246 for line in self.get_history(sess, s, e, raw=raw, output=output):
275 for line in self.get_history(sess, s, e, raw=raw, output=output):
247 yield line
276 yield line
248
277
278 ## ----------------------------
279 ## Methods for storing history:
280 ## ----------------------------
249 def store_inputs(self, line_num, source, source_raw=None):
281 def store_inputs(self, line_num, source, source_raw=None):
250 """Store source and raw input in history and create input cache
282 """Store source and raw input in history and create input cache
251 variables _i*.
283 variables _i*.
@@ -312,27 +344,6 b' class HistoryManager(Configurable):'
312 self.db_input_cache = []
344 self.db_input_cache = []
313 self.db_output_cache = []
345 self.db_output_cache = []
314
346
315 def sync_inputs(self):
316 """Ensure raw and translated histories have same length."""
317 lr = len(self.input_hist_raw)
318 lp = len(self.input_hist_parsed)
319 if lp < lr:
320 self.input_hist_raw[:lr-lp] = []
321 elif lr < lp:
322 self.input_hist_parsed[:lp-lr] = []
323
324 def reset(self, new_session=True):
325 """Clear the current session's history, and (optionally) start a new
326 session."""
327 self.input_hist_parsed[:] = [""]
328 self.input_hist_raw[:] = [""]
329 self.output_hist.clear()
330 # The directory history can't be completely empty
331 self.dir_hist[:] = [os.getcwd()]
332
333 if new_session:
334 self.writeout_cache()
335 self.init_db() # Get new session number
336
347
337 # To match, e.g. ~5/8-~2/3
348 # To match, e.g. ~5/8-~2/3
338 range_re = re.compile(r"""
349 range_re = re.compile(r"""
@@ -2526,10 +2526,10 b' class InteractiveShell(Configurable, Magic):'
2526 os.unlink(tfile)
2526 os.unlink(tfile)
2527 except OSError:
2527 except OSError:
2528 pass
2528 pass
2529
2529
2530 # Write anything in the history cache to the database.
2530 # Close the history session (this stores the end time and line count)
2531 self.history_manager.writeout_cache()
2531 self.history_manager.end_session()
2532
2532
2533 # Clear all user namespaces to release all references cleanly.
2533 # Clear all user namespaces to release all references cleanly.
2534 self.reset(new_session=False)
2534 self.reset(new_session=False)
2535
2535
@@ -30,6 +30,7 b' def test_history():'
30 ip.history_manager = HistoryManager(shell=ip)
30 ip.history_manager = HistoryManager(shell=ip)
31 ip.history_manager.hist_file = histfile
31 ip.history_manager.hist_file = histfile
32 ip.history_manager.init_db() # Has to be called after changing file
32 ip.history_manager.init_db() # Has to be called after changing file
33 ip.history_manager.reset()
33 print 'test',histfile
34 print 'test',histfile
34 hist = ['a=1', 'def f():\n test = 1\n return test', 'b=2']
35 hist = ['a=1', 'def f():\n test = 1\n return test', 'b=2']
35 for i, h in enumerate(hist, start=1):
36 for i, h in enumerate(hist, start=1):
@@ -185,10 +185,6 b' class TerminalInteractiveShell(InteractiveShell):'
185
185
186 with nested(self.builtin_trap, self.display_trap):
186 with nested(self.builtin_trap, self.display_trap):
187
187
188 # if you run stuff with -c <cmd>, raw hist is not updated
189 # ensure that it's in sync
190 self.history_manager.sync_inputs()
191
192 while 1:
188 while 1:
193 try:
189 try:
194 self.interact(display_banner=display_banner)
190 self.interact(display_banner=display_banner)
General Comments 0
You need to be logged in to leave comments. Login now