session.py :  » Web-Frameworks » Spyce » spyce-2.1 » modules » 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 » Spyce 
Spyce » spyce 2.1 » modules » session.py
#!/usr/bin/python
#
# Copyright (c) 2006 Conan C. Albrecht, Jonathan Ellis
#
# Permission is hereby granted, free of charge, to any person obtaining a copy 
# of this software and associated documentation files (the "Software"), to deal 
# in the Software without restriction, including without limitation the rights 
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
# copies of the Software, and to permit persons to whom the Software is furnished 
# to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all 
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
# FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
# DEALINGS IN THE SOFTWARE.

'''
Module Use
==========

Use the session by calling the get(), put(), and delete() methods. The
following example stores, uses, and then deletes the username:

In other words, it is very similar to a dictionary.  You can pretend the session
module is just a dictionary specific to the user accessing the page.  The session
"dictionary" contents will change automatically to match the user's values
for the browser accessing your page.

IMPORTANT NOTE 1:
You should only put small, temporary objects into the session.  While 
the session will take anything that can be pickled, remember that
it must be stored in a dbm hash.  Large objects will slow things down
considerably; avoid them.  Store strings, ints, and simple objects.

For example, if you want to store a User object, store the primary 
key (user id) to the User object.  On each request, reload the User
object from yourdatabaseusingtheuseridstoredthesession. import 

IMPORTANT NOTE 2:

This module adds a _lastaccess key to each session.  It uses this 
value to know when the session needs to be cleaned out.  Don't 
use this key name.  In other words, don't call 
session['_lastaccess'] = anything.

IMPORTANT NOTE 3:

If you are running Spyce in mod_python, cgi, or fastcgi installation
modes, you *should not* use the memory option.  Use the dbm method
for these installation modes.  While dbm is slower and takes disk
space, an in-memory cache will cause update errors since you'll
get multiple caches!  This is because mod_python, cgi, and  fastcgi
create multiple instances of your application -- you'll have an
cache in *each* instance.

In addition, dbm-method sessions survive a server reboot, memory-method
sessions do not.


The Internals
=============
Sessions are regular Python dictionaries.  The standard python 
"shelve" module is used to save these dictionaries to disk.
Since sessions are temporary and shouldn't be moved from installation import 
to installation, the module uses the highest possible pickle
protocol for speed.

Whenever a session is saved to a file, the last checked
timestamp is used to see if the file should be cleaned.
If enough time has passed, all old sessions are deleted
from theshelf. import 

The module cleans every CLEAN_INTERVAL seconds (set to every
24 hours right now).  If you want a different CLEAN_INTERVAL,
just change this constant at the top of the file.  I didn't make
this accessible publicly because I don't think most users
care how often it cleans sessions out.  Note that files are
not actually ever cleaned until something is set within them.

Sessions are never actually created until your program puts
data in them.  In other words, they are created just in time.
Since many site visitors may never get session data set, this
provides a great efficiency since no disk access is required
for them.  Your program will think they have a session, but
as long as you don't call put(), they won't get a session
internally.

The module is thread-safe.  A series of file locks is used 
to lock shelves when they are being used or cleaned.  However,
if multiple instances of Spyce are created, it is remotely
possible that sessions might blast each other.  I'm not 
sure how to solve this without additional disk access.  If anyone
wants to help here please do.  (note that the regular session
module has this problem, too.)
'''

import types, sys, shelve, pickle, os, anydbm
from UserDict import DictMixin
from spyceModule import spyceModule,moduleFinder
import spyce, spyceLock

########################################################
###   globals

# how often to check for and clean old sessions
CLEAN_INTERVAL = 60 * 60 * 2  # clean every 2 hours

########################################################
###   Session stores

class SessionNotFoundError(Exception): pass


class SessionStore:
  def __init__(self):
    self._last_cleaned = time.time()
  def load(self, sessionid, expires): 
    # when creating a new session object, its expiration should be set to "expires"
    raise NotImplementedError()
  def save(self, sessionid, session, expires): 
    raise NotImplementedError()
  def clear(self, sessionid): 
    raise NotImplementedError()
  def is_expired(self, sessionid):
    raise NotImplementedError()
  def list(self): 
    raise NotImplementedError()


memory_cache = {}
class MemoryStore(SessionStore):
  def load(self, sessionid, expires):
    # (python interpreter lock takes care of locking)
    return memory_cache.get(sessionid, {'_expires': expires})
  def save(self, sessionid, session): 
    session['_lastaccess'] = time.time()
    memory_cache[sessionid] = session
  def is_expired(self, sessionid):
    try:
      s = memory_cache[sessionid]
    except KeyError:
      raise SessionNotFoundError()
    return s['_lastaccess'] < time.time() - s['_expires']
  def clear(self, sessionid):
    try:
      del memory_cache[sessionid]
    except KeyError:
      raise SessionNotFoundError()
  def list(self): 
    return memory_cache.keys()
      

