store profiles as blobs

the cPickle data may not be valid utf8, and even with
text_factory=str this seems to cause problems sometimes
This commit is contained in:
Damien Elmes 2016-05-12 14:19:16 +10:00
parent 47521cff9a
commit 89a5777585
2 changed files with 20 additions and 23 deletions

View file

@ -17,13 +17,11 @@ except ImportError:
Error = sqlite.Error Error = sqlite.Error
class DB(object): class DB(object):
def __init__(self, path, text=None, timeout=0): def __init__(self, path, timeout=0):
encpath = path encpath = path
if isinstance(encpath, unicode): if isinstance(encpath, unicode):
encpath = path.encode("utf-8") encpath = path.encode("utf-8")
self._db = sqlite.connect(encpath, timeout=timeout) self._db = sqlite.connect(encpath, timeout=timeout)
if text:
self._db.text_factory = text
self._path = path self._path = path
self.echo = os.environ.get("DBECHO") self.echo = os.environ.get("DBECHO")
self.mod = False self.mod = False

View file

@ -74,6 +74,9 @@ class ProfileManager(object):
self.firstRun = self._loadMeta() self.firstRun = self._loadMeta()
# did the user request a profile to start up with? # did the user request a profile to start up with?
if profile: if profile:
if profile not in self.profiles():
QMessageBox.critical(None, "Error", "Requested profile does not exist.")
sys.exit(1)
try: try:
self.load(profile) self.load(profile)
except TypeError: except TypeError:
@ -99,15 +102,13 @@ a flash drive.""" % self.base)
###################################################################### ######################################################################
def profiles(self): def profiles(self):
return sorted( return sorted(x for x in
unicode(x, "utf8") for x in
self.db.list("select name from profiles") self.db.list("select name from profiles")
if x != "_global") if x != "_global")
def load(self, name, passwd=None): def load(self, name, passwd=None):
prof = cPickle.loads( data = self.db.scalar("select cast(data as blob) from profiles where name = ?", name)
self.db.scalar("select data from profiles where name = ?", prof = cPickle.loads(str(data))
name.encode("utf8")))
if prof['key'] and prof['key'] != self._pwhash(passwd): if prof['key'] and prof['key'] != self._pwhash(passwd):
self.name = None self.name = None
return False return False
@ -118,23 +119,21 @@ a flash drive.""" % self.base)
def save(self): def save(self):
sql = "update profiles set data = ? where name = ?" sql = "update profiles set data = ? where name = ?"
self.db.execute(sql, cPickle.dumps(self.profile), self.db.execute(sql, buffer(cPickle.dumps(self.profile)), self.name)
self.name.encode("utf8")) self.db.execute(sql, buffer(cPickle.dumps(self.meta)), "_global")
self.db.execute(sql, cPickle.dumps(self.meta), "_global")
self.db.commit() self.db.commit()
def create(self, name): def create(self, name):
prof = profileConf.copy() prof = profileConf.copy()
self.db.execute("insert into profiles values (?, ?)", self.db.execute("insert into profiles values (?, ?)",
name.encode("utf8"), cPickle.dumps(prof)) name, buffer(cPickle.dumps(prof)))
self.db.commit() self.db.commit()
def remove(self, name): def remove(self, name):
p = self.profileFolder() p = self.profileFolder()
if os.path.exists(p): if os.path.exists(p):
send2trash(p) send2trash(p)
self.db.execute("delete from profiles where name = ?", self.db.execute("delete from profiles where name = ?", name)
name.encode("utf8"))
self.db.commit() self.db.commit()
def rename(self, name): def rename(self, name):
@ -163,7 +162,7 @@ a flash drive.""" % self.base)
# update name # update name
self.db.execute("update profiles set name = ? where name = ?", self.db.execute("update profiles set name = ? where name = ?",
name.encode("utf8"), oldName.encode("utf-8")) name, oldName)
# rename folder # rename folder
try: try:
os.rename(oldFolder, newFolder) os.rename(oldFolder, newFolder)
@ -253,7 +252,7 @@ and no other programs are accessing your profile folders, then try again."""))
Anki's prefs.db file was corrupt and has been recreated. If you were using multiple \ Anki's prefs.db file was corrupt and has been recreated. If you were using multiple \
profiles, please add them back using the same names to recover your cards.""") profiles, please add them back using the same names to recover your cards.""")
try: try:
self.db = DB(path, text=str) self.db = DB(path)
self.db.execute(""" self.db.execute("""
create table if not exists profiles create table if not exists profiles
(name text primary key, data text not null);""") (name text primary key, data text not null);""")
@ -263,9 +262,9 @@ create table if not exists profiles
if not new: if not new:
# load previously created # load previously created
try: try:
self.meta = cPickle.loads( data = self.db.scalar(
self.db.scalar( "select cast(data as blob) from profiles where name = '_global'")
"select data from profiles where name = '_global'")) self.meta = cPickle.loads(str(data))
return return
except: except:
recover() recover()
@ -273,7 +272,7 @@ create table if not exists profiles
# create a default global profile # create a default global profile
self.meta = metaConf.copy() self.meta = metaConf.copy()
self.db.execute("insert or replace into profiles values ('_global', ?)", self.db.execute("insert or replace into profiles values ('_global', ?)",
cPickle.dumps(metaConf)) buffer(cPickle.dumps(metaConf)))
self._setDefaultLang() self._setDefaultLang()
return True return True
@ -351,6 +350,6 @@ please see:
def setLang(self, code): def setLang(self, code):
self.meta['defaultLang'] = code self.meta['defaultLang'] = code
sql = "update profiles set data = ? where name = ?" sql = "update profiles set data = ? where name = ?"
self.db.execute(sql, cPickle.dumps(self.meta), "_global") self.db.execute(sql, buffer(cPickle.dumps(self.meta)), "_global")
self.db.commit() self.db.commit()
anki.lang.setLang(code, local=False) anki.lang.setLang(code, local=False)