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_idand 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)