Mercurial > p > roundup > code
diff roundup/backends/sessions_dbm.py @ 2082:c091cacdc505
Finished implementation of session and one-time-key stores for RDBMS backends.
Refactored the API of sessions and their interaction with the
backend database a fair bit too.
Added some session tests. Nothing testing ageing yet, 'cos that's a pain
inna ass to test :)
Note: metakit backend still uses the *dbm implementation. It might
want to implement its own session store some day, as it'll be faster than
the *dbm one.
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Thu, 18 Mar 2004 01:58:46 +0000 |
| parents | |
| children | 93f03c6714d8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/roundup/backends/sessions_dbm.py Thu Mar 18 01:58:46 2004 +0000 @@ -0,0 +1,141 @@ +#$Id: sessions_dbm.py,v 1.1 2004-03-18 01:58:45 richard Exp $ +"""This module defines a very basic store that's used by the CGI interface +to store session and one-time-key information. + +Yes, it's called "sessions" - because originally it only defined a session +class. It's now also used for One Time Key handling too. +""" +__docformat__ = 'restructuredtext' + +import anydbm, whichdb, os, marshal, time + +class BasicDatabase: + ''' Provide a nice encapsulation of an anydbm store. + + Keys are id strings, values are automatically marshalled data. + ''' + _db_type = None + + def __init__(self, db): + self.config = db.config + self.dir = db.config.DATABASE + # ensure files are group readable and writable + os.umask(0002) + + def clear(self): + path = os.path.join(self.dir, self.name) + if os.path.exists(path): + os.remove(path) + elif os.path.exists(path+'.db'): # dbm appends .db + os.remove(path+'.db') + + def cache_db_type(self, path): + ''' determine which DB wrote the class file, and cache it as an + attribute of __class__ (to allow for subclassed DBs to be + different sorts) + ''' + db_type = '' + if os.path.exists(path): + db_type = whichdb.whichdb(path) + if not db_type: + raise hyperdb.DatabaseError, "Couldn't identify database type" + elif os.path.exists(path+'.db'): + # if the path ends in '.db', it's a dbm database, whether + # anydbm says it's dbhash or not! + db_type = 'dbm' + self.__class__._db_type = db_type + + _marker = [] + def get(self, infoid, value, default=_marker): + db = self.opendb('c') + try: + if db.has_key(infoid): + values = marshal.loads(db[infoid]) + else: + if default != self._marker: + return default + raise KeyError, 'No such %s "%s"'%(self.name, infoid) + return values.get(value, None) + finally: + db.close() + + def getall(self, infoid): + db = self.opendb('c') + try: + try: + return marshal.loads(db[infoid]) + except KeyError: + raise KeyError, 'No such %s "%s"'%(self.name, infoid) + finally: + db.close() + + def set(self, infoid, **newvalues): + db = self.opendb('c') + try: + if db.has_key(infoid): + values = marshal.loads(db[infoid]) + else: + values = {'__timestamp': time.time()} + values.update(newvalues) + db[infoid] = marshal.dumps(values) + finally: + db.close() + + def list(self): + db = self.opendb('r') + try: + return db.keys() + finally: + db.close() + + def destroy(self, infoid): + db = self.opendb('c') + try: + if db.has_key(infoid): + del db[infoid] + finally: + db.close() + + def opendb(self, mode): + '''Low-level database opener that gets around anydbm/dbm + eccentricities. + ''' + # figure the class db type + path = os.path.join(os.getcwd(), self.dir, self.name) + if self._db_type is None: + self.cache_db_type(path) + + db_type = self._db_type + + # new database? let anydbm pick the best dbm + if not db_type: + return anydbm.open(path, 'c') + + # open the database with the correct module + dbm = __import__(db_type) + return dbm.open(path, mode) + + def commit(self): + pass + + def close(self): + pass + + def updateTimestamp(self, sessid): + self.set(sessid, __timestamp=time.time()) + + def clean(self, now): + """Age sessions, remove when they haven't been used for a week. + """ + week = 60*60*24*7 + for sessid in self.list(): + interval = now - self.get(sessid, '__timestamp') + if interval > week: + self.destroy(sessid) + +class Sessions(BasicDatabase): + name = 'sessions' + +class OneTimeKeys(BasicDatabase): + name = 'otks' +
