On 18 May 2002, I said: > Here's the interface I'm leaning towards at the moment: [...] > How's that grab everyone? Feels pretty right to me. I think I'll code > it up and see how hard it is to implement a persistent SessionManager. OK, I've implemented a simple persistent SessionManager (the SessionManager maintains a directory where it pickles each session to a separate file). I forgot two minor details: loading sessions from the persistent store, and removing them when they're no longer needed. Loading required a bit of work: I had to split the get_session() method of SessionManager up, so that subclasses can override this: def _lookup_session (self, session_id): """_lookup_session(session_id : string) -> Session Look for a session with ID 'session_id' and return it, or None if no such session. Subclasses that implement persistence will probably want to override this. """ return self.sessions.get(id) Removing sessions just meant adding an existing SessionManager method to the list of methods that are part of the session persistence interface: def expire_session (self, request): """expire_session(request : HTTPRequest) Expire the current session, ie. revoke the session cookie from the client and remove the session object from the session manager and from 'request'. """ All done and it seems to work. Still feels like the right interface to me. Here's my persistent SessionManager class: -- snip ---------------------------------------------------------------- class DemoSessionManager (SessionManager): def __init__ (self, session_klass=None, save_dir=None): SessionManager.__init__(self, session_klass=session_klass) self.set_save_dir(save_dir) def set_save_dir (self, save_dir): self.save_dir = save_dir if save_dir and not os.path.isdir(save_dir): os.mkdir(save_dir, 0700) def _lookup_session (self, session_id): if not self.sessions.has_key(session_id): filename = os.path.join(self.save_dir, session_id) if os.path.exists(filename): try: file = open(filename, "rb") print "loading session from %r" % file session = load(file) except IOError, err: sys.stderr.write("error reading session from %s: %s" % (filename, err)) else: self.sessions[session_id] = session return self.sessions.get(session_id) def save_changes (self, session): if session is None or session.is_empty(): return filename = os.path.join(self.save_dir, session.id) file = open(filename, "wb") print "saving session to %s" % file dump(session, file, 1) file.close() def expire_session (self, request): filename = os.path.join(self.save_dir, request.session.id) if os.path.exists(filename): os.remove(filename) SessionManager.expire_session(self, request) -- snip ---------------------------------------------------------------- Should probably do something to ensure that save_dir is set, and factor out the os.path.join() logic. Pretty simple though. Can anyone think of interface changes that would make this even easier? Can you think of how you would work your persistence mechanism into this interface so you can save your application's session data in your preferred way? If you see problems, say so now -- otherwise this will most likely be Quixote's session persistence interface for a good long while to come. Greg -- Greg Ward - software developer gward@mems-exchange.org MEMS Exchange http://www.mems-exchange.org