Show More
@@ -13,11 +13,13 b'' | |||
|
13 | 13 | from __future__ import print_function |
|
14 | 14 | |
|
15 | 15 | # Stdlib imports |
|
16 | import atexit | |
|
16 | 17 | import datetime |
|
17 | 18 | import json |
|
18 | 19 | import os |
|
19 | 20 | import re |
|
20 | 21 | import sqlite3 |
|
22 | import threading | |
|
21 | 23 | |
|
22 | 24 | from collections import defaultdict |
|
23 | 25 | |
@@ -76,6 +78,10 b' class HistoryManager(Configurable):' | |||
|
76 | 78 | db_input_cache = List() |
|
77 | 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 | 85 | # Private interface |
|
80 | 86 | # Variables used to store the three last inputs from the user. On each new |
|
81 | 87 | # history update, we populate the user's namespace with these, shifted as |
@@ -119,6 +125,10 b' class HistoryManager(Configurable):' | |||
|
119 | 125 | else: |
|
120 | 126 | # The hist_file is probably :memory: or something else. |
|
121 | 127 | raise |
|
128 | ||
|
129 | self.save_flag = threading.Event() | |
|
130 | self.save_thread = HistorySavingThread(self) | |
|
131 | self.save_thread.start() | |
|
122 | 132 | |
|
123 | 133 | self.new_session() |
|
124 | 134 | |
@@ -376,7 +386,7 b' class HistoryManager(Configurable):' | |||
|
376 | 386 | self.db_input_cache.append((line_num, source, source_raw)) |
|
377 | 387 | # Trigger to flush cache and write to DB. |
|
378 | 388 | if len(self.db_input_cache) >= self.db_cache_size: |
|
379 |
self. |
|
|
389 | self.save_flag.set() | |
|
380 | 390 | |
|
381 | 391 | # update the auto _i variables |
|
382 | 392 | self._iii = self._ii |
@@ -408,44 +418,69 b' class HistoryManager(Configurable):' | |||
|
408 | 418 | |
|
409 | 419 | self.db_output_cache.append((line_num, output)) |
|
410 | 420 | if self.db_cache_size <= 1: |
|
411 |
self. |
|
|
412 |
|
|
|
413 | def _writeout_input_cache(self): | |
|
414 |
with |
|
|
421 | self.save_flag.set() | |
|
422 | ||
|
423 | def _writeout_input_cache(self, conn): | |
|
424 | with conn: | |
|
415 | 425 | for line in self.db_input_cache: |
|
416 |
|
|
|
426 | conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)", | |
|
417 | 427 | (self.session_number,)+line) |
|
418 | 428 | |
|
419 | def _writeout_output_cache(self): | |
|
420 |
with |
|
|
429 | def _writeout_output_cache(self, conn): | |
|
430 | with conn: | |
|
421 | 431 | for line in self.db_output_cache: |
|
422 |
|
|
|
432 | conn.execute("INSERT INTO output_history VALUES (?, ?, ?)", | |
|
423 | 433 | (self.session_number,)+line) |
|
424 | 434 | |
|
425 | def writeout_cache(self): | |
|
435 | def writeout_cache(self, conn=None): | |
|
426 | 436 | """Write any entries in the cache to the database.""" |
|
437 | if conn is None: | |
|
438 | conn = self.db | |
|
427 | 439 | try: |
|
428 | self._writeout_input_cache() | |
|
440 | self._writeout_input_cache(conn) | |
|
429 | 441 | except sqlite3.IntegrityError: |
|
430 | 442 | self.new_session() |
|
431 | 443 | print("ERROR! Session/line number was not unique in", |
|
432 | 444 | "database. History logging moved to new session", |
|
433 | 445 | self.session_number) |
|
434 | 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 | 448 | except sqlite3.IntegrityError: |
|
437 | 449 | pass |
|
438 | 450 | finally: |
|
439 | 451 | self.db_input_cache = [] |
|
440 | 452 | |
|
441 | 453 | try: |
|
442 | self._writeout_output_cache() | |
|
454 | self._writeout_output_cache(conn) | |
|
443 | 455 | except sqlite3.IntegrityError: |
|
444 | 456 | print("!! Session/line number for output was not unique", |
|
445 | 457 | "in database. Output will not be stored.") |
|
446 | 458 | finally: |
|
447 | 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 | 485 | # To match, e.g. ~5/8-~2/3 |
|
451 | 486 | range_re = re.compile(r""" |
General Comments 0
You need to be logged in to leave comments.
Login now