dabo Commit
Revision 2199
Date: 2006-06-04 17:50:42 -0700 (Sun, 04 Jun 2006)
Author: ed
Changed:
U trunk/dabo/__init__.py
A trunk/dabo/dPref.py
Log:
Created the dPref class. This is an attempt to make the handling of preferences
much simpler and more direct. It is based on the concept of nested preferences,
so that by running code like:
pref = dabo.dPref(level="MyCoolApp")
pref.positions.MainWindow.Top = 33
will create several nested objects: pref, pref.positions, and
pref.positions.MainWindow. The value of 33 is automaticall persisted to a
SQLite database in the home dabo directory (typically
~/.dabo/DaboPreferences.db on POSIX systems). No explicit commit is needed,
unless the AutoPersist property is set to False. In that case, in order to
persist preference settings to the database, you need to call the preference
object's persist() method explicitly.
This is still very much a work-in-progress, and needs a lot of refinement
before it is ready to replace the current userSettings system.
Diff:
Modified: trunk/dabo/__init__.py
===================================================================
--- trunk/dabo/__init__.py 2006-06-05 00:42:32 UTC (rev 2198)
+++ trunk/dabo/__init__.py 2006-06-05 00:50:42 UTC (rev 2199)
@@ -107,6 +107,7 @@
errorLog.LogObject = sys.stderr
from dApp import dApp
+from dPref import dPref
from __version__ import version
import dColors
Added: trunk/dabo/dPref.py
===================================================================
--- trunk/dabo/dPref.py 2006-06-05 00:42:32 UTC (rev 2198)
+++ trunk/dabo/dPref.py 2006-06-05 00:50:42 UTC (rev 2199)
@@ -0,0 +1,335 @@
+import os
+import datetime
+import dabo
+from dabo.dLocalize import _
+import dabo.lib.utils as utils
+try:
+ from pysqlite2 import dbapi2 as sqlite
+except ImportError:
+ dabo.errorLog.write("This class requires SQLite")
+
+# We don't want to deal with these as preferences.
+regularAtts = ("_cache", "_parent", "_name", "_cursor", "_cxn", "_typeDict",
"AutoPersist",
+ "_autoPersist", "__methods__", "__basicsize__", "__members__",
"_getAttributeNames",
+ "__itemsize__", "__base__", "__flags__", "__subclasses__",
"__cmp__", "__bases__",
+ "__dictoffset__", "__call__", "__name__", "__mro__",
"__weakrefoffset__", "mro")
+
+
+
+class dPref(object):
+ """dPref is a class that is used to automatically manage preferences.
It requires
+ SQLite in order to function; without that installed, you cannot use
this class. It
+ automatically supports nesting of preferences; if you have a dPref
object named
+ 'basePref', and then issue the statement
'basePref.subPref.something=True', a
+ new dPref object named 'subPref' will be created, and can be referred
to using
+ 'basePref.subPref'.
+
+ Normally you should specify the initial level for your prefs. This will
ensure that
+ your preference names do not conflict with other dabo preferences. This
is much like
+ the approach to modules in the Python namespace. Failure to specify a
base
+ level puts all of your prefs into the 'root' namespace, where
collisions can more
+ easily happen.
+
+ All preference assignments are automatically persisted to the database
unless
+ the 'AutoPersist' propertyon this object or one of its 'ancestors' is
set to False.
+ When that is False, you must call the persist() method manually, or
your settings
+ will not be saved. Calling 'persist()' will write any values of that
object and all of its
+ child objects to the database.
+ """
+ def __init__(self, level=None, crs=None, cxn=None):
+ self._cache = {}
+ self._autoPersist = True
+ super(dPref, self).__init__()
+ self._parent = None
+ if level is None:
+ self._name = ""
+ else:
+ self._name = level
+ self._typeDict = {int: "int", float: "float", long: "long",
str: "str", unicode: "unicode",
+ bool: "bool", datetime.date: "date",
datetime.datetime: "datetime"}
+ if crs is None:
+ prefdir = utils.getUserDaboDirectory()
+ self._cxn = dabo.db.dConnection(connectInfo={"dbType":
"SQLite",
+ "database": os.path.join(prefdir,
"DaboPreferences.db")})
+ self._cursor = self._cxn.getDaboCursor()
+ # Make sure that the table exists
+ if not "daboprefs" in self._cursor.getTables():
+ self._cursor.execute("create table daboprefs
(ckey text not null, ctype text not null, cvalue text not null)")
+ else:
+ self._cursor = crs
+ self._cxn = cxn
+
+
+ def __getattr__(self, att):
+ if att in regularAtts:
+ if self.__dict__.has_key(att):
+ return self.__dict__[att]
+ else:
+ return None
+ if self.__dict__.has_key(att):
+ ret = self.__dict__[att]
+ elif self._cache.has_key(att):
+ ret = self._cache[att]
+ else:
+ # See if it's in the database
+ key = "%s.%s" % (self._getKey(), att)
+ crs = self._cursor
+ try:
+ crs.execute("select ctype, cvalue from
daboprefs where ckey = ? ", (key, ))
+ rec = crs.getCurrentRecord()
+ except StandardError, e:
+ print "QUERY ERR", e
+ rec = {}
+ if rec:
+ ret = self._decodeType(rec)
+ else:
+ ret = dPref(crs=self._cursor, cxn = self._cxn)
+ ret._parent = self
+ ret._name = att
+ self._cache[att] = ret
+ return ret
+
+
+ def __setattr__(self, att, val):
+ if att in regularAtts:
+ super(dPref, self).__setattr__(att, val)
+ #self.__dict__[att] = val
+ return
+ persist = False
+ try:
+ curr = self._cache[att]
+ exists = True
+ except KeyError:
+ exists = False
+ persist = self.AutoPersist
+ if exists:
+ persist = (curr != val) and self.AutoPersist
+ if persist:
+ self._persist(att, val)
+ self._cache[att] = val
+
+
+ def _getKey(self):
+ """The key is a concatenation of this object's name and the
names of its
+ ancestors, separated with periods.
+ """
+ ret = self._name
+ if not ret:
+ ret = "root"
+ else:
+ if self._parent is not None:
+ ret = ".".join((self._parent._getKey(), ret))
+ return ret
+
+
+ def _encodeType(self, val, typ):
+ """Takes various value types and converts them to a string
formats that
+ can be converted back when needed.
+ """
+ if typ in ("str", "unicode"):
+ ret = val
+ elif typ == "date":
+ ret = str((val.year, val.month, val.day))
+ elif typ == "datetime":
+ ret = str((val.year, val.month, val.day, val.hour,
val.minute, val.second, val.microsecond))
+ else:
+ ret = unicode(val)
+ return ret
+
+
+ def _decodeType(self, rec):
+ """Take a record containing a cvalue and ctype, and convert the
type
+ as needed.
+ """
+ val = rec["cvalue"]
+ typ = rec["ctype"]
+ if typ in ("str", "unicode"):
+ ret = val
+ elif typ == "int":
+ ret = int(val)
+ elif typ == "float":
+ ret = float(val)
+ elif typ == "long":
+ ret = long(val)
+ elif typ == "bool":
+ ret = (val == "True")
+ elif typ == "date":
+ ret = eval("datetime.date%s" % val)
+ elif typ == "datetime":
+ ret = eval("datetime.datetime%s" % val)
+ return ret
+
+
+ def _persist(self, att, val):
+ """Writes the value of the particular att to the database with
the proper key."""
+ key = "%s.%s" % (self._getKey(), att)
+ crs = self._cursor
+ try:
+ typ = self._typeDict[type(val)]
+ except:
+ print "BAD TYPE", type(val)
+ typ = "?"
+ # Convert it to a string that can be properly converted back
+ val = self._encodeType(val, typ)
+ try:
+ crs.execute("select ckey from daboprefs where ckey = ?
", (key, ))
+ res = crs.RowCount
+ except StandardError, e:
+ print "UPD ERR", e
+ res = -1
+ if res > 0:
+ sql = "update daboprefs set ctype = ?, cvalue = ? where
ckey = ? "
+ prm = (typ, val, key)
+ else:
+ sql = "insert into daboprefs (ckey, ctype, cvalue)
values (?, ?, ?)"
+ prm = (key, typ, val)
+ crs.execute(sql, prm)
+ crs.flush()
+
+
+ def persist(self):
+ """Manually save preferences to the database."""
+ for key, val in self._cache.items():
+ if isinstance(val, dPref):
+ # Child pref; tell it to persist itself
+ val.persist()
+ else:
+ self._persist(key, val)
+
+
+ def deletePref(self, att):
+ """Deletes a particular preference from both the database and
the cache."""
+ key = "%s.%s" % (self._getKey(), att)
+ crs = self._cursor
+ crs.execute("delete from daboprefs where ckey = ? ", (key, ))
+ if self._cache.has_key(att):
+ del self._cache[att]
+
+
+ def deleteAllPrefs(self):
+ """Deletes all preferences for this object, and all sub-prefs
as well."""
+ key = "%s.%%" % self._getKey()
+ crs = self._cursor
+ crs.execute("delete from daboprefs where ckey like ? ", (key, ))
+ for key, val in self._cache.items():
+ if isinstance(val, dPref):
+ # In case there are any other references to it
hanging around,
+ # clear its cache.
+ val.flushCache()
+ self._cache = {}
+
+
+ def flushCache(self):
+ """Clear the cache, forcing fresh reads from the database."""
+ for key, val in self._cache.items():
+ if isinstance(val, dPref):
+ val.flushCache()
+ else:
+ del self._cache[key]
+
+
+ def getPrefs(self, returnNested=False):
+ """Returns all the preferences set for this object. If
returnNested is True,
+ returns any sub-preferences too.
+ """
+ crs = self._cursor
+ key = self._getKey()
+ sql = "select * from daboprefs where ckey like ?"
+ crs.execute(sql, ("%s.%%" % key, ))
+ ds = crs.getDataSet()
+ if not returnNested:
+ # Filter out all the results that are not first-level
prefs
+ keylen = len(key)+1
+ ds = [rec for rec in ds
+ if len(rec["ckey"][keylen:].split("."))
== 1]
+ ret = {}
+ for rec in ds:
+ ret[rec["ckey"]] = self._decodeType(rec)
+ return ret
+
+
+ def getPrefKeys(self):
+ """Return a list of all preference keys for this level."""
+ crs = self._cursor
+ key = self._getKey()
+ keylen = len(key) + 1
+ keydots = len(key.split("."))
+ sql = "select ckey from daboprefs where ckey like ?"
+ crs.execute(sql, ("%s.%%" % key, ))
+ rs = crs.getDataSet()
+ ret = [rec["ckey"][keylen:] for rec in rs
+ if len(rec["ckey"].split(".")) == keydots+1]
+ # Now add any cached entries
+ addl = [kk for kk in self._cache.keys()
+ if kk not in ret and not isinstance(kk, dPref)]
+ ret += addl
+ return ret
+
+
+ def getSubPrefKeys(self):
+ """Return a list of all 'child' keys for this level."""
+ crs = self._cursor
+ key = self._getKey()
+ keylen = len(key) + 1
+ keydots = len(key.split("."))
+ sql = "select ckey from daboprefs where ckey like ?"
+ crs.execute(sql, ("%s.%%" % key, ))
+ rs = crs.getDataSet()
+ retList = [rec["ckey"].split(".")[1] for rec in rs
+ if len(rec["ckey"].split(".")) > 2]
+ tmp = {}
+ for itm in retList:
+ tmp[itm] = None
+ ret = tmp.keys()
+ return ret
+
+
+ # Property definitions start here
+ def _getAutoPersist(self):
+ ret = self._autoPersist
+ if ret and self._parent is not None:
+ # Make sure all parents are also auto-persist
+ ret = ret and self._parent.AutoPersist
+ return ret
+
+ def _setAutoPersist(self, val):
+ self._autoPersist = val
+
+
+ AutoPersist = property(_getAutoPersist, _setAutoPersist, None,
+ _("Do property assignments automatically save
themselves? Default=True (bool)"))
+
+
+
+
+if __name__ == "__main__":
+ a = dPref(level="TESTING")
+ a.testValue = "First Level"
+ a.anotherValue = "Another First"
+ a.b.testValue = "Second Level"
+ a.b.anotherValue = "Another Second"
+
+ print "Just 'a' prefs:"
+ print a.getPrefs()
+ print
+ print "'a' prefs and all sub-prefs:"
+ print a.getPrefs(True)
+
+
+ zz=a.getSubPrefKeys()
+ print "SUB PREFS", zz
+
+ a.AutoPersist = False
+ a.b.shouldntStay = "XXXXXXXXXX"
+
+ print "BEFORE FLUSH", a.b.getPrefKeys()
+ a.flushCache()
+ print "AFTER FLUSH", a.b.getPrefKeys()
+
+ print "DELETE ONE"
+ a.deletePref("anotherValue")
+ print a.getPrefs(True)
+ print "DELETE ALL"
+ a.deleteAllPrefs()
+ print a.getPrefs(True)
+
Property changes on: trunk/dabo/dPref.py
___________________________________________________________________
Name: svn:eol-style
+ native
_______________________________________________
Post Messages to: [email protected]
Subscription Maintenance: http://leafe.com/mailman/listinfo/dabo-dev