Session use with classes

Using the Session object with classes

with focus on the [WWW] Publisher model


In order to help familiarize yourself with the coding standards of these examples, please review the Best programming practices and style guide

The mod_pyton documentation already offers concise examples on how to invoke [WWW] functions directly with Session support. Including the Publisher model and integrating into a class framework takes some more work. These examples are minimal constructs on how things fit together.

Motivation

  1. To ease the porting of your existing class-based python 'cgi' scripts into the mod_python framework.

  2. To provide boilerplate code to wrap your existing modules/classes.

  3. To minimally impact your design implementation.

Examples

These examples are in order of preferred coding standard. While they all work, example #1 most embodies the spirit of mod_python programming. Attention is also paid to portability and extendability.

  1. Using the Stacked Handler

  2. Using a class Instance

  3. Using a class to invoke functions


1. Using the Stacked Handler

Your nominal URL for this example will be

http://your.domain.com/ExampleSession/ExampleStackedHandler.py/foo

This first example requires some tweaking of your httpd.conf:

<Directory /usr/local/apache2/htdocs/ExampleSession>
    AddHandler mod_python .py
    PythonDebug On
    PythonHandler _sessions
    PythonHandler mod_python.publisher
</Directory>

Notice there are two PythonHandler entries here. When invoked, Apache will call these in order. The first calls a file _sessions.py.

   1 # Save this file as _sessions.py
   2 from mod_python import apache, Session
   3 
   4 def handler(req):
   5     if not hasattr(req, 'session'):
   6         req.session = Session.Session(req)
   7     return apache.OK

When the default 'handler' function in this file is invoked, the req object is populated with a Session object if it doesn't already exist. Since mod_python keeps track of the req object, it is availble to each successive PythonHandler call. Therefore, when the following file is invoked, the req object is ready to be passed into the __call__ method.

   1 # Save this file as ExampleStackedHandlerSession.py
   2 # This example uses stacked handlers and assume the _sessions handler has
   3 # already been invoked.
   4 
   5 from mod_python import apache, Session
   6 from time import time
   7 
   8 _tags = (1,2,3,4)
   9 
  10 class ExampleSession:
  11    def __call__(self, req):
  12        return self._foo(req)
  13 
  14    def __init__(self, *args, **kwargs):
  15        # Do something with the args here.
  16        return
  17 
  18    def _foo(self, req):
  19        try:
  20            delta = int(time()- req.session['last'])
  21        except KeyError:
  22            delta = 0
  23        total = int(time()- req.session.created())
  24        try:
  25            req.session['hits'] += 1
  26        except:
  27            req.session['hits'] = 1
  28 
  29        req.session['last'] = req.session.last_accessed()
  30        req.session.save()
  31        return """Session ID: %s
  32 This session has been active for %d seconds
  33 This session was last accessed %d seconds ago
  34 This session has been accessed %d times""" % \
           (req.session.id(), total, delta, req.session['hits'])
  35 
  36 foo=ExampleSession(_tags)

As an added feature of using Stacked Handlers is that when any module following this framework is invoked in the directory, it will have automatic access to the req object. Therefore, instances of your classes are not required to manage their own session state.

So what happened to the `__init__`?

Since mod_python calls the object to invoke it, the __call__ method needs to be defined and accept the req object as one of its arguments. You can still pass in global vars to the __init__ to setup private data members.


2. Using a class Instance

Your nominal URL for this example will be

http://your.domain.com/ExampleSession/ExampleInstanceSession.py/foo

