Fixup saving and loading state files
* All state files have a backup created before saving * The backup will now be used if saving or loading fails * GTKUI state files stored in new gtkui_state dir and common load/save functions created * Detects bad shutdown and archives timestamped state files in separate config directory.
This commit is contained in:
parent
2bbc1013be
commit
2c4ef9dbb3
|
@ -486,8 +486,8 @@ what is currently in the config and it could not convert the value
|
||||||
|
|
||||||
# Make a backup of the old config
|
# Make a backup of the old config
|
||||||
try:
|
try:
|
||||||
log.debug("Backing up old config file to %s~", filename)
|
log.debug("Backing up old config file to %s.bak", filename)
|
||||||
shutil.move(filename, filename + "~")
|
shutil.move(filename, filename + ".bak")
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.warning("Unable to backup old config...")
|
log.warning("Unable to backup old config...")
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ class AuthManager(component.Component):
|
||||||
def update(self):
|
def update(self):
|
||||||
auth_file = configmanager.get_config_dir("auth")
|
auth_file = configmanager.get_config_dir("auth")
|
||||||
# Check for auth file and create if necessary
|
# Check for auth file and create if necessary
|
||||||
if not os.path.exists(auth_file):
|
if not os.path.isfile(auth_file):
|
||||||
log.info("Authfile not found, recreating it.")
|
log.info("Authfile not found, recreating it.")
|
||||||
self.__load_auth_file()
|
self.__load_auth_file()
|
||||||
return
|
return
|
||||||
|
@ -192,36 +192,40 @@ class AuthManager(component.Component):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def write_auth_file(self):
|
def write_auth_file(self):
|
||||||
old_auth_file = configmanager.get_config_dir("auth")
|
filename = "auth"
|
||||||
new_auth_file = old_auth_file + '.new'
|
filepath = os.path.join(configmanager,get_config_dir(), filename)
|
||||||
bak_auth_file = old_auth_file + '.bak'
|
filepath_bak = filepath + ".bak"
|
||||||
# Let's first create a backup
|
|
||||||
if os.path.exists(old_auth_file):
|
|
||||||
shutil.copy2(old_auth_file, bak_auth_file)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
fd = open(new_auth_file, "w")
|
if os.path.isfile(filepath):
|
||||||
for account in self.__auth.values():
|
log.info("Creating backup of %s at: %s", filename, filepath_bak)
|
||||||
fd.write(
|
shutil.copy2(filepath, filepath_bak)
|
||||||
"%(username)s:%(password)s:%(authlevel_int)s\n" %
|
except IOError as ex:
|
||||||
account.data()
|
log.error("Unable to backup %s to %s: %s", filepath, filepath_bak, ex)
|
||||||
)
|
else:
|
||||||
fd.flush()
|
log.info("Saving the %s at: %s", filename, filepath)
|
||||||
os.fsync(fd.fileno())
|
try:
|
||||||
fd.close()
|
with open(filepath, "wb") as _file:
|
||||||
os.rename(new_auth_file, old_auth_file)
|
for account in self.__auth.values():
|
||||||
except:
|
_file.write("%(username)s:%(password)s:%(authlevel_int)s\n" % account.data())
|
||||||
# Something failed, let's restore the previous file
|
_file.flush()
|
||||||
if os.path.exists(bak_auth_file):
|
os.fsync(_file.fileno())
|
||||||
os.rename(bak_auth_file, old_auth_file)
|
except (IOError) as ex:
|
||||||
|
log.error("Unable to save %s: %s", filename, ex)
|
||||||
|
if os.path.isfile(filepath_bak):
|
||||||
|
log.info("Restoring backup of %s from: %s", filename, filepath_bak)
|
||||||
|
shutil.move(filepath_bak, filepath)
|
||||||
|
|
||||||
self.__load_auth_file()
|
self.__load_auth_file()
|
||||||
|
|
||||||
def __load_auth_file(self):
|
def __load_auth_file(self):
|
||||||
save_and_reload = False
|
save_and_reload = False
|
||||||
auth_file = configmanager.get_config_dir("auth")
|
filename = "auth"
|
||||||
|
auth_file = configmanager.get_config_dir(filename)
|
||||||
|
auth_file_bak = auth_file + ".bak"
|
||||||
|
|
||||||
# Check for auth file and create if necessary
|
# Check for auth file and create if necessary
|
||||||
if not os.path.exists(auth_file):
|
if not os.path.isfile(auth_file):
|
||||||
create_localclient_account()
|
create_localclient_account()
|
||||||
return self.__load_auth_file()
|
return self.__load_auth_file()
|
||||||
|
|
||||||
|
@ -232,10 +236,20 @@ class AuthManager(component.Component):
|
||||||
# File didn't change, no need for re-parsing's
|
# File didn't change, no need for re-parsing's
|
||||||
return
|
return
|
||||||
|
|
||||||
# Load the auth file into a dictionary: {username: Account(...)}
|
for _filepath in (auth_file, auth_file_bak):
|
||||||
f = open(auth_file, "r").readlines()
|
log.info("Opening %s for load: %s", filename, _filepath)
|
||||||
|
try:
|
||||||
|
with open(_filepath, "rb") as _file:
|
||||||
|
file_data = _file.readlines()
|
||||||
|
except (IOError), ex:
|
||||||
|
log.warning("Unable to load %s: %s", _filepath, ex)
|
||||||
|
file_data = []
|
||||||
|
else:
|
||||||
|
log.info("Successfully loaded %s: %s", filename, _filepath)
|
||||||
|
break
|
||||||
|
|
||||||
for line in f:
|
# Load the auth file into a dictionary: {username: Account(...)}
|
||||||
|
for line in file_data:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if line.startswith("#") or not line:
|
if line.startswith("#") or not line:
|
||||||
# This line is a comment or empty
|
# This line is a comment or empty
|
||||||
|
|
|
@ -38,6 +38,7 @@ from deluge._libtorrent import lt
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import glob
|
import glob
|
||||||
|
import shutil
|
||||||
import base64
|
import base64
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
@ -50,7 +51,7 @@ import twisted.web.error
|
||||||
from deluge.httpdownloader import download_file
|
from deluge.httpdownloader import download_file
|
||||||
from deluge import path_chooser_common
|
from deluge import path_chooser_common
|
||||||
|
|
||||||
import deluge.configmanager
|
from deluge.configmanager import ConfigManager, get_config_dir
|
||||||
import deluge.common
|
import deluge.common
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
from deluge.event import *
|
from deluge.event import *
|
||||||
|
@ -120,7 +121,7 @@ class Core(component.Component):
|
||||||
self.new_release = None
|
self.new_release = None
|
||||||
|
|
||||||
# Get the core config
|
# Get the core config
|
||||||
self.config = deluge.configmanager.ConfigManager("core.conf")
|
self.config = ConfigManager("core.conf")
|
||||||
self.config.save()
|
self.config.save()
|
||||||
|
|
||||||
# If there was an interface value from the command line, use it, but
|
# If there was an interface value from the command line, use it, but
|
||||||
|
@ -153,19 +154,46 @@ class Core(component.Component):
|
||||||
|
|
||||||
def __save_session_state(self):
|
def __save_session_state(self):
|
||||||
"""Saves the libtorrent session state"""
|
"""Saves the libtorrent session state"""
|
||||||
|
filename = "session.state"
|
||||||
|
filepath = get_config_dir(filename)
|
||||||
|
filepath_bak = filepath + ".bak"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
session_state = deluge.configmanager.get_config_dir("session.state")
|
if os.path.isfile(filepath):
|
||||||
open(session_state, "wb").write(lt.bencode(self.session.save_state()))
|
log.info("Creating backup of %s at: %s", filename, filepath_bak)
|
||||||
except Exception, e:
|
shutil.copy2(filepath, filepath_bak)
|
||||||
log.warning("Failed to save lt state: %s", e)
|
except IOError as ex:
|
||||||
|
log.error("Unable to backup %s to %s: %s", filepath, filepath_bak, ex)
|
||||||
|
else:
|
||||||
|
log.info("Saving the %s at: %s", filename, filepath)
|
||||||
|
try:
|
||||||
|
with open(filepath, "wb") as _file:
|
||||||
|
_file.write(lt.bencode(self.session.save_state()))
|
||||||
|
_file.flush()
|
||||||
|
os.fsync(_file.fileno())
|
||||||
|
except (IOError, EOFError) as ex:
|
||||||
|
log.error("Unable to save %s: %s", filename, ex)
|
||||||
|
if os.path.isfile(filepath_bak):
|
||||||
|
log.info("Restoring backup of %s from: %s", filename, filepath_bak)
|
||||||
|
shutil.move(filepath_bak, filepath)
|
||||||
|
|
||||||
def __load_session_state(self):
|
def __load_session_state(self):
|
||||||
"""Loads the libtorrent session state"""
|
"""Loads the libtorrent session state"""
|
||||||
try:
|
filename = "session.state"
|
||||||
session_state = deluge.configmanager.get_config_dir("session.state")
|
filepath = get_config_dir(filename)
|
||||||
self.session.load_state(lt.bdecode(open(session_state, "rb").read()))
|
filepath_bak = filepath + ".bak"
|
||||||
except Exception, e:
|
|
||||||
log.warning("Failed to load lt state: %s", e)
|
for _filepath in (filepath, filepath_bak):
|
||||||
|
log.info("Opening %s for load: %s", filename, _filepath)
|
||||||
|
try:
|
||||||
|
with open(_filepath, "rb") as _file:
|
||||||
|
state = lt.bdecode(_file.read())
|
||||||
|
except (IOError, EOFError, RuntimeError), ex:
|
||||||
|
log.warning("Unable to load %s: %s", _filepath, ex)
|
||||||
|
else:
|
||||||
|
log.info("Successfully loaded %s: %s", filename, _filepath)
|
||||||
|
self.session.load_state(state)
|
||||||
|
return
|
||||||
|
|
||||||
def get_new_release(self):
|
def get_new_release(self):
|
||||||
log.debug("get_new_release")
|
log.debug("get_new_release")
|
||||||
|
@ -679,7 +707,7 @@ class Core(component.Component):
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
return
|
return
|
||||||
|
|
||||||
f = open(os.path.join(deluge.configmanager.get_config_dir(), "plugins", filename), "wb")
|
f = open(os.path.join(get_config_dir(), "plugins", filename), "wb")
|
||||||
f.write(filedump)
|
f.write(filedump)
|
||||||
f.close()
|
f.close()
|
||||||
component.get("CorePluginManager").scan_for_plugins()
|
component.get("CorePluginManager").scan_for_plugins()
|
||||||
|
|
|
@ -144,8 +144,9 @@ class TorrentManager(component.Component):
|
||||||
self.config = ConfigManager("core.conf")
|
self.config = ConfigManager("core.conf")
|
||||||
|
|
||||||
# Make sure the state folder has been created
|
# Make sure the state folder has been created
|
||||||
if not os.path.exists(os.path.join(get_config_dir(), "state")):
|
self.state_dir = os.path.join(get_config_dir(), "state")
|
||||||
os.makedirs(os.path.join(get_config_dir(), "state"))
|
if not os.path.exists(self.state_dir):
|
||||||
|
os.makedirs(self.state_dir)
|
||||||
|
|
||||||
# Create the torrents dict { torrent_id: Torrent }
|
# Create the torrents dict { torrent_id: Torrent }
|
||||||
self.torrents = {}
|
self.torrents = {}
|
||||||
|
@ -212,6 +213,34 @@ class TorrentManager(component.Component):
|
||||||
# Get the pluginmanager reference
|
# Get the pluginmanager reference
|
||||||
self.plugins = component.get("CorePluginManager")
|
self.plugins = component.get("CorePluginManager")
|
||||||
|
|
||||||
|
# Check for temp file
|
||||||
|
self.temp_file = os.path.join(self.state_dir, ".safe_state_check")
|
||||||
|
if os.path.isfile(self.temp_file):
|
||||||
|
def archive_file(filename):
|
||||||
|
import datetime
|
||||||
|
filepath = os.path.join(self.state_dir, filename)
|
||||||
|
filepath_bak = state_filepath + ".bak"
|
||||||
|
archive_dir = os.path.join(get_config_dir(), "archive")
|
||||||
|
if not os.path.exists(archive_dir):
|
||||||
|
os.makedirs(archive_dir)
|
||||||
|
|
||||||
|
for _filepath in (filepath, filepath_bak):
|
||||||
|
timestamp = datetime.datetime.now().replace(microsecond=0).isoformat().replace(':', '-')
|
||||||
|
archive_filepath = os.path.join(archive_dir, filename + "-" + timestamp)
|
||||||
|
try:
|
||||||
|
shutil.copy2(_filepath, archive_filepath)
|
||||||
|
except IOError:
|
||||||
|
log.error("Unable to archive: %s", filename)
|
||||||
|
else:
|
||||||
|
log.info("Archive of %s successful: %s", filename, archive_filepath)
|
||||||
|
|
||||||
|
log.warning("Potential bad shutdown of Deluge detected, archiving torrent state files...")
|
||||||
|
archive_file("torrents.state")
|
||||||
|
archive_file("torrents.fastresume")
|
||||||
|
else:
|
||||||
|
with file(self.temp_file, 'a'):
|
||||||
|
os.utime(self.temp_file, None)
|
||||||
|
|
||||||
# Run the old state upgrader before loading state
|
# Run the old state upgrader before loading state
|
||||||
deluge.core.oldstateupgrader.OldStateUpgrader()
|
deluge.core.oldstateupgrader.OldStateUpgrader()
|
||||||
|
|
||||||
|
@ -246,7 +275,13 @@ class TorrentManager(component.Component):
|
||||||
# Stop the status cleanup LoopingCall here
|
# Stop the status cleanup LoopingCall here
|
||||||
self.torrents[key].prev_status_cleanup_loop.stop()
|
self.torrents[key].prev_status_cleanup_loop.stop()
|
||||||
|
|
||||||
return self.save_resume_data(self.torrents.keys())
|
def remove_temp_file(result):
|
||||||
|
if result and os.path.isfile(self.temp_file):
|
||||||
|
os.remove(self.temp_file)
|
||||||
|
|
||||||
|
d = self.save_resume_data(self.torrents.keys())
|
||||||
|
d.addCallback(remove_temp_file)
|
||||||
|
return d
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
for torrent_id, torrent in self.torrents.items():
|
for torrent_id, torrent in self.torrents.items():
|
||||||
|
@ -301,8 +336,7 @@ class TorrentManager(component.Component):
|
||||||
"""Returns an entry with the resume data or None"""
|
"""Returns an entry with the resume data or None"""
|
||||||
fastresume = ""
|
fastresume = ""
|
||||||
try:
|
try:
|
||||||
_file = open(os.path.join(get_config_dir(), "state",
|
_file = open(os.path.join(self.state_dir, torrent_id + ".fastresume"), "rb")
|
||||||
torrent_id + ".fastresume"), "rb")
|
|
||||||
fastresume = _file.read()
|
fastresume = _file.read()
|
||||||
_file.close()
|
_file.close()
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
|
@ -312,8 +346,7 @@ class TorrentManager(component.Component):
|
||||||
|
|
||||||
def legacy_delete_resume_data(self, torrent_id):
|
def legacy_delete_resume_data(self, torrent_id):
|
||||||
"""Deletes the .fastresume file"""
|
"""Deletes the .fastresume file"""
|
||||||
path = os.path.join(get_config_dir(), "state",
|
path = os.path.join(self.state_dir, torrent_id + ".fastresume")
|
||||||
torrent_id + ".fastresume")
|
|
||||||
log.debug("Deleting fastresume file: %s", path)
|
log.debug("Deleting fastresume file: %s", path)
|
||||||
try:
|
try:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
|
@ -501,9 +534,7 @@ class TorrentManager(component.Component):
|
||||||
# Write the .torrent file to the state directory
|
# Write the .torrent file to the state directory
|
||||||
if filedump:
|
if filedump:
|
||||||
try:
|
try:
|
||||||
save_file = open(os.path.join(get_config_dir(), "state",
|
save_file = open(os.path.join(self.state_dir, torrent.torrent_id + ".torrent"), "wb")
|
||||||
torrent.torrent_id + ".torrent"),
|
|
||||||
"wb")
|
|
||||||
save_file.write(filedump)
|
save_file.write(filedump)
|
||||||
save_file.close()
|
save_file.close()
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
|
@ -546,10 +577,7 @@ class TorrentManager(component.Component):
|
||||||
# Get the torrent data from the torrent file
|
# Get the torrent data from the torrent file
|
||||||
try:
|
try:
|
||||||
log.debug("Attempting to open %s for add.", torrent_id)
|
log.debug("Attempting to open %s for add.", torrent_id)
|
||||||
_file = open(
|
_file = open(os.path.join(self.state_dir, torrent_id + ".torrent"), "rb")
|
||||||
os.path.join(
|
|
||||||
get_config_dir(), "state", torrent_id + ".torrent"),
|
|
||||||
"rb")
|
|
||||||
filedump = lt.bdecode(_file.read())
|
filedump = lt.bdecode(_file.read())
|
||||||
_file.close()
|
_file.close()
|
||||||
except (IOError, RuntimeError), e:
|
except (IOError, RuntimeError), e:
|
||||||
|
@ -636,16 +664,24 @@ class TorrentManager(component.Component):
|
||||||
|
|
||||||
def load_state(self):
|
def load_state(self):
|
||||||
"""Load the state of the TorrentManager from the torrents.state file"""
|
"""Load the state of the TorrentManager from the torrents.state file"""
|
||||||
state = TorrentManagerState()
|
filename = "torrents.state"
|
||||||
|
filepath = os.path.join(self.state_dir, filename)
|
||||||
|
filepath_bak = filepath + ".bak"
|
||||||
|
|
||||||
try:
|
for _filepath in (filepath, filepath_bak):
|
||||||
log.debug("Opening torrent state file for load.")
|
log.info("Opening %s for load: %s", filename, _filepath)
|
||||||
state_file = open(
|
try:
|
||||||
os.path.join(get_config_dir(), "state", "torrents.state"), "rb")
|
with open(_filepath, "rb") as _file:
|
||||||
state = cPickle.load(state_file)
|
state = cPickle.load(_file)
|
||||||
state_file.close()
|
except (IOError, EOFError, cPickle.UnpicklingError), ex:
|
||||||
except (EOFError, IOError, Exception, cPickle.UnpicklingError), e:
|
log.warning("Unable to load %s: %s", _filepath, ex)
|
||||||
log.warning("Unable to load state file: %s", e)
|
state = None
|
||||||
|
else:
|
||||||
|
log.info("Successfully loaded %s: %s", filename, _filepath)
|
||||||
|
break
|
||||||
|
|
||||||
|
if state is None:
|
||||||
|
state = TorrentManagerState()
|
||||||
|
|
||||||
# Try to use an old state
|
# Try to use an old state
|
||||||
try:
|
try:
|
||||||
|
@ -727,28 +763,29 @@ class TorrentManager(component.Component):
|
||||||
)
|
)
|
||||||
state.torrents.append(torrent_state)
|
state.torrents.append(torrent_state)
|
||||||
|
|
||||||
# Pickle the TorrentManagerState object
|
filename = "torrents.state"
|
||||||
try:
|
filepath = os.path.join(self.state_dir, filename)
|
||||||
log.debug("Saving torrent state file.")
|
filepath_bak = filepath + ".bak"
|
||||||
state_file = open(os.path.join(get_config_dir(),
|
|
||||||
"state", "torrents.state.new"), "wb")
|
|
||||||
cPickle.dump(state, state_file)
|
|
||||||
state_file.flush()
|
|
||||||
os.fsync(state_file.fileno())
|
|
||||||
state_file.close()
|
|
||||||
except IOError, e:
|
|
||||||
log.warning("Unable to save state file: %s", e)
|
|
||||||
return True
|
|
||||||
|
|
||||||
# We have to move the 'torrents.state.new' file to 'torrents.state'
|
|
||||||
try:
|
try:
|
||||||
shutil.move(
|
if os.path.isfile(filepath):
|
||||||
os.path.join(get_config_dir(), "state", "torrents.state.new"),
|
log.info("Creating backup of %s at: %s", filename, filepath_bak)
|
||||||
os.path.join(get_config_dir(), "state", "torrents.state"))
|
shutil.copy2(filepath, filepath_bak)
|
||||||
except IOError:
|
except IOError as ex:
|
||||||
log.warning("Unable to save state file.")
|
log.error("Unable to backup %s to %s: %s", filepath, filepath_bak, ex)
|
||||||
return True
|
else:
|
||||||
|
log.info("Saving the %s at: %s", filename, filepath)
|
||||||
|
try:
|
||||||
|
with open(filepath, "wb") as _file:
|
||||||
|
# Pickle the TorrentManagerState object
|
||||||
|
cPickle.dump(state, _file)
|
||||||
|
_file.flush()
|
||||||
|
os.fsync(_file.fileno())
|
||||||
|
except (IOError, cPickle.PicklingError) as ex:
|
||||||
|
log.error("Unable to save %s: %s", filename, ex)
|
||||||
|
if os.path.isfile(filepath_bak):
|
||||||
|
log.info("Restoring backup of %s from: %s", filename, filepath_bak)
|
||||||
|
shutil.move(filepath_bak, filepath)
|
||||||
# We return True so that the timer thread will continue
|
# We return True so that the timer thread will continue
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -760,7 +797,6 @@ class TorrentManager(component.Component):
|
||||||
:returns: A Deferred whose callback will be invoked when save is complete
|
:returns: A Deferred whose callback will be invoked when save is complete
|
||||||
:rtype: twisted.internet.defer.Deferred
|
:rtype: twisted.internet.defer.Deferred
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if torrent_ids is None:
|
if torrent_ids is None:
|
||||||
torrent_ids = (t[0] for t in self.torrents.iteritems() if t[1].handle.need_save_resume_data())
|
torrent_ids = (t[0] for t in self.torrents.iteritems() if t[1].handle.need_save_resume_data())
|
||||||
|
|
||||||
|
@ -779,43 +815,63 @@ class TorrentManager(component.Component):
|
||||||
|
|
||||||
def on_all_resume_data_finished(result):
|
def on_all_resume_data_finished(result):
|
||||||
if result:
|
if result:
|
||||||
self.save_resume_data_file()
|
if self.save_resume_data_file():
|
||||||
|
return True
|
||||||
|
|
||||||
return DeferredList(deferreds).addBoth(on_all_resume_data_finished)
|
return DeferredList(deferreds).addBoth(on_all_resume_data_finished)
|
||||||
|
|
||||||
def load_resume_data_file(self):
|
def load_resume_data_file(self):
|
||||||
resume_data = {}
|
filename = "torrents.fastresume"
|
||||||
try:
|
filepath = os.path.join(self.state_dir, filename)
|
||||||
log.debug("Opening torrents fastresume file for load.")
|
filepath_bak = filepath + ".bak"
|
||||||
fastresume_file = open(os.path.join(get_config_dir(), "state",
|
old_data_filepath = os.path.join(get_config_dir(), filename)
|
||||||
"torrents.fastresume"), "rb")
|
|
||||||
resume_data = lt.bdecode(fastresume_file.read())
|
|
||||||
fastresume_file.close()
|
|
||||||
except (EOFError, IOError, Exception), e:
|
|
||||||
log.warning("Unable to load fastresume file: %s", e)
|
|
||||||
|
|
||||||
|
for _filepath in (filepath, filepath_bak, old_data_filepath):
|
||||||
|
log.info("Opening %s for load: %s", filename, _filepath)
|
||||||
|
try:
|
||||||
|
with open(_filepath, "rb") as _file:
|
||||||
|
resume_data = lt.bdecode(_file.read())
|
||||||
|
except (IOError, EOFError, RuntimeError), ex:
|
||||||
|
log.warning("Unable to load %s: %s", _filepath, ex)
|
||||||
|
resume_data = None
|
||||||
|
else:
|
||||||
|
log.info("Successfully loaded %s: %s", filename, _filepath)
|
||||||
|
break
|
||||||
# If the libtorrent bdecode doesn't happen properly, it will return None
|
# If the libtorrent bdecode doesn't happen properly, it will return None
|
||||||
# so we need to make sure we return a {}
|
# so we need to make sure we return a {}
|
||||||
if resume_data is None:
|
if resume_data is None:
|
||||||
return {}
|
return {}
|
||||||
|
else:
|
||||||
return resume_data
|
return resume_data
|
||||||
|
|
||||||
def save_resume_data_file(self):
|
def save_resume_data_file(self):
|
||||||
"""
|
"""
|
||||||
Saves the resume data file with the contents of self.resume_data.
|
Saves the resume data file with the contents of self.resume_data.
|
||||||
"""
|
"""
|
||||||
path = os.path.join(get_config_dir(), "state", "torrents.fastresume")
|
filename = "torrents.fastresume"
|
||||||
|
filepath = os.path.join(self.state_dir, filename)
|
||||||
|
filepath_bak = filepath + ".bak"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
log.debug("Saving fastresume file: %s", path)
|
if os.path.isfile(filepath):
|
||||||
fastresume_file = open(path, "wb")
|
log.info("Creating backup of %s at: %s", filename, filepath_bak)
|
||||||
fastresume_file.write(lt.bencode(self.resume_data))
|
shutil.copy2(filepath, filepath_bak)
|
||||||
fastresume_file.flush()
|
except IOError as ex:
|
||||||
os.fsync(fastresume_file.fileno())
|
log.error("Unable to backup %s to %s: %s", filepath, filepath_bak, ex)
|
||||||
fastresume_file.close()
|
else:
|
||||||
except IOError:
|
log.info("Saving the %s at: %s", filename, filepath)
|
||||||
log.warning("Error trying to save fastresume file")
|
try:
|
||||||
|
with open(filepath, "wb") as _file:
|
||||||
|
_file.write(lt.bencode(self.session.save_state()))
|
||||||
|
_file.flush()
|
||||||
|
os.fsync(_file.fileno())
|
||||||
|
except (IOError, EOFError) as ex:
|
||||||
|
log.error("Unable to save %s: %s", filename, ex)
|
||||||
|
if os.path.isfile(filepath_bak):
|
||||||
|
log.info("Restoring backup of %s from: %s", filename, filepath_bak)
|
||||||
|
shutil.move(filepath_bak, filepath)
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
def get_queue_position(self, torrent_id):
|
def get_queue_position(self, torrent_id):
|
||||||
"""Get queue position of torrent"""
|
"""Get queue position of torrent"""
|
||||||
|
|
|
@ -41,6 +41,8 @@ import pygtk
|
||||||
pygtk.require('2.0')
|
pygtk.require('2.0')
|
||||||
import gtk
|
import gtk
|
||||||
import logging
|
import logging
|
||||||
|
import cPickle
|
||||||
|
import shutil
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.common
|
import deluge.common
|
||||||
|
@ -264,3 +266,53 @@ def associate_magnet_links(overwrite=False):
|
||||||
log.error("Unable to register Deluge as default magnet uri handler.")
|
log.error("Unable to register Deluge as default magnet uri handler.")
|
||||||
return False
|
return False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def save_pickled_state_file(filename, state):
|
||||||
|
"""Save a file in the config directory and creates a backup
|
||||||
|
filename: Filename to be saved to config
|
||||||
|
state: The data to be pickled and written to file
|
||||||
|
"""
|
||||||
|
from deluge.configmanager import get_config_dir
|
||||||
|
filepath = os.path.join(get_config_dir(), "gtkui_state", filename)
|
||||||
|
filepath_bak = filepath + ".bak"
|
||||||
|
|
||||||
|
try:
|
||||||
|
if os.path.isfile(filepath):
|
||||||
|
log.info("Creating backup of %s at: %s", filename, filepath_bak)
|
||||||
|
shutil.copy2(filepath, filepath_bak)
|
||||||
|
except IOError as ex:
|
||||||
|
log.error("Unable to backup %s to %s: %s", filepath, filepath_bak, ex)
|
||||||
|
else:
|
||||||
|
log.info("Saving the %s at: %s", filename, filepath)
|
||||||
|
try:
|
||||||
|
with open(filepath, "wb") as _file:
|
||||||
|
# Pickle the state object
|
||||||
|
cPickle.dump(state, _file)
|
||||||
|
_file.flush()
|
||||||
|
os.fsync(_file.fileno())
|
||||||
|
except (IOError, EOFError, cPickle.PicklingError) as ex:
|
||||||
|
log.error("Unable to save %s: %s", filename, ex)
|
||||||
|
if os.path.isfile(filepath_bak):
|
||||||
|
log.info("Restoring backup of %s from: %s", filename, filepath_bak)
|
||||||
|
shutil.move(filepath_bak, filepath)
|
||||||
|
|
||||||
|
def load_pickled_state_file(filename):
|
||||||
|
"""Loads a file from the config directory, attempting backup if original fails to load.
|
||||||
|
filename: Filename to be loaded from config
|
||||||
|
returns unpickled state
|
||||||
|
"""
|
||||||
|
from deluge.configmanager import get_config_dir
|
||||||
|
filepath = os.path.join(get_config_dir(), "gtkui_state", filename)
|
||||||
|
filepath_bak = filepath + ".bak"
|
||||||
|
old_data_filepath = os.path.join(get_config_dir(), filename)
|
||||||
|
|
||||||
|
for _filepath in (filepath, filepath_bak, old_data_filepath):
|
||||||
|
log.info("Opening %s for load: %s", filename, _filepath)
|
||||||
|
try:
|
||||||
|
with open(_filepath, "rb") as _file:
|
||||||
|
state = cPickle.load(_file)
|
||||||
|
except (IOError, cPickle.UnpicklingError), ex:
|
||||||
|
log.warning("Unable to load %s: %s", _filepath, ex)
|
||||||
|
else:
|
||||||
|
log.info("Successfully loaded %s: %s", filename, _filepath)
|
||||||
|
return state
|
||||||
|
|
|
@ -38,15 +38,14 @@ import gtk
|
||||||
import gtk.gdk
|
import gtk.gdk
|
||||||
import gobject
|
import gobject
|
||||||
import os.path
|
import os.path
|
||||||
import cPickle
|
|
||||||
import logging
|
import logging
|
||||||
|
import cPickle
|
||||||
|
|
||||||
from deluge.ui.gtkui.torrentdetails import Tab
|
from deluge.ui.gtkui.torrentdetails import Tab
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
import deluge.configmanager
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.common
|
import deluge.common
|
||||||
import common
|
from deluge.ui.gtkui.common import reparent_iter, save_pickled_state_file, load_pickled_state_file
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -242,7 +241,6 @@ class FilesTab(Tab):
|
||||||
getattr(widget, attr)()
|
getattr(widget, attr)()
|
||||||
|
|
||||||
def save_state(self):
|
def save_state(self):
|
||||||
filename = "files_tab.state"
|
|
||||||
# Get the current sort order of the view
|
# Get the current sort order of the view
|
||||||
column_id, sort_order = self.treestore.get_sort_column_id()
|
column_id, sort_order = self.treestore.get_sort_column_id()
|
||||||
|
|
||||||
|
@ -259,30 +257,10 @@ class FilesTab(Tab):
|
||||||
"width": column.get_width()
|
"width": column.get_width()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get the config location for saving the state file
|
save_pickled_state_file("files_tab.state", state)
|
||||||
config_location = deluge.configmanager.get_config_dir()
|
|
||||||
|
|
||||||
try:
|
|
||||||
log.debug("Saving FilesTab state file: %s", filename)
|
|
||||||
state_file = open(os.path.join(config_location, filename), "wb")
|
|
||||||
cPickle.dump(state, state_file)
|
|
||||||
state_file.close()
|
|
||||||
except IOError, e:
|
|
||||||
log.warning("Unable to save state file: %s", e)
|
|
||||||
|
|
||||||
def load_state(self):
|
def load_state(self):
|
||||||
filename = "files_tab.state"
|
state = load_pickled_state_file("files_tabs.state")
|
||||||
# Get the config location for loading the state file
|
|
||||||
config_location = deluge.configmanager.get_config_dir()
|
|
||||||
state = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
log.debug("Loading FilesTab state file: %s", filename)
|
|
||||||
state_file = open(os.path.join(config_location, filename), "rb")
|
|
||||||
state = cPickle.load(state_file)
|
|
||||||
state_file.close()
|
|
||||||
except (EOFError, IOError, AttributeError, cPickle.UnpicklingError), e:
|
|
||||||
log.warning("Unable to load state file: %s", e)
|
|
||||||
|
|
||||||
if state == None:
|
if state == None:
|
||||||
return
|
return
|
||||||
|
@ -807,14 +785,14 @@ class FilesTab(Tab):
|
||||||
return
|
return
|
||||||
if new_folder_iter:
|
if new_folder_iter:
|
||||||
# This means that a folder by this name already exists
|
# This means that a folder by this name already exists
|
||||||
common.reparent_iter(self.treestore, self.treestore.iter_children(old_folder_iter), new_folder_iter)
|
reparent_iter(self.treestore, self.treestore.iter_children(old_folder_iter), new_folder_iter)
|
||||||
else:
|
else:
|
||||||
parent = old_folder_iter_parent
|
parent = old_folder_iter_parent
|
||||||
for ns in new_split[:-1]:
|
for ns in new_split[:-1]:
|
||||||
parent = self.treestore.append(parent, [ns + "/", 0, "", 0, 0, -1, gtk.STOCK_DIRECTORY])
|
parent = self.treestore.append(parent, [ns + "/", 0, "", 0, 0, -1, gtk.STOCK_DIRECTORY])
|
||||||
|
|
||||||
self.treestore[old_folder_iter][0] = new_split[-1] + "/"
|
self.treestore[old_folder_iter][0] = new_split[-1] + "/"
|
||||||
common.reparent_iter(self.treestore, old_folder_iter, parent)
|
reparent_iter(self.treestore, old_folder_iter, parent)
|
||||||
|
|
||||||
# We need to check if the old_folder_iter_parent no longer has children
|
# We need to check if the old_folder_iter_parent no longer has children
|
||||||
# and if so, we delete it
|
# and if so, we delete it
|
||||||
|
|
|
@ -40,6 +40,7 @@ gobject.set_prgname("deluge")
|
||||||
from twisted.internet import gtk2reactor
|
from twisted.internet import gtk2reactor
|
||||||
reactor = gtk2reactor.install()
|
reactor = gtk2reactor.install()
|
||||||
|
|
||||||
|
import os
|
||||||
import gtk
|
import gtk
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
@ -71,14 +72,13 @@ from queuedtorrents import QueuedTorrents
|
||||||
from addtorrentdialog import AddTorrentDialog
|
from addtorrentdialog import AddTorrentDialog
|
||||||
from deluge.ui.sessionproxy import SessionProxy
|
from deluge.ui.sessionproxy import SessionProxy
|
||||||
import dialogs
|
import dialogs
|
||||||
import common
|
from deluge.ui.gtkui.common import associate_magnet_links
|
||||||
|
from deluge.configmanager import ConfigManager, get_config_dir
|
||||||
import deluge.configmanager
|
|
||||||
import deluge.common
|
import deluge.common
|
||||||
import deluge.error
|
import deluge.error
|
||||||
|
|
||||||
from deluge.ui.ui import _UI
|
from deluge.ui.ui import _UI
|
||||||
|
|
||||||
|
|
||||||
class Gtk(_UI):
|
class Gtk(_UI):
|
||||||
|
|
||||||
help = """Starts the Deluge GTK+ interface"""
|
help = """Starts the Deluge GTK+ interface"""
|
||||||
|
@ -90,6 +90,7 @@ class Gtk(_UI):
|
||||||
super(Gtk, self).start()
|
super(Gtk, self).start()
|
||||||
GtkUI(self.args)
|
GtkUI(self.args)
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
Gtk().start()
|
Gtk().start()
|
||||||
|
|
||||||
|
@ -152,6 +153,7 @@ DEFAULT_PREFS = {
|
||||||
"focus_main_window_on_add": True,
|
"focus_main_window_on_add": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class GtkUI(object):
|
class GtkUI(object):
|
||||||
def __init__(self, args):
|
def __init__(self, args):
|
||||||
self.daemon_bps = (0,0,0)
|
self.daemon_bps = (0,0,0)
|
||||||
|
@ -192,10 +194,14 @@ class GtkUI(object):
|
||||||
|
|
||||||
# Attempt to register a magnet URI handler with gconf, but do not overwrite
|
# Attempt to register a magnet URI handler with gconf, but do not overwrite
|
||||||
# if already set by another program.
|
# if already set by another program.
|
||||||
common.associate_magnet_links(False)
|
associate_magnet_links(False)
|
||||||
|
|
||||||
# Make sure gtkui.conf has at least the defaults set
|
# Make sure gtkui.conf has at least the defaults set
|
||||||
self.config = deluge.configmanager.ConfigManager("gtkui.conf", DEFAULT_PREFS)
|
self.config = ConfigManager("gtkui.conf", DEFAULT_PREFS)
|
||||||
|
|
||||||
|
# Make sure the gtkui state folder has been created
|
||||||
|
if not os.path.exists(os.path.join(get_config_dir(), "gtkui_state")):
|
||||||
|
os.makedirs(os.path.join(get_config_dir(), "gtkui_state"))
|
||||||
|
|
||||||
# We need to check on exit if it was started in classic mode to ensure we
|
# We need to check on exit if it was started in classic mode to ensure we
|
||||||
# shutdown the daemon.
|
# shutdown the daemon.
|
||||||
|
@ -370,7 +376,7 @@ Please see the details below for more information."), details=traceback.format_e
|
||||||
if self.config["autostart_localhost"] and host in ("localhost", "127.0.0.1"):
|
if self.config["autostart_localhost"] and host in ("localhost", "127.0.0.1"):
|
||||||
log.debug("Autostarting localhost:%s", host)
|
log.debug("Autostarting localhost:%s", host)
|
||||||
try_connect = client.start_daemon(
|
try_connect = client.start_daemon(
|
||||||
port, deluge.configmanager.get_config_dir()
|
port, get_config_dir()
|
||||||
)
|
)
|
||||||
log.debug("Localhost started: %s", try_connect)
|
log.debug("Localhost started: %s", try_connect)
|
||||||
if not try_connect:
|
if not try_connect:
|
||||||
|
|
|
@ -34,19 +34,13 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
import cPickle
|
|
||||||
import os.path
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pygtk
|
import pygtk
|
||||||
pygtk.require('2.0')
|
pygtk.require('2.0')
|
||||||
import gtk
|
import gtk
|
||||||
import gettext
|
|
||||||
|
|
||||||
from deluge.configmanager import ConfigManager
|
|
||||||
import deluge.configmanager
|
|
||||||
import deluge.common
|
import deluge.common
|
||||||
|
from deluge.ui.gtkui.common import save_pickled_state_file, load_pickled_state_file
|
||||||
|
|
||||||
from gobject import signal_new, SIGNAL_RUN_LAST, TYPE_NONE
|
from gobject import signal_new, SIGNAL_RUN_LAST, TYPE_NONE
|
||||||
from gtk import gdk
|
from gtk import gdk
|
||||||
|
@ -333,34 +327,11 @@ class ListView:
|
||||||
state.append(self.create_column_state(column, counter))
|
state.append(self.create_column_state(column, counter))
|
||||||
|
|
||||||
state += self.removed_columns_state
|
state += self.removed_columns_state
|
||||||
|
save_pickled_state_file(filename, state)
|
||||||
# Get the config location for saving the state file
|
|
||||||
config_location = deluge.configmanager.get_config_dir()
|
|
||||||
|
|
||||||
try:
|
|
||||||
log.debug("Saving ListView state file: %s", filename)
|
|
||||||
state_file = open(os.path.join(config_location, filename), "wb")
|
|
||||||
cPickle.dump(state, state_file)
|
|
||||||
state_file.close()
|
|
||||||
except IOError, e:
|
|
||||||
log.warning("Unable to save state file: %s", e)
|
|
||||||
|
|
||||||
def load_state(self, filename):
|
def load_state(self, filename):
|
||||||
"""Load the listview state from filename."""
|
"""Load the listview state from filename."""
|
||||||
# Get the config location for loading the state file
|
self.state = load_pickled_state_file(filename)
|
||||||
config_location = deluge.configmanager.get_config_dir()
|
|
||||||
state = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
log.debug("Loading ListView state file: %s", filename)
|
|
||||||
state_file = open(os.path.join(config_location, filename), "rb")
|
|
||||||
state = cPickle.load(state_file)
|
|
||||||
state_file.close()
|
|
||||||
except (EOFError, IOError, cPickle.UnpicklingError), e:
|
|
||||||
log.warning("Unable to load state file: %s", e)
|
|
||||||
|
|
||||||
# Keep the state in self.state so we can access it as we add new columns
|
|
||||||
self.state = state
|
|
||||||
|
|
||||||
def set_treeview(self, treeview_widget):
|
def set_treeview(self, treeview_widget):
|
||||||
"""Set the treeview widget that this listview uses."""
|
"""Set the treeview widget that this listview uses."""
|
||||||
|
|
|
@ -36,16 +36,15 @@
|
||||||
import gtk
|
import gtk
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
import cPickle
|
|
||||||
from itertools import izip
|
from itertools import izip
|
||||||
|
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
import deluge.configmanager
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.common
|
import deluge.common
|
||||||
from deluge.ui.gtkui.listview import cell_data_speed as cell_data_speed
|
from deluge.ui.gtkui.listview import cell_data_speed as cell_data_speed
|
||||||
from deluge.ui.gtkui.torrentdetails import Tab
|
from deluge.ui.gtkui.torrentdetails import Tab
|
||||||
from deluge.ui.countries import COUNTRIES
|
from deluge.ui.countries import COUNTRIES
|
||||||
|
from deluge.ui.gtkui.common import save_pickled_state_file, load_pickled_state_file
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -171,7 +170,6 @@ class PeersTab(Tab):
|
||||||
self.torrent_id = None
|
self.torrent_id = None
|
||||||
|
|
||||||
def save_state(self):
|
def save_state(self):
|
||||||
filename = "peers_tab.state"
|
|
||||||
# Get the current sort order of the view
|
# Get the current sort order of the view
|
||||||
column_id, sort_order = self.liststore.get_sort_column_id()
|
column_id, sort_order = self.liststore.get_sort_column_id()
|
||||||
|
|
||||||
|
@ -187,31 +185,10 @@ class PeersTab(Tab):
|
||||||
"position": index,
|
"position": index,
|
||||||
"width": column.get_width()
|
"width": column.get_width()
|
||||||
}
|
}
|
||||||
|
save_pickled_state_file("peers_tab.state", state)
|
||||||
# Get the config location for saving the state file
|
|
||||||
config_location = deluge.configmanager.get_config_dir()
|
|
||||||
|
|
||||||
try:
|
|
||||||
log.debug("Saving FilesTab state file: %s", filename)
|
|
||||||
state_file = open(os.path.join(config_location, filename), "wb")
|
|
||||||
cPickle.dump(state, state_file)
|
|
||||||
state_file.close()
|
|
||||||
except IOError, e:
|
|
||||||
log.warning("Unable to save state file: %s", e)
|
|
||||||
|
|
||||||
def load_state(self):
|
def load_state(self):
|
||||||
filename = "peers_tab.state"
|
state = load_pickled_state_file("peers_tabs.state")
|
||||||
# Get the config location for loading the state file
|
|
||||||
config_location = deluge.configmanager.get_config_dir()
|
|
||||||
state = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
log.debug("Loading PeersTab state file: %s", filename)
|
|
||||||
state_file = open(os.path.join(config_location, filename), "rb")
|
|
||||||
state = cPickle.load(state_file)
|
|
||||||
state_file.close()
|
|
||||||
except (EOFError, IOError, AttributeError, cPickle.UnpicklingError), e:
|
|
||||||
log.warning("Unable to load state file: %s", e)
|
|
||||||
|
|
||||||
if state == None:
|
if state == None:
|
||||||
return
|
return
|
||||||
|
|
|
@ -37,15 +37,11 @@
|
||||||
"""The torrent details component shows info about the selected torrent."""
|
"""The torrent details component shows info about the selected torrent."""
|
||||||
|
|
||||||
import gtk
|
import gtk
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import cPickle
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.configmanager import ConfigManager
|
from deluge.ui.gtkui.common import save_pickled_state_file, load_pickled_state_file
|
||||||
import deluge.configmanager
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -411,8 +407,6 @@ class TorrentDetails(component.Component):
|
||||||
|
|
||||||
def save_state(self):
|
def save_state(self):
|
||||||
"""We save the state, which is basically the tab_index list"""
|
"""We save the state, which is basically the tab_index list"""
|
||||||
filename = "tabs.state"
|
|
||||||
|
|
||||||
#Update the visiblity status of all tabs
|
#Update the visiblity status of all tabs
|
||||||
#Leave tabs we dont know anything about it the state as they
|
#Leave tabs we dont know anything about it the state as they
|
||||||
#might come from a plugin
|
#might come from a plugin
|
||||||
|
@ -423,29 +417,7 @@ class TorrentDetails(component.Component):
|
||||||
log.debug("Set to %s %d" % self.state[i])
|
log.debug("Set to %s %d" % self.state[i])
|
||||||
state = self.state
|
state = self.state
|
||||||
|
|
||||||
# Get the config location for saving the state file
|
save_pickled_state_file("tabs.state", state)
|
||||||
config_location = deluge.configmanager.get_config_dir()
|
|
||||||
|
|
||||||
try:
|
|
||||||
log.debug("Saving TorrentDetails state file: %s", filename)
|
|
||||||
state_file = open(os.path.join(config_location, filename), "wb")
|
|
||||||
cPickle.dump(state, state_file)
|
|
||||||
state_file.close()
|
|
||||||
except IOError, e:
|
|
||||||
log.warning("Unable to save state file: %s", e)
|
|
||||||
|
|
||||||
def load_state(self):
|
def load_state(self):
|
||||||
filename = "tabs.state"
|
return load_pickled_state_file("tabs.state")
|
||||||
# Get the config location for loading the state file
|
|
||||||
config_location = deluge.configmanager.get_config_dir()
|
|
||||||
state = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
log.debug("Loading TorrentDetails state file: %s", filename)
|
|
||||||
state_file = open(os.path.join(config_location, filename), "rb")
|
|
||||||
state = cPickle.load(state_file)
|
|
||||||
state_file.close()
|
|
||||||
except (EOFError, IOError, cPickle.UnpicklingError), e:
|
|
||||||
log.warning("Unable to load state file: %s", e)
|
|
||||||
|
|
||||||
return state
|
|
||||||
|
|
Loading…
Reference in New Issue