SessionContainer.py :  » Web-Frameworks » Aquarium » aquarium-2.3 » aquarium » session » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Frameworks » Aquarium 
Aquarium » aquarium 2.3 » aquarium » session » SessionContainer.py
"""This module contains the base classes SessionContainer and Session."""

__docformat__ = "restructuredtext"

# Created: Tue Mar 23 13:09:56 PST 2004
# Author: Shannon -jj Behrens
# Email: jjinux@users.sourceforge.net
#
# Copyright (c) Shannon -jj Behrens.  All rights reserved.

from random import choice
import time

import aquarium.conf.AquariumProperties as properties


try:
    from threading import Lock
except ImportError:
    class Lock:
        """Python was built without thread support.  I'll use a fake Lock.

        My assumption is that a subclass is going to create its own Lock.  I
        wish I could just create a Lock factory in ``SessionContainer``, but
        the Lock must be initialized statically.  This is the least ugly HACK I
        could think of.

        """
        def acquire(self): pass
        def release(self): pass


class SessionContainer:

    """This is a container for sessions.

    A container class is needed because:

    * We need to be able to call certain methods such as ``cleanup`` without
      actually creating a ``Session`` instance.  Hence, these methods obviously
      can't be instance methods of the ``Session`` class.
    * I want subclasses to be able to override some of these methods.

    On the other hand, no instance state is kept, so one instance of this class
    is just as good as another.  All state is kept in the class.  I'd use a
    singleton, but the version of Python I'm stuck with doesn't have class
    methods.

    Concerning locking:  in general, a global lock (of some sort) should be
    used so that creating, deleting, reading, and writing sessions is
    serialized.  However, it is not necessary to have a lock for each session.
    If a user wishes to use two browser windows at the same time, the last
    writer wins.

    Concerning aquarium.util.AquariumClass_:  this class does not subclass
    aquarium.util.AquariumClass and does not require a ``ctx`` parameter in its
    constructor.  Subclasses may choose to mixin aquarium.util.AquariumClass
    as necessary, but they will then require a ``ctx`` in their constructors.
    aquarium.util.Aquarium_ is aware of this.

    This base class is useful for mutlithreaded environments.  For other
    environments, such as CGI, it simply provides an API.

    The following class level constants are defined:

    SID_LENGTH
      This is the length of standard sid's.

    These are needed since not all "mutexes" provide the same API,
    unfortunately:

    _acquire
      Acquire the global lock.
    _release
      Release the global lock.

    The following protected class variables are used:

    _lock
      This is the global lock.  It is initialized statically.  (This base class
      uses ``threading.Lock``.  Subclasses can set this to something different
      if appropriate.)
    _sessions
      This is the global dictionary of sessions.

    .. _aquarium.util.AquariumClass:
       aquarium.util.AquariumClass.AquariumClass-class.html
    .. _aquarium.util.Aquarium:
        aquarium.util.Aquarium.Aquarium-class.html

    """

    SID_LENGTH = 20

    _lock = Lock()
    _sessions = {}

    def open(self, sid=None):
        """Create or open a session.

        sid
          If this is None, the default, or if no such session exists, a
          new sid will be generated (even if you provided an sid) and a new
          session will be created with that sid.

        Return the session.

        """
        self._acquire()
        try:
            if not sid or not self._exists(sid):
                tries = 10
                while 1:
                    sid = self._createSid()
                    if not self._exists(sid):
                        break
                    tries -= 1
                    if tries <= 0:
                        raise OverflowError("Could not create an unused sid")
                self._sessions[sid] = self._createSession(sid)
            return self._sessions[sid]
        finally:
            self._release()

    def cleanup(self):
        """Delete all of the expired sessions.

        It's the application's responsibility to occasionally call this.

        """
        self._acquire()
        try:
            unlinkThese = [         # Don't modify the dict during iteration.
                sid
                for sid in self._sessions
                    if self._isExpired(sid)
            ]
            map(self._unlink, unlinkThese)
        finally:
            self._release()

    def adjustTime(self, deltaSeconds):
        """Adjust all of the ``lastModified`` keys.

        If you have to change the time, do this::

            sessionContainer._acquire()
            try:
                changeTime()
                sessionContainer.adjustTime(deltaSeconds)
            finally:
                sessionContainer._release()

        You have to manage the lock yourself, otherwise changing the time might
        cause some sessions to get deleted before we can update them.

        """
        for sid in self._sessions.keys():
            self._sessions[sid]["lastModified"] += deltaSeconds

    def _createSid(self):
        """Generate a new sid.

        It is not guaranteed to be unique.

        """
        pool = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890"
        return "".join([choice(pool) for _unused in range(self.SID_LENGTH)])

    def _createSession(self, sid):
        """This is a session factory method.

        It's here so that you can override what type of session gets
        created without too much trouble.  You must acquire the lock before
        calling this.

        """
        return Session(sid)

    def _exists(self, sid):
        """Does a session with the given sid exist?

        This implies that it is not expired.  You must acquire the lock before
        calling this.

        """
        return self._sessions.has_key(sid) and not self._isExpired(sid)

    def _isExpired(self, sid):
        """Is the session with the given sid expired?

        If the session doesn't exist, this will raise a ``KeyError``.  You must
        acquire the lock before calling this.

        """
        return (self._sessions[sid]["lastModified"] <
                time.time() - properties.MAXIMUM_SESSION_LIFETIME)

    def _unlink(self, sid):
        """Delete a session permanently.

        You must acquire the lock before calling this.

        """
        del self._sessions[sid]

    def _acquire(self):
        """Acquire the global lock."""
        self._lock.acquire()

    def _release(self):
        """Release the global lock."""
        self._lock.release()


