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,14 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 | # 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 | def _init_shadow_hist(self): |
|
118 | def _init_shadow_hist(self): | |
110 | try: |
|
119 | try: | |
@@ -142,12 +151,22 b' class HistoryManager(object):' | |||||
142 | with open(self.hist_file,'wt') as hfile: |
|
151 | with open(self.hist_file,'wt') as hfile: | |
143 | json.dump(hist, hfile, |
|
152 | json.dump(hist, hfile, | |
144 | sort_keys=True, indent=4) |
|
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 | def reload_history(self): |
|
162 | def reload_history(self): | |
147 | """Reload the input history from disk file.""" |
|
163 | """Reload the input history from disk file.""" | |
148 |
|
164 | |||
149 | with open(self.hist_file,'rt') as hfile: |
|
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 | self.input_hist_parsed = hist['parsed'] |
|
170 | self.input_hist_parsed = hist['parsed'] | |
152 | self.input_hist_raw = hist['raw'] |
|
171 | self.input_hist_raw = hist['raw'] | |
153 | if self.shell.has_readline: |
|
172 | if self.shell.has_readline: | |
@@ -254,25 +273,37 b' class HistoryManager(object):' | |||||
254 | self.dir_hist[:] = [os.getcwd()] |
|
273 | self.dir_hist[:] = [os.getcwd()] | |
255 |
|
274 | |||
256 | class HistorySaveThread(threading.Thread): |
|
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 | threading.Thread.__init__(self) |
|
289 | threading.Thread.__init__(self) | |
261 | self.IPython_object = IPython_object |
|
|||
262 | self.time_interval = time_interval |
|
290 | self.time_interval = time_interval | |
263 | self.exit_now = exit_now |
|
291 | self.autosave_flag = autosave_flag | |
264 |
self. |
|
292 | self.exit_now = threading.Event() | |
|
293 | # Ensure the thread is stopped tidily when exiting normally | |||
|
294 | atexit.register(self.stop) | |||
265 |
|
295 | |||
266 | def run(self): |
|
296 | def run(self): | |
267 |
while |
|
297 | while True: | |
268 | self.cond.acquire() |
|
298 | self.exit_now.wait(self.time_interval) | |
269 | self.cond.wait(self.time_interval) |
|
299 | if self.exit_now.is_set(): | |
270 | self.cond.release() |
|
|||
271 | if self.exit_now==True: |
|
|||
272 | break |
|
300 | break | |
273 | #printing for debug |
|
301 | self.autosave_flag.set() | |
274 | #print("Saving...") |
|
302 | ||
275 | self.IPython_object.save_history() |
|
303 | def stop(self): | |
|
304 | """Safely and quickly stop the autosave timer thread.""" | |||
|
305 | self.exit_now.set() | |||
|
306 | self.join() | |||
276 |
|
307 | |||
277 | def magic_history(self, parameter_s = ''): |
|
308 | def magic_history(self, parameter_s = ''): | |
278 | """Print input history (_i<n> variables), with most recent last. |
|
309 | """Print input history (_i<n> variables), with most recent last. |
@@ -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 | |
@@ -294,8 +293,6 b' class InteractiveShell(Configurable, Magic):' | |||||
294 | self.init_payload() |
|
293 | self.init_payload() | |
295 | self.hooks.late_startup_hook() |
|
294 | self.hooks.late_startup_hook() | |
296 | atexit.register(self.atexit_operations) |
|
295 | atexit.register(self.atexit_operations) | |
297 | self.history_thread = HistorySaveThread(self, 60, False) |
|
|||
298 | self.history_thread.start() |
|
|||
299 |
|
296 | |||
300 | # While we're trying to have each part of the code directly access what it |
|
297 | # While we're trying to have each part of the code directly access what it | |
301 | # needs without keeping redundant references to objects, we have too much |
|
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 | def init_history(self): |
|
1237 | def init_history(self): | |
|
1238 | """Sets up the command history, and starts regular autosaves.""" | |||
1241 | self.history_manager = HistoryManager(shell=self) |
|
1239 | self.history_manager = HistoryManager(shell=self) | |
1242 |
|
1240 | |||
1243 | def save_history(self): |
|
1241 | def save_history(self): |
@@ -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 | |
@@ -499,10 +498,6 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
499 | This method calls the ask_exit callback.""" |
|
498 | This method calls the ask_exit callback.""" | |
500 | if self.confirm_exit: |
|
499 | if self.confirm_exit: | |
501 | if self.ask_yes_no('Do you really want to exit ([y]/n)?','y'): |
|
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 | self.ask_exit() |
|
501 | self.ask_exit() | |
507 | else: |
|
502 | else: | |
508 | self.ask_exit() |
|
503 | self.ask_exit() |
General Comments 0
You need to be logged in to leave comments.
Login now