This example does not use the augmented entry in httpd.conf, so be sure and comment out the PythonHandler _sessions entry.

   1 # Save this file as ExampleInstanceSession.py
   2 # This example uses an instance of the ExampleSession class for every request.
   3 
   4 from mod_python import apache, Session, util
   5 from time import time
   6 import types
   7 
   8 _tags = (1,2,3,4)
   9 
  10 class Instance:
  11    def __init__(self, target, *args, **kwargs):
  12        self.__target = target
  13        self.__args = args
  14        self.__kwargs = kwargs
  15        return
  16 
  17    def __call__(self, req):
  18        assert(type(self.__target) in [types.ClassType, types.TypeType])
  19        if not hasattr(req, 'session'):
  20           req.session = Session.Session(req)
  21        target = self.__target(*self.__args, **self.__kwargs)
  22        return util.apply_fs_data(target, req.form, req=req)
  23 
  24 
  25 class ExampleSession:
  26    def __call__(self, req):
  27        return self._foo(req)
  28 
  29    def __init__(self, *args, **kwargs):
  30        # Do something with the args here.
  31        return
  32 
  33    def _foo(self, req):
  34        try:
  35            delta = int(time()- req.session['last'])
  36        except KeyError:
  37            delta = 0
  38        total = int(time()- req.session.created())
  39        try:
  40            req.session['hits'] += 1
  41        except:
  42            req.session['hits'] = 1
  43 
  44        req.session['last'] = req.session.last_accessed()
  45        req.session.save()
  46        return """Session ID: %s
  47 This session has been active for %d seconds
  48 This session was last accessed %d seconds ago
  49 This session has been accessed %d times""" % \
           (req.session.id(), total, delta, req.session['hits'])
  50 
  51 foo=Instance(ExampleSession, _tags)

As you see, we are not instantiating the ExampleSession class directly, but using the Instance class to create a new ExampleSession object each time a call is made to the request handler. This avoids the possibility of gumming up the works in Apache if concurrent worker threads are all executing the same handler.

Advantages

(./) This allows you to have a mixture of non-session and session enabled funxtions/classes in a single package. Those not needing a session context are not required to implement this solution.

(./) You have access to the req object before your working class is called. This allows you to initialize it with info from the request FormObject (if available).

Disadvantages

(./) The Instance class is duplicated in all modules. If you have a large python package, this is an inefficient manner to implement Sessions.

Note that in both of these prior examples, the ExampleSession class remained the same.


3. Using a class to invoke functions

This last example is a bit of an odd-ball, we see how to use a class to invoke a selection of methods defined in the same module. This is not common practice, but maybe it will give you some ideas. Your nominal URLs for this example will be

http://your.domain.com/ExampleSession/ExampleFunctionSession.py/foo
http://your.domain.com/ExampleSession/ExampleFunctionSession.py/bar
   1 # Save this file as ExampleFunctionSession.py
   2 # This example uses a class to session enable standalone functions
   3 
   4 from mod_python import apache, Session, util
   5 from time import time
   6 
   7 class SessionEnabled:
   8    def __init__(self, target):
   9        self.__target = target
  10        return
  11 
  12    def __call__(self, req):
  13        if not hasattr(req, 'session'):
  14            req.session = Session.Session(req)
  15        return util.apply_fs_data(self.__target, req.form, req=req)
  16 
  17 
  18 def _foo(req):
  19     try:
  20         delta = int(time()- req.session['last'])
  21     except KeyError:
  22         delta = 0
  23     total = int(time()- req.session.created())
  24     try:
  25         req.session['hits'] += 1
  26     except:
  27         req.session['hits'] = 1
  28 
  29     req.session['last'] = req.session.last_accessed()
  30     req.session.save()
  31     return """Session ID: %s
  32 This session has been active for %d seconds
  33 This session was last accessed %d seconds ago
  34 This session has been accessed %d times""" % \
           (req.session.id(), total, delta, req.session['hits'])
  35 
  36 def _bar(req):
  37     return """Another function called with session ID: %s""" % req.session.id()
  38 
  39 foo=SessionEnabled(_foo)
  40 bar=SessionEnabled(_bar)

This should give you a working starting point. From here the sky is the limit!


CategoryExamples CategoryExamples

last edited 2007-03-26 23:50:44 by RoyFielding