##// 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 13 from __future__ import print_function
14 14
15 15 # Stdlib imports
16 import atexit
16 17 import fnmatch
17 18 import json
18 19 import os
@@ -105,6 +106,13 b' class HistoryManager(object):'
105 106
106 107 # Fill the history zero entry, user counter starts at 1
107 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 117 def _init_shadow_hist(self):
110 118 try:
@@ -142,6 +150,13 b' class HistoryManager(object):'
142 150 with open(self.hist_file,'wt') as hfile:
143 151 json.dump(hist, hfile,
144 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 161 def reload_history(self):
147 162 """Reload the input history from disk file."""
@@ -257,29 +272,34 b' class HistoryManager(object):'
257 272 self.dir_hist[:] = [os.getcwd()]
258 273
259 274 class HistorySaveThread(threading.Thread):
260 """This thread saves history periodically (the current default is once per
261 minute), so that it is not lost in the event of a crash. It also allows the
262 commands in the current IPython shell to be accessed in a newly started
263 instance."""
275 """This thread makes IPython save history periodically (the current default
276 is once per minute), so that it is not lost in the event of a crash. It also
277 allows the commands in the current IPython shell to be accessed in a newly
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 283 daemon = True
265 284
266 def __init__(self, IPython_object, time_interval=60):
285 def __init__(self, autosave_flag, time_interval=60):
267 286 threading.Thread.__init__(self)
268 self.IPython_object = IPython_object
269 287 self.time_interval = time_interval
288 self.autosave_flag = autosave_flag
270 289 self.exit_now = threading.Event()
290 # Ensure the thread is stopped tidily when exiting normally
291 atexit.register(self.stop)
271 292
272 293 def run(self):
273 294 while True:
274 295 self.exit_now.wait(self.time_interval)
275 296 if self.exit_now.is_set():
276 297 break
277 #print("Autosaving history...") # DEBUG
278 self.IPython_object.save_history()
298 #print("Setting flag for autosaving history...") # DEBUG
299 self.autosave_flag.set()
279 300
280 301 def stop(self):
281 """Safely and quickly stop the autosave thread. This will not cause the
282 history to be saved before stopping."""
302 """Safely and quickly stop the autosave timer thread."""
283 303 self.exit_now.set()
284 304 self.join()
285 305
@@ -45,7 +45,6 b' from IPython.core.error import TryNext, UsageError'
45 45 from IPython.core.extensions import ExtensionManager
46 46 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
47 47 from IPython.core.history import HistoryManager
48 from IPython.core.history import HistorySaveThread
49 48 from IPython.core.inputsplitter import IPythonInputSplitter
50 49 from IPython.core.logger import Logger
51 50 from IPython.core.magic import Magic
@@ -1238,8 +1237,6 b' class InteractiveShell(Configurable, Magic):'
1238 1237 def init_history(self):
1239 1238 """Sets up the command history, and starts regular autosaves."""
1240 1239 self.history_manager = HistoryManager(shell=self)
1241 self.history_thread = HistorySaveThread(self, time_interval=60)
1242 self.history_thread.start()
1243 1240
1244 1241 def save_history(self):
1245 1242 """Save input history to a file (via readline library)."""
@@ -2527,7 +2524,6 b' class InteractiveShell(Configurable, Magic):'
2527 2524 except OSError:
2528 2525 pass
2529 2526
2530 self.history_thread.stop()
2531 2527 self.save_history()
2532 2528
2533 2529 # Clear all user namespaces to release all references cleanly.
@@ -24,7 +24,6 b' import sys'
24 24 from IPython.core.error import TryNext
25 25 from IPython.core.usage import interactive_usage, default_banner
26 26 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
27 from IPython.core.history import HistorySaveThread
28 27 from IPython.lib.inputhook import enable_gui
29 28 from IPython.lib.pylabtools import pylab_activate
30 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