Using the Session object with classes
with focus on the
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
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
To ease the porting of your existing class-based python 'cgi' scripts into the mod_python framework.
To provide boilerplate code to wrap your existing modules/classes.
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
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