Show More
@@ -27,7 +27,9 b' import threading' | |||
|
27 | 27 | from IPython.config.configurable import Configurable |
|
28 | 28 | from IPython.external.decorator import decorator |
|
29 | 29 | from IPython.utils.path import locate_profile |
|
30 |
from IPython.utils.traitlets import |
|
|
30 | from IPython.utils.traitlets import ( | |
|
31 | Any, Bool, Dict, Instance, Integer, List, Unicode, TraitError, | |
|
32 | ) | |
|
31 | 33 | from IPython.utils.warn import warn |
|
32 | 34 | |
|
33 | 35 | #----------------------------------------------------------------------------- |
@@ -52,12 +54,12 b' class DummyDB(object):' | |||
|
52 | 54 | |
|
53 | 55 | |
|
54 | 56 | @decorator |
|
55 | def needs_sqlite(f,*a,**kw): | |
|
57 | def needs_sqlite(f, self, *a, **kw): | |
|
56 | 58 | """return an empty list in the absence of sqlite""" |
|
57 |
if sqlite |
|
|
59 | if sqlite is None or self.disabled: | |
|
58 | 60 | return [] |
|
59 | 61 | else: |
|
60 | return f(*a,**kw) | |
|
62 | return f(self, *a, **kw) | |
|
61 | 63 | |
|
62 | 64 | |
|
63 | 65 | class HistoryAccessor(Configurable): |
@@ -82,11 +84,31 b' class HistoryAccessor(Configurable):' | |||
|
82 | 84 | |
|
83 | 85 | """) |
|
84 | 86 | |
|
87 | disabled = Bool(False, config=True, | |
|
88 | help="""disable the SQLite history | |
|
89 | ||
|
90 | If True there will be no stored history, no SQLite connection, | |
|
91 | and no background saving thread. This may be necessary in some | |
|
92 | threaded environments where IPython is embedded. | |
|
93 | """ | |
|
94 | ) | |
|
95 | ||
|
96 | connection_options = Dict(config=True, | |
|
97 | help="""Options for configuring the SQLite connection | |
|
98 | ||
|
99 | These options are passed as keyword args to sqlite3.connect | |
|
100 | when establishing database conenctions. | |
|
101 | """ | |
|
102 | ) | |
|
103 | ||
|
85 | 104 | # The SQLite database |
|
86 | if sqlite3: | |
|
87 | db = Instance(sqlite3.Connection) | |
|
88 | else: | |
|
89 | db = Instance(DummyDB) | |
|
105 | db = Any() | |
|
106 | def _db_changed(self, name, old, new): | |
|
107 | """validate the db, since it can be an Instance of two different types""" | |
|
108 | if not isinstance(new, (sqlite3.Connection, DummyDB)): | |
|
109 | msg = "%s.db must be sqlite3 Connection or DummyDB, not %r" % \ | |
|
110 | (self.__class__.__name__, new) | |
|
111 | raise TraitError(msg) | |
|
90 | 112 | |
|
91 | 113 | def __init__(self, profile='default', hist_file=u'', config=None, **traits): |
|
92 | 114 | """Create a new history accessor. |
@@ -113,14 +135,18 b' class HistoryAccessor(Configurable):' | |||
|
113 | 135 | # No one has set the hist_file, yet. |
|
114 | 136 | self.hist_file = self._get_hist_file_name(profile) |
|
115 | 137 | |
|
116 | if sqlite3 is None: | |
|
138 | if sqlite3 is None and not self.disabled: | |
|
117 | 139 | warn("IPython History requires SQLite, your history will not be saved\n") |
|
118 |
self.db = |
|
|
119 |
|
|
|
140 | self.disabled = True | |
|
141 | ||
|
142 | if sqlite3 is not None: | |
|
143 | DatabaseError = sqlite3.DatabaseError | |
|
144 | else: | |
|
145 | DatabaseError = Exception | |
|
120 | 146 | |
|
121 | 147 | try: |
|
122 | 148 | self.init_db() |
|
123 |
except |
|
|
149 | except DatabaseError: | |
|
124 | 150 | if os.path.isfile(self.hist_file): |
|
125 | 151 | # Try to move the file out of the way |
|
126 | 152 | base,ext = os.path.splitext(self.hist_file) |
@@ -148,9 +174,14 b' class HistoryAccessor(Configurable):' | |||
|
148 | 174 | |
|
149 | 175 | def init_db(self): |
|
150 | 176 | """Connect to the database, and create tables if necessary.""" |
|
177 | if self.disabled: | |
|
178 | self.db = DummyDB() | |
|
179 | return | |
|
180 | ||
|
151 | 181 | # use detect_types so that timestamps return datetime objects |
|
152 | self.db = sqlite3.connect(self.hist_file, | |
|
153 | detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES) | |
|
182 | kwargs = dict(detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES) | |
|
183 | kwargs.update(self.connection_options) | |
|
184 | self.db = sqlite3.connect(self.hist_file, **kwargs) | |
|
154 | 185 | self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer |
|
155 | 186 | primary key autoincrement, start timestamp, |
|
156 | 187 | end timestamp, num_cmds integer, remark text)""") |
@@ -402,7 +433,7 b' class HistoryManager(HistoryAccessor):' | |||
|
402 | 433 | self.save_flag = threading.Event() |
|
403 | 434 | self.db_input_cache_lock = threading.Lock() |
|
404 | 435 | self.db_output_cache_lock = threading.Lock() |
|
405 | if self.hist_file != ':memory:': | |
|
436 | if not self.disabled and self.hist_file != ':memory:': | |
|
406 | 437 | self.save_thread = HistorySavingThread(self) |
|
407 | 438 | self.save_thread.start() |
|
408 | 439 | |
@@ -638,16 +669,20 b' class HistorySavingThread(threading.Thread):' | |||
|
638 | 669 | the cache size reaches a defined threshold.""" |
|
639 | 670 | daemon = True |
|
640 | 671 | stop_now = False |
|
672 | disabled = False | |
|
641 | 673 | def __init__(self, history_manager): |
|
642 | 674 | super(HistorySavingThread, self).__init__() |
|
643 | 675 | self.history_manager = history_manager |
|
676 | self.disabled = history_manager.disabled | |
|
644 | 677 | atexit.register(self.stop) |
|
645 | 678 | |
|
646 | 679 | @needs_sqlite |
|
647 | 680 | def run(self): |
|
648 | 681 | # We need a separate db connection per thread: |
|
649 | 682 | try: |
|
650 |
self.db = sqlite3.connect(self.history_manager.hist_file |
|
|
683 | self.db = sqlite3.connect(self.history_manager.hist_file, | |
|
684 | **self.history_manager.connection_options | |
|
685 | ) | |
|
651 | 686 | while True: |
|
652 | 687 | self.history_manager.save_flag.wait() |
|
653 | 688 | if self.stop_now: |
General Comments 0
You need to be logged in to leave comments.
Login now