durusmail: quixote-users: Interface for session persistence
Interface for session persistence
2002-05-18
2002-05-18
2002-05-19
2002-05-20
2002-05-20
2002-05-20
2002-05-20
2002-05-20
2002-05-21
2002-05-24
2002-05-24
2002-05-24
2002-05-18
2002-05-20
2002-05-20
Interface for session persistence
Greg Ward
2002-05-18
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


reply