class Session(dict):

    """This is a dict-like object for session management.

    The following keys are used:

    sid
      This is the session's ID.

    lastModified
      This is the last time the session was modified.

    """

    def __init__(self, sid):
        """Create or open a session.

        Don't call this directly--use ``SessionContainer.open``.

        """
        dict.__init__(self)
        self["sid"] = sid
        self["lastModified"] = time.time()

    def save(self):
        """Persist a session.

        This method may be extended.  This base class takes care of updating
        the ``lastModified`` key.

        """
        self["lastModified"] = time.time()

    def clear(self):
        """Clear everything except the ``sid`` and ``lastModified`` keys."""
        sid = self["sid"]
        dict.clear(self)
        self["sid"] = sid
        self["lastModified"] = time.time()

    def getCookieExpiration(self):
        """Return a value for ``ctx.response.cookie["sid"]["expires"]``.

        If ``properties.SET_COOKIE_EXPIRATION`` is 0 or not set, just return 0.
        Otherwise, return something based on
        ``properties.MAXIMUM_SESSION_LIFETIME``.

        """
        if not getattr(properties, "SET_COOKIE_EXPIRATION", 0):
            return 0
        else:
            expires = time.gmtime(time.time() +
                                  properties.MAXIMUM_SESSION_LIFETIME)
            return time.strftime("%A, %d-%b-%Y %T GMT", expires)


def getCookieExpirationNever():
    """Return this in ``Session.getCookieExpiration`` for a long-lasting cookie.

    Sometimes you don't want the cookie to go away when the user closes his
    browser.  Setting a fixed time limit is bad if your software allows the
    user to change the server's time.  Neither ``expires`` nor ``Max-Age``
    works for this case.  Hence, I'll just return something 20 years from now.
    I see that Google, PayPal, and MSN are doing something similar.  If the
    user sets his time wrong by more than 20 years, he'll have to change his
    browser's time too.

    """
    LONG_TIME = 60 * 60 * 24 * 365 * 20
    expires = time.gmtime(time.time() + LONG_TIME)
    return time.strftime("%A, %d-%b-%Y %T GMT", expires)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.