##// END OF EJS Templates
Merge branch 'takowl-fix-terminal-exit' into trunk
Brian Granger -
r3274:6d136257 merge
parent child Browse files
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,14 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 # Create and start the autosaver.
111 self.autosave_flag = threading.Event()
112 self.autosave_timer = HistorySaveThread(self.autosave_flag, 60)
113 self.autosave_timer.start()
114 # Register the autosave handler to be triggered as a post execute
115 # callback.
116 self.shell.register_post_execute(self.autosave_if_due)
108 117
109 118 def _init_shadow_hist(self):
110 119 try:
@@ -142,12 +151,22 b' class HistoryManager(object):'
142 151 with open(self.hist_file,'wt') as hfile:
143 152 json.dump(hist, hfile,
144 153 sort_keys=True, indent=4)
154
155 def autosave_if_due(self):
156 """Check if the autosave event is set; if so, save history. We do it
157 this way so that the save takes place in the main thread."""
158 if self.autosave_flag.is_set():
159 self.save_history()
160 self.autosave_flag.clear()
145 161
146 162 def reload_history(self):
147 163 """Reload the input history from disk file."""
148 164
149 165 with open(self.hist_file,'rt') as hfile:
150 hist = json.load(hfile)
166 try:
167 hist = json.load(hfile)
168 except ValueError: # Ignore it if JSON is corrupt.
169 return
151 170 self.input_hist_parsed = hist['parsed']
152 171 self.input_hist_raw = hist['raw']
153 172 if self.shell.has_readline:
@@ -254,25 +273,37 b' class HistoryManager(object):'
254 273 self.dir_hist[:] = [os.getcwd()]
255 274
256 275 class HistorySaveThread(threading.Thread):
257 """Thread to save history periodically"""
276 """This thread makes IPython save history periodically.
258 277
259 def __init__(self, IPython_object, time_interval, exit_now):
278 Without this class, IPython would only save the history on a clean exit.
279 This saves the history periodically (the current default is once per
280 minute), so that it is not lost in the event of a crash.
281
282 The implementation sets an event to indicate that history should be saved.
283 The actual save is carried out after executing a user command, to avoid
284 thread issues.
285 """
286 daemon = True
287
288 def __init__(self, autosave_flag, time_interval=60):
260 289 threading.Thread.__init__(self)
261 self.IPython_object = IPython_object
262 290 self.time_interval = time_interval
263 self.exit_now = exit_now
264 self.cond = threading.Condition()
291 self.autosave_flag = autosave_flag
292 self.exit_now = threading.Event()
293 # Ensure the thread is stopped tidily when exiting normally
294 atexit.register(self.stop)
265 295
266 296 def run(self):
267 while 1:
268 self.cond.acquire()
269 self.cond.wait(self.time_interval)
270 self.cond.release()
271 if self.exit_now==True:
297 while True:
298 self.exit_now.wait(self.time_interval)
299 if self.exit_now.is_set():
272 300 break
273 #printing for debug
274 #print("Saving...")
275 self.IPython_object.save_history()
301 self.autosave_flag.set()
302
303 def stop(self):
304 """Safely and quickly stop the autosave timer thread."""
305 self.exit_now.set()
306 self.join()
276 307
277 308 def magic_history(self, parameter_s = ''):
278 309 """Print input history (_i<n> variables), with most recent last.
@@ -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
@@ -294,8 +293,6 b' class InteractiveShell(Configurable, Magic):'
294 293 self.init_payload()
295 294 self.hooks.late_startup_hook()
296 295 atexit.register(self.atexit_operations)
297 self.history_thread = HistorySaveThread(self, 60, False)
298 self.history_thread.start()
299 296
300 297 # While we're trying to have each part of the code directly access what it
301 298 # needs without keeping redundant references to objects, we have too much
@@ -1238,6 +1235,7 b' class InteractiveShell(Configurable, Magic):'
1238 1235 #-------------------------------------------------------------------------
1239 1236
1240 1237 def init_history(self):
1238 """Sets up the command history, and starts regular autosaves."""
1241 1239 self.history_manager = HistoryManager(shell=self)
1242 1240
1243 1241 def save_history(self):
@@ -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
@@ -499,10 +498,6 b' class TerminalInteractiveShell(InteractiveShell):'
499 498 This method calls the ask_exit callback."""
500 499 if self.confirm_exit:
501 500 if self.ask_yes_no('Do you really want to exit ([y]/n)?','y'):
502 self.shell.history_thread.exit_now=True
503 self.shell.history_thread.cond.acquire()
504 self.shell.history_thread.cond.notify()
505 self.shell.history_thread.cond.release()
506 501 self.ask_exit()
507 502 else:
508 503 self.ask_exit()
General Comments 0
You need to be logged in to leave comments. Login now