saving state in Python/CGI
Peter van Kampen
news at datatailors.com
Fri Sep 20 17:00:09 EDT 2002
In article <mailman.1032544767.23458.python-list at python.org>, Frank Gibbons
wrote:
> Hi,
>
> I've been using Python (and Jython) for about 5 months now. So far, so
> great. I'm building a CGI app that has about 5 different stages, and I need
> to maintain state between them. Specifically, it's a scientific tool that
> requires the user to upload a (possibly quite large) datafile, processes
> it, does some computation, then hands it back (formatted) with results.
>
> I need access to the data on each page, yet don't want to pass it as a
> hidden field, because of its potential size. In Perl, I could just use the
> save() method from CGI.pm to save the form on the server side, pass a
> session key in a hidden field, then retrieve the data using the key. For
> the life of me, I can't find an equivalent way in Python. cgiFormStorage
> looks like a dictionary, but it's immutable, so you can't retrieve data
> from disk and add it to the form. cgi.py appears to have no analog to
> CGI.pm's save() method, at least not that I've been able to find. I've
> tried pickling the form, but that doesn't work either (it saves something
> to disk alright, but not the large data retrieved from a multi-part form
> file upload).
>
> Perhaps I've missed something. Maybe that's just not how you do things in
> Python. Can anyone help?
>
> Thanks,
>
> -Frank Gibbons
>
Hi Frank,
Perhaps you can use the code below. It's my first little python
project so I'm mainly posting this for some feedback. It's a simpler
version (at least to my mind) of something I found in the vaults.
It creates a dict to store sessionvars in and pickles it to the hd. You
have to setup a dir where the webserver account has write (and read)
access. It defaults to .session/ in the current dir.
It uses cookies to pass the sessionid that's why you have be careful to
write the headers that the class generates. It uses apache's
mod_unique_id if it is enabled (and you're using apache ;-) I haven't
tested yet but it should work in any cgi-environment) which provides
nicer cookie-id's than tempfiles which is used as a fallback but I
haven't checked if tempfilenames are safe sessionids.
You can use it like this.
#First page
import CookieSession
s = CookieSession.CookieSession()
s["whatever"] = "you want (if it can be pickled)"
s.save()
print "Content-type: text/html"
print s.headers()
print
print YourHTML
#Another page
import CookieSession
s = CookieSession.CookieSession()
restore_from_session = s["whatever"]
As I said. Comments are very welcome.
PterK
#! /usr/bin/env python
# CookieSession.py
#
# Based on stuff i found at http://wingide.com/opensource/httpsession
# which in turn I located in the Vaults of Parnassus :
# http://py.vaults.ca/parnassus/
import os
import time
import stat
import glob
import Cookie
import cPickle
import tempfile
#python2.2 version
#class CookieSession(dict):
class CookieSession:
"""
TODO: Write docstring
"""
#-----------------------------------------------------------------------
def __init__(self, timeout = 20, \
store = ".sessions"):
""" Init the class.
1. Set timeout
2. Set where to store the pickles
3. Clear up old sessions
4. Find out if a session is active
5. Yes? Load it and 'touch' it
6. No? Create a new one, set some attributes and save it to disk.
"""
self.__timeout = timeout * 60
self.__session_store = store
self.__cookies = "Cache-control: no-cache\r\n"
self.__on_expire_is_set = 0
# expire old sessions
self.__ClearOldSessions()
# Get the current session or create a new one
try:
self.__sessionid = Cookie.SimpleCookie(os.environ["HTTP_COOKIE"]) \
["sessionid"].value
#touch session (from an effbot-posting)
now = time.time()
os.utime(self.__SessionFilename(), (now, now))
#load session
self.__SessionDict = self.__LoadSession()
except:
# Create a new session
self.__sessionid = self.__CreateSessionid()
self.__CreateCookie()
self.__SessionDict = {}
self["sessionid"] = self.__sessionid
self.save()
#-----------------------------------------------------------------------
def expire(self):
"Forces the current Session to expire"
self.__on_expire()
#-----------------------------------------------------------------------
def headers(self):
"Creates all headers for the calling page"
return self.__cookies
#-----------------------------------------------------------------------
def save(self):
"Saves the sessionDict to a pickled file"
cPickle.dump(self.__SessionDict, self.__OpenSessionFile("w"))
#-----------------------------------------------------------------------
def __setattr__(self, name, value):
"Used to check whether on_expire is set. Maybe a method is better?)"
self.__dict__[name] = value
if name == "on_expire":
self.__on_expire_is_set = 1
#-----------------------------------------------------------------------
def __on_expire(self, sessionid = None):
""" Calls a function that can be assigned to the attribute on_expire
similar to ASP's On_Session_End.
It passes the session about to expire.
def OnExpire(expired_session):
if not expired_session["TransactionComplete"]:
#Remove cruft from database
pass
s = CookieSession()
s.on_expire = OnExpire
...
s.expire()
"""
if sessionid is None: sessionid = self.__sessionid
try:
# Test if it is set to prevent unnecessary loading of pickles
if self.__on_expire_is_set:
self.on_expire(self.__LoadSession(sessionid))
finally:
os.remove(self.__SessionFilename(sessionid))
#-----------------------------------------------------------------------
def __CreateSessionid(self):
"Create a sessionid from the apache unique_id module or a tempfile."
try:
id = os.environ["UNIQUE_ID"]
except:
id = os.path.basename(tempfile.mktemp())
return id
#-----------------------------------------------------------------------
def __CreateCookie(self):
"Create a cookie to store the sessionid on the clientside"
C = Cookie.SimpleCookie()
C["sessionid"] = self.__sessionid
C["sessionid"]["path"] = "/"
self.__cookies += C.output()
#-----------------------------------------------------------------------
def __SessionFilename(self, sessionid = None):
if sessionid is None: sessionid = self.__sessionid
return self.__session_store + "/.session" + str(sessionid)
#-----------------------------------------------------------------------
def __OpenSessionFile(self, mode, sessionid = None):
"Open up the on-disk session persistence file"
if sessionid is None: sessionid = self.__sessionid
f = open(self.__SessionFilename(sessionid), mode)
return f
#-----------------------------------------------------------------------
def __LoadSession(self, sessionid = None):
"Loads a session from a pickled file"
if sessionid is None: sessionid = self.__sessionid
return cPickle.load(self.__OpenSessionFile("r", sessionid))
#-----------------------------------------------------------------------
def __ClearOldSessions(self):
"Remove any expired session files"
files = glob.glob(os.path.join(self.__session_store, '.session*'))
for file in files:
filetime = os.stat(file)[stat.ST_MTIME]
currtime = time.time()
if filetime + self.__timeout < currtime:
# len(".session") = 8
sessionid = file[len(self.__session_store)+9:]
self.__on_expire(sessionid)
#-----------------------------------------------------------------------
# In 2.2 this works :
# class CookieSession(dict):
#
# Python map emulation : This (below) can be dropped for python2.2
#-----------------------------------------------------------------------
def __len__(self):
return len(self.__SessionDict)
#-----------------------------------------------------------------------
def __getitem__(self, key):
retval = self.__SessionDict.get(key)
if retval == None:
retval = ""
return retval
#-----------------------------------------------------------------------
def __setitem__(self, key, value):
self.__SessionDict[key] = value
#-----------------------------------------------------------------------
def __delitem__(self, key):
del self.__SessionDict[key]
#-----------------------------------------------------------------------
def keys(self):
return self.__SessionDict.keys()
#-----------------------------------------------------------------------
def values(self):
return self.__SessionDict.values()
#-----------------------------------------------------------------------
def items(self):
return self.__SessionDict.items()
#-----------------------------------------------------------------------
def has_key(self, key):
return self.__SessionDict.has_key(key)
#-----------------------------------------------------------------------
def get(self, key, f=None):
return self.__SessionDict.get(key, f)
#-----------------------------------------------------------------------
def clear():
return self.__SessionDict.clear()
#-----------------------------------------------------------------------
def copy():
return self.__SessionDict.copy()
#-----------------------------------------------------------------------
def update(b):
return self.__SessionDict.update(b)
More information about the Python-list
mailing list