durusmail: quixote-users: Re: sessions and mod_python (Solution, LONG)
sessions and mod_python
Re: sessions and mod_python
2003-08-13
Re: sessions and mod_python (Solution, LONG)
2003-08-15
Re: sessions and mod_python (Solution, LONG)
VanL
2003-08-15
In case anyone was wondering, I solved this problem.  The problem was
that my database mapper imported the psycopg module in code running
under mod_python -- which caused an immediate segfault of the apache
child process, thus leading to no output.  When I made the changes to
use pyGreSQL, the code I posted previously worked.

For the archives, this is what I used.  This is snipped from my code, no
guarantees it will work for anyone else:


In the database interface (creates and executes SQL statements):
====from db.py====================================
     #####
     #
     # Session Management Methods
     #
     #####

     def create_session(self, key, value):
         """
         Insert a new row into web_session with
         session_id  and session_data .

         Arguments are:
             key
                 (string representing the session_id)
             value
                 (string representing the session_data)

         Does not return a result.

         Note that this method does no conversion on the arguments
         except a SQL cast() as text.
         """

         insert = {'session_id': key,
                   'session_data': value,
                   'session_time': 'now()',
                   'session_creation_time': 'now()',
                  }
         # Sessions must be unique.  Double-check uniqueness.
         if self.has_session(key):
             self.save_session(key, value)
             return
         else:
             table_schema = getattr(schema, 'web_session')
             sql, values = self._make_insert('web_session',
                                              key, table_schema,
                                              insert, 'session_id')
             self._execute_action_noresult(sql, values)


     def has_session(self, key):
         """
         Return a boolean (1,0) result indicating whether or not a
         session with session_id  exists.

         Argument: key (string representing session_id)

         Returns boolean.

         Note that this method does no conversion on the arguments
         except a SQL cast() as text.
         """

         return self.execute("""
             select exists(select * from web_session
             where session_id = %(key)s)""", {'key': key}).fetchone()[0]


     def get_session(self, key):
         """
         Get the session identified by session_id .

         Argument: key (string representing session_id)

         Returns db_row{session_id, session_data,
                       session_time, session_creation_time}

         Note that this method does no conversion on the arguments
         except a SQL cast() as text.
         """
         cursor = self.execute("""
             select * from web_session
             where session_id = %(key)s""",
             {'key': key})

         # Make a class to store the resulting row, to allow attr access
         R = db_row.IMetaRow(cursor.description)
         session = cursor.fetchone()
         if not session: return None
         else: return R(session)


     def save_session(self, key, newvalue):
         """
         Save  as the session_data in the session identified
         by session_id .  Also update the session_time.

         Arguments are:
             key
                 (string representing the session_id)
             newvalue
                 (string representing the session_data)

         Returns the number of rows affected (presumably 1).

         Note that this method does no conversion on the arguments
         except a SQL cast() as text.
         """

         update = {'session_id': key,
                   'session_data': newvalue,
                   'session_time': 'now()',
                  }
         table_schema = getattr(schema, 'web_session')
         additional_where = """
             session_data != %(session_data)s
                            """
         sql, values = self._make_update('web_session',
                                         key, table_schema,
                                         update, 'session_id',
                                         additional_where)
         return self._execute_action_noresult(sql, values)


     def delete_session(self, key):
         """
         Delete the session referred to by session_id .

         Argument: key (string representing session_id)

         Does not return a value.

         Note that this method does no conversion on the arguments
         except a SQL cast() as text.
         """

         self.execute("""
             delete from web_session
             where session_id = %(key)s""", {'key': key})
====================================================

On top of this I created a mapping interface, and appropriate
Sessions and SessionManagers:

==web_sessions.py===================================

try: import cPickle as pickle
except ImportError: import pickle
import quixote.session
from web import shared


class db_session_mapper:
     def __init__(self):
         self.db = shared.db

     def __serialize(self, obj):
         return pickle.dumps(obj)

     def __deserialize(self, objstr):
         return pickle.loads(objstr)

     def commit(self):
         self.db.commit()

     def abort(self):
         self.db.abort()

     def __contains__(self, key):
         return self.db.has_session('%s' % key)

     def has_key(self, key):
         return self.db.has_session('%s' % key)

     def __getitem__(self, key):
         sessiondata = self.__deserialize(
             self.db.get_session('%s' % key)['session_data'])
         return sessiondata

     def __setitem__(self, key, value):
         newvalue = self.__serialize(value)
         success = self.db.save_session('%s' % key, newvalue)
         if not success:
             self.db.create_session('%s' % key, newvalue)

     def __delitem__(self, key):
         self.db.delete_session('%s' % key)

     def keys(self):
         return [(res['session_id'])
                    for res in self.db.get_sessions()]

     def values(self):
         return [self.__deserialize(res['session_data'])
                    for res in self.db.get_sessions()]

     def items(self):
         return [((res['session_id']),
                  self.__deserialize(res['session_data']))
                    for res in self.db.get_sessions()]

     def update(self, updates_dict):
         for k in updates_dict.keys():
             self.__setitem__(k, updates_dict[k])

     def get(self, key, default=None):
         try:
             value = self.__getitem__(key)
             return value
         except KeyError: return default


class SessionManager(quixote.session.SessionManager):
     def abort_changes(self, session):
         self.sessions.abort()

     def commit_changes(self, session):
         self.sessions.commit()


class Session(quixote.session.Session):
     def __init__(self, request, id):
         quixote.session.Session.__init__(self, request, id)
         self.data = {}
         self.real_user = None

     def set_user(self, user):
         if self.real_user:
             db = shared.db
             user_info = db.get_ellis_user(self.real_user)
             if user_info['team_member']:
                 self.user = user
             else:
                 self.user = user
                 self.real_user = user
         else:
             self.user = user
             self.real_user = user
         self.data._dirty = 1

     def has_info(self):
         return 1

     def is_dirty(self):
         # The SQL statement generated assures that only
         # changes are saved to the database.  Because the
         # issue is taken care of there, just alwasy save here
         return 1
================================================

Finally, I created a new mod_python handler:

====mod_python_session_handler.py===============
import os, sys
from mod_python import apache
from quixote import enable_ptl
from quixote.publish import SessionPublisher
from web.sessions import web_sessions

sessions = web_sessions.db_session_mapper()
web_session_mgr = web_sessions.SessionManager(
                        session_class=web_sessions.Session,
                        session_mapping=sessions)

class ModPythonSessionPublisher(SessionPublisher):
     def publish_modpython(self, req):
         """publish_modpython() -> None

         Entry point from mod_python.
         """
         self.publish(apache.CGIStdin(req),
                      apache.CGIStdout(req),
                      sys.stderr,
                      apache.build_cgi_env(req))
         return apache.OK

enable_ptl()

name2publisher = {}

def handler(req):
     opts = req.get_options()
     try:
         package = opts['quixote-root-namespace']
     except KeyError:
         package = None

     if not package:
         return apache.HTTP_INTERNAL_SERVER_ERROR
     else:
         pub = name2publisher.get(package)
         if pub is None:
             pub = ModPythonSessionPublisher(package,
                       session_mgr=web_session_mgr)
             pub.setup_logs()
             name2publisher[package] = pub
         return pub.publish_modpython(req)



reply