##// END OF EJS Templates
Put history saving into a separate thread.
Thomas Kluyver -
Show More
@@ -13,11 +13,13 b''
13 from __future__ import print_function
13 from __future__ import print_function
14
14
15 # Stdlib imports
15 # Stdlib imports
16 import atexit
16 import datetime
17 import datetime
17 import json
18 import json
18 import os
19 import os
19 import re
20 import re
20 import sqlite3
21 import sqlite3
22 import threading
21
23
22 from collections import defaultdict
24 from collections import defaultdict
23
25
@@ -76,6 +78,10 b' class HistoryManager(Configurable):'
76 db_input_cache = List()
78 db_input_cache = List()
77 db_output_cache = List()
79 db_output_cache = List()
78
80
81 # History saving in separate thread
82 save_thread = Instance('IPython.core.history.HistorySavingThread')
83 save_flag = Instance(threading._Event)
84
79 # Private interface
85 # Private interface
80 # Variables used to store the three last inputs from the user. On each new
86 # Variables used to store the three last inputs from the user. On each new
81 # history update, we populate the user's namespace with these, shifted as
87 # history update, we populate the user's namespace with these, shifted as
@@ -119,6 +125,10 b' class HistoryManager(Configurable):'
119 else:
125 else:
120 # The hist_file is probably :memory: or something else.
126 # The hist_file is probably :memory: or something else.
121 raise
127 raise
128
129 self.save_flag = threading.Event()
130 self.save_thread = HistorySavingThread(self)
131 self.save_thread.start()
122
132
123 self.new_session()
133 self.new_session()
124
134
@@ -376,7 +386,7 b' class HistoryManager(Configurable):'
376 self.db_input_cache.append((line_num, source, source_raw))
386 self.db_input_cache.append((line_num, source, source_raw))
377 # Trigger to flush cache and write to DB.
387 # Trigger to flush cache and write to DB.
378 if len(self.db_input_cache) >= self.db_cache_size:
388 if len(self.db_input_cache) >= self.db_cache_size:
379 self.writeout_cache()
389 self.save_flag.set()
380
390
381 # update the auto _i variables
391 # update the auto _i variables
382 self._iii = self._ii
392 self._iii = self._ii
@@ -408,44 +418,69 b' class HistoryManager(Configurable):'
408
418
409 self.db_output_cache.append((line_num, output))
419 self.db_output_cache.append((line_num, output))
410 if self.db_cache_size <= 1:
420 if self.db_cache_size <= 1:
411 self.writeout_cache()
421 self.save_flag.set()
412
422
413 def _writeout_input_cache(self):
423 def _writeout_input_cache(self, conn):
414 with self.db:
424 with conn:
415 for line in self.db_input_cache:
425 for line in self.db_input_cache:
416 self.db.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
426 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
417 (self.session_number,)+line)
427 (self.session_number,)+line)
418
428
419 def _writeout_output_cache(self):
429 def _writeout_output_cache(self, conn):
420 with self.db:
430 with conn:
421 for line in self.db_output_cache:
431 for line in self.db_output_cache:
422 self.db.execute("INSERT INTO output_history VALUES (?, ?, ?)",
432 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
423 (self.session_number,)+line)
433 (self.session_number,)+line)
424
434
425 def writeout_cache(self):
435 def writeout_cache(self, conn=None):
426 """Write any entries in the cache to the database."""
436 """Write any entries in the cache to the database."""
437 if conn is None:
438 conn = self.db
427 try:
439 try:
428 self._writeout_input_cache()
440 self._writeout_input_cache(conn)
429 except sqlite3.IntegrityError:
441 except sqlite3.IntegrityError:
430 self.new_session()
442 self.new_session()
431 print("ERROR! Session/line number was not unique in",
443 print("ERROR! Session/line number was not unique in",
432 "database. History logging moved to new session",
444 "database. History logging moved to new session",
433 self.session_number)
445 self.session_number)
434 try: # Try writing to the new session. If this fails, don't recurse
446 try: # Try writing to the new session. If this fails, don't recurse
435 self._writeout_input_cache()
447 self._writeout_input_cache(conn)
436 except sqlite3.IntegrityError:
448 except sqlite3.IntegrityError:
437 pass
449 pass
438 finally:
450 finally:
439 self.db_input_cache = []
451 self.db_input_cache = []
440
452
441 try:
453 try:
442 self._writeout_output_cache()
454 self._writeout_output_cache(conn)
443 except sqlite3.IntegrityError:
455 except sqlite3.IntegrityError:
444 print("!! Session/line number for output was not unique",
456 print("!! Session/line number for output was not unique",
445 "in database. Output will not be stored.")
457 "in database. Output will not be stored.")
446 finally:
458 finally:
447 self.db_output_cache = []
459 self.db_output_cache = []
448
460
461
462 class HistorySavingThread(threading.Thread):
463 daemon = True
464 stop_now = False
465 def __init__(self, history_manager):
466 super(HistorySavingThread, self).__init__()
467 self.history_manager = history_manager
468 atexit.register(self.stop)
469
470 def run(self):
471 # We need a separate db connection per thread:
472 self.db = sqlite3.connect(self.history_manager.hist_file)
473 while True:
474 self.history_manager.save_flag.wait()
475 self.history_manager.save_flag.clear()
476 self.history_manager.writeout_cache(self.db)
477 if self.stop_now:
478 return
479
480 def stop(self):
481 self.stop_now = True
482 self.history_manager.save_flag.set()
483
449
484
450 # To match, e.g. ~5/8-~2/3
485 # To match, e.g. ~5/8-~2/3
451 range_re = re.compile(r"""
486 range_re = re.compile(r"""
General Comments 0
You need to be logged in to leave comments. Login now