class DbmStore(SessionStore):
  def __init__(self, dir):
    SessionStore.__init__(self)
    self.dir = dir
  def _init(self):
    # can't perform these in __init__ b/c spyce's server object
    # doesn't exist yet when __init__ is called from spyceconf
    server = spyce.getServer()
    gen = lambda i: server.createLock('sessionlock%d' % i)
    self.lock = spyceLock.MultiLock(100, gen)
  def _filename(self, sessionid):
    return os.path.join(self.dir, 'spysession' + sessionid)
  def load(self, sessionid, expires):
    """
    (Conan's original session2 used a fixed, configurable, number of
    shelve files for all session objects.  This works poorly for highly
    concurrent access, though: shelve files serialize write access.
    So as aesthetically messy as it may be, one-file-per-session seems
    to be the way to go.)
    """
    if not hasattr(self, 'lock'): self._init()
    fname = self._filename(sessionid)
    self.lock.acquire(fname)
    try:
      session = shelve.open(fname, writeback=True, protocol=pickle.HIGHEST_PROTOCOL)
    finally:
      self.lock.release(fname)
    if '_expires' not in session:
      session['_expires'] = expires
    return session
  def save(self, sessionid, session):
    if not hasattr(self, 'lock'): self._init()
    fname = self._filename(sessionid)
    self.lock.acquire(fname)
    try:
      session.close()
    finally:
      self.lock.release(fname)
  def is_expired(self, sessionid):
    self.lock.acquire(fname)
    try:
      try:
        s = shelve.open(fname, flag='r')
      except anydbm.error, e:
        # "need 'c' or 'n' flag to open new db"
        if 'new db' in e.args[0]:
          raise SessionNotFoundError()
        raise
    finally:
      self.lock.release(fname)
    # (shelve updates mtime even when nothing changes)
    return os.path.getmtime(self._filename(sessionid)) < time.time() - s['_expires']
  def clear(self, sessionid): 
    try:
      os.unlink(self._filename(sessionid))
    except OSError, e:
      if e.args[0] == 2:
        # OSError: [Errno 2] No such file or directory: 'foo.bar'
        raise SessionNotFoundError()
      elif e.args[0] == 13:
        # OSError: [Errno 13] Permission denied: 'foo.sh'
        # usually means in-use by another thread/process
        pass
      else:
        raise
  def list(self):
    import glob
    n = len('spysession')
    return [fname[n:] for fname in
            glob.glob(os.path.join(self.dir, 'spysession*'))]


def clean_store(self):
  store = server.config.session_store
  for sessionid in store.list():
    try:
      if store.is_expired(sessionid):
        store.clear(sessionid)
    except SessionNotFoundError:
      pass

########################################################
###   The session module

class session(spyceModule, DictMixin):
  '''Manages the sessions of the web site in memory or a dbm (shelve) file.'''
  def start(self, *args, **kargs):
    self.mf = moduleFinder(self._api)
    server = spyce.getServer()
    self.store = server.config.session_store
    def get_option(name):
      if kargs.has_key(name):
        return kargs[name]
      return getattr(server.config, 'session_' + name)
    self.path = get_option('path')
    self.expire = get_option('expire')
    self.sessionid = self._find_sessionid()
    self.session = self.store.load(self.sessionid, self.expire)
    
  def finish(self, err):
    # refresh the cookie in the browser and restart expiration timer
    self._api.getModule('cookie').set('sessionid', self.sessionid, expire=self.expire, path=self.path)
    self.store.save(self.sessionid, self.session)
    
  ###################################################
  ###   Dictionary-like methods
    
  def __setitem__(self, key, value):
    self.session[key] = value
  def __delitem__(self, key):
        del self.session[key]
  def __getitem__(self, key):
    return self.session[key]
  def keys(self):
        return self.session.keys()
  def __contains__(self, key):
        return key in self.session
  def __iter__(self):
        return iter(self.session)
  def iteritems(self):
        return iter(self.session.iteritems())
    
  #####################################################
  ###   Private methods

  def _find_sessionid(self):
    return self._api.getModule('cookie').get('sessionid') or newtoken(self.mf)
    

############################################################################

seed = 'spycesessionseed'
import sha, time, random

def newtoken(mf):
    global seed
    s = sha.sha(seed)
    s.update(str(time.time()))
    s.update(str(random.random()))
    s.update(mf.request.getHeader('User-Agent') or '')
    s.update(mf.request.filename())
    h = s.hexdigest()
    seed = h[:10]
    return h[10:]
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.