##// END OF EJS Templates
Rework history autosave, so that the timer thread sets an event, and the save is performed in the main thread after executing a user command.
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 atexit
16 import fnmatch
17 import fnmatch
17 import json
18 import json
18 import os
19 import os
@@ -105,6 +106,13 b' class HistoryManager(object):'
105
106
106 # Fill the history zero entry, user counter starts at 1
107 # Fill the history zero entry, user counter starts at 1
107 self.store_inputs('\n', '\n')
108 self.store_inputs('\n', '\n')
109
110 # Autosave every 60 seconds:
111 self.autosave_flag = threading.Event()
112 self.autosave_timer = HistorySaveThread(self.autosave_flag, 60)
113 self.autosave_timer.start()
114 self.shell.register_post_execute(self.autosave_if_due)
115 # Ensure that any autosave thread we start is stopped tidily.
108
116
109 def _init_shadow_hist(self):
117 def _init_shadow_hist(self):
110 try:
118 try:
@@ -142,6 +150,13 b' class HistoryManager(object):'
142 with open(self.hist_file,'wt') as hfile:
150 with open(self.hist_file,'wt') as hfile:
143 json.dump(hist, hfile,
151 json.dump(hist, hfile,
144 sort_keys=True, indent=4)
152 sort_keys=True, indent=4)
153
154 def autosave_if_due(self):
155 """Check if the autosave event is set; if so, save history. We do it
156 this way so that the save takes place in the main thread."""
157 if self.autosave_flag.is_set():
158 self.save_history()
159 self.autosave_flag.clear()
145
160
146 def reload_history(self):
161 def reload_history(self):
147 """Reload the input history from disk file."""
162 """Reload the input history from disk file."""
@@ -257,29 +272,34 b' class HistoryManager(object):'
257 self.dir_hist[:] = [os.getcwd()]
272 self.dir_hist[:] = [os.getcwd()]
258
273
259 class HistorySaveThread(threading.Thread):
274 class HistorySaveThread(threading.Thread):
260 """This thread saves history periodically (the current default is once per
275 """This thread makes IPython save history periodically (the current default
261 minute), so that it is not lost in the event of a crash. It also allows the
276 is once per minute), so that it is not lost in the event of a crash. It also
262 commands in the current IPython shell to be accessed in a newly started
277 allows the commands in the current IPython shell to be accessed in a newly
263 instance."""
278 started instance.
279
280 This simply sets an event to indicate that history should be saved. The
281 actual save is carried out after executing a user command, to avoid
282 thread issues."""
264 daemon = True
283 daemon = True
265
284
266 def __init__(self, IPython_object, time_interval=60):
285 def __init__(self, autosave_flag, time_interval=60):
267 threading.Thread.__init__(self)
286 threading.Thread.__init__(self)
268 self.IPython_object = IPython_object
269 self.time_interval = time_interval
287 self.time_interval = time_interval
288 self.autosave_flag = autosave_flag
270 self.exit_now = threading.Event()
289 self.exit_now = threading.Event()
290 # Ensure the thread is stopped tidily when exiting normally
291 atexit.register(self.stop)
271
292
272 def run(self):
293 def run(self):
273 while True:
294 while True:
274 self.exit_now.wait(self.time_interval)
295 self.exit_now.wait(self.time_interval)
275 if self.exit_now.is_set():
296 if self.exit_now.is_set():
276 break
297 break
277 #print("Autosaving history...") # DEBUG
298 #print("Setting flag for autosaving history...") # DEBUG
278 self.IPython_object.save_history()
299 self.autosave_flag.set()
279
300
280 def stop(self):
301 def stop(self):
281 """Safely and quickly stop the autosave thread. This will not cause the
302 """Safely and quickly stop the autosave timer thread."""
282 history to be saved before stopping."""
283 self.exit_now.set()
303 self.exit_now.set()
284 self.join()
304 self.join()
285
305
@@ -45,7 +45,6 b' from IPython.core.error import TryNext, UsageError'
45 from IPython.core.extensions import ExtensionManager
45 from IPython.core.extensions import ExtensionManager
46 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
46 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
47 from IPython.core.history import HistoryManager
47 from IPython.core.history import HistoryManager
48 from IPython.core.history import HistorySaveThread
49 from IPython.core.inputsplitter import IPythonInputSplitter
48 from IPython.core.inputsplitter import IPythonInputSplitter
50 from IPython.core.logger import Logger
49 from IPython.core.logger import Logger
51 from IPython.core.magic import Magic
50 from IPython.core.magic import Magic
@@ -1238,8 +1237,6 b' class InteractiveShell(Configurable, Magic):'
1238 def init_history(self):
1237 def init_history(self):
1239 """Sets up the command history, and starts regular autosaves."""
1238 """Sets up the command history, and starts regular autosaves."""
1240 self.history_manager = HistoryManager(shell=self)
1239 self.history_manager = HistoryManager(shell=self)
1241 self.history_thread = HistorySaveThread(self, time_interval=60)
1242 self.history_thread.start()
1243
1240
1244 def save_history(self):
1241 def save_history(self):
1245 """Save input history to a file (via readline library)."""
1242 """Save input history to a file (via readline library)."""
@@ -2527,7 +2524,6 b' class InteractiveShell(Configurable, Magic):'
2527 except OSError:
2524 except OSError:
2528 pass
2525 pass
2529
2526
2530 self.history_thread.stop()
2531 self.save_history()
2527 self.save_history()
2532
2528
2533 # Clear all user namespaces to release all references cleanly.
2529 # Clear all user namespaces to release all references cleanly.
@@ -24,7 +24,6 b' import sys'
24 from IPython.core.error import TryNext
24 from IPython.core.error import TryNext
25 from IPython.core.usage import interactive_usage, default_banner
25 from IPython.core.usage import interactive_usage, default_banner
26 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
26 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
27 from IPython.core.history import HistorySaveThread
28 from IPython.lib.inputhook import enable_gui
27 from IPython.lib.inputhook import enable_gui
29 from IPython.lib.pylabtools import pylab_activate
28 from IPython.lib.pylabtools import pylab_activate
30 from IPython.utils.terminal import toggle_set_term_title, set_term_title
29 from IPython.utils.terminal import toggle_set_term_title, set_term_title
General Comments 0
You need to be logged in to leave comments. Login now