Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Timed history save #234

Closed
wants to merge 4 commits into from

2 participants

@mchandra

Implemented autosave by spawning a thread which saves periodically. Thread spawned when core/InteractiveShell is initialized and is closed when ctrl+D is pressed. If the user presses 'n' when asked to exit the thread is spawned again.

@minrk
Owner

This sounds great, but I have a few notes:

  • It seems like the HistorySaveThread object should be defined in core.history instead of core.interactiveshell
  • Saving every second seems excessive, especially if you develop a large history and/or leave an IPython session sitting in the background (many do this). Perhaps make it every 5 or 10, or even every minute. If you are going to save at high frequency, it's important to detect changes in order to avoid writing the exact same content to a file over and over again, and we don't do this yet.
  • If you do increase the save interval (and possibly even if not), we don't want the save dialog to wait for the interval to complete to come up, and that's what currently happens. You could change:

    self.shell.history_thread.exit_now=True
    self.shell.history_thread.join()
    if self.confirm_exit:
        if self.ask_yes_no('Do you really want to exit ([y]/n)?','y'):
            self.ask_exit()
        else:
            self.shell.history_thread = HistorySaveThread(self.shell, 1,
                    False)
            self.shell.history_thread.start()
    else:
        self.ask_exit()
    

to

    if self.confirm_exit:
        if self.ask_yes_no('Do you really want to exit ([y]/n)?','y'):
            self.shell.history_thread.exit_now=True
            self.shell.history_thread.join()
            self.ask_exit()
    else:
        self.shell.history_thread.exit_now=True
        self.shell.history_thread.join()
        self.ask_exit()

You can also use a threading.Condition, and its wait()/notify() methods to avoid having to wait on join() at all.

@mchandra

Hi, thanks for the suggestions. I have implemented it as you said but I have a few doubts. I'm looking at how to use the threading.Condition and wait()/noitfy() methods but meanwhile I realized that I do not need to use join() and the results are still the same. The problem with the current implementation is that the user has to wait for the sleep time to complete when ctrl+D + yes is pressed before the program can exit. Any suggestions on how to make the program exit instantly? Perhaps this is possible with the methods you suggested and I'm working on how to use them.

@minrk
Owner

I think you can use wait(timeout) instead of sleep(timeout), then the notify() trigger will effectively interrupt the sleep. Along the lines of:
...
while True:
self.cond.acquire()
self.cond.wait(self.time_interval)
self.cond.release()
if self.exit_now == True:
break
...
and trigger with something like:

history_thread.exit_now = True
history_thread.cond.acquire()
history_thread.cond.notify()
history_thread.cond.release()
@mchandra

Hi, I made the changes and now it works nicely. I also increased the timeout to one minute.

@minrk
Owner

Great, this looks excellent. One last question: Why do you start a new HistorySaveThread if confirm_exit is False? Shouldn't it just go straight to ask_exit()?

@mchandra

Oops. You're right, those lines shouldn't be there. There were part of earlier code where I was using slightly different logic. Fixed it now.

@mchandra

Hi, so what happens now?

@mchandra

Periodic Autosave added in background thread

closed by 8bac8cb

Signed-off-by: MinRK <benjaminrk@gmail.com>

@minrk
Owner

Hi, sorry for the delay.

It's merged into master now.

@markvoorhies markvoorhies referenced this pull request from a commit in markvoorhies/ipython
Mani chandra Periodic Autosave added in background thread
closes gh-234

Signed-off-by: MinRK <benjaminrk@gmail.com>
8bac8cb
@ellisonbg ellisonbg referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@damianavila damianavila referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@mattvonrocketstein mattvonrocketstein referenced this pull request from a commit in mattvonrocketstein/ipython
Mani chandra Periodic Autosave added in background thread
closes gh-234

Signed-off-by: MinRK <benjaminrk@gmail.com>
6166b15
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 18, 2010
  1. Implemented autosave.

    Mani chandra authored
Commits on Dec 20, 2010
  1. Changed autosave code.

    Mani chandra authored
Commits on Dec 21, 2010
  1. Fixed autosave code.

    Mani chandra authored
This page is out of date. Refresh to see the latest.
View
22 IPython/core/history.py
@@ -17,6 +17,8 @@
import json
import os
import sys
+import threading
+import time
# Our own packages
import IPython.utils.io
@@ -251,6 +253,26 @@ def reset(self):
# The directory history can't be completely empty
self.dir_hist[:] = [os.getcwd()]
+class HistorySaveThread(threading.Thread):
+ """Thread to save history periodically"""
+
+ def __init__(self, IPython_object, time_interval, exit_now):
+ threading.Thread.__init__(self)
+ self.IPython_object = IPython_object
+ self.time_interval = time_interval
+ self.exit_now = exit_now
+ self.cond = threading.Condition()
+
+ def run(self):
+ while 1:
+ self.cond.acquire()
+ self.cond.wait(self.time_interval)
+ self.cond.release()
+ if self.exit_now==True:
+ break
+ #printing for debug
+ #print("Saving...")
+ self.IPython_object.save_history()
def magic_history(self, parameter_s = ''):
"""Print input history (_i<n> variables), with most recent last.
View
4 IPython/core/interactiveshell.py
@@ -45,6 +45,7 @@
from IPython.core.extensions import ExtensionManager
from IPython.core.fakemodule import FakeModule, init_fakemod_dict
from IPython.core.history import HistoryManager
+from IPython.core.history import HistorySaveThread
from IPython.core.inputsplitter import IPythonInputSplitter
from IPython.core.logger import Logger
from IPython.core.magic import Magic
@@ -293,6 +294,8 @@ def __init__(self, config=None, ipython_dir=None,
self.init_payload()
self.hooks.late_startup_hook()
atexit.register(self.atexit_operations)
+ self.history_thread = HistorySaveThread(self, 60, False)
+ self.history_thread.start()
# While we're trying to have each part of the code directly access what it
# needs without keeping redundant references to objects, we have too much
@@ -2523,7 +2526,6 @@ def atexit_operations(self):
except OSError:
pass
-
self.save_history()
# Clear all user namespaces to release all references cleanly.
View
5 IPython/frontend/terminal/interactiveshell.py
@@ -24,6 +24,7 @@
from IPython.core.error import TryNext
from IPython.core.usage import interactive_usage, default_banner
from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
+from IPython.core.history import HistorySaveThread
from IPython.lib.inputhook import enable_gui
from IPython.lib.pylabtools import pylab_activate
from IPython.utils.terminal import toggle_set_term_title, set_term_title
@@ -498,6 +499,10 @@ def exit(self):
This method calls the ask_exit callback."""
if self.confirm_exit:
if self.ask_yes_no('Do you really want to exit ([y]/n)?','y'):
+ self.shell.history_thread.exit_now=True
+ self.shell.history_thread.cond.acquire()
+ self.shell.history_thread.cond.notify()
+ self.shell.history_thread.cond.release()
self.ask_exit()
else:
self.ask_exit()
Something went wrong with that request. Please try again.