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 available 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 func Instance(target, *args, **kwargs):
11 def handler(req):
12 assert(type(target) in [types.ClassType, types.TypeType])
13 if not hasattr(req, 'session'):
14 req.session = Session.Session(req)
15 return util.apply_fs_data(target(*args, **kwargs), req.form, req=req)
16 return handler
17
18
19 class ExampleSession:
20 def __call__(self, req):
21 return self._foo(req)
22
23 def __init__(self, *args, **kwargs):
24 # Do something with the args here.
25 return
26
27 def _foo(self, req):
28 try:
29 delta = int(time()- req.session['last'])
30 except KeyError:
31 delta = 0
32 total = int(time()- req.session.created())
33 try:
34 req.session['hits'] += 1
35 except:
36 req.session['hits'] = 1
37
38 req.session['last'] = req.session.last_accessed()
39 req.session.save()
40 return """Session ID: %s
41 This session has been active for %d seconds
42 This session was last accessed %d seconds ago
43 This session has been accessed %d times""" % \
(req.session.id(), total, delta, req.session['hits'])
44
45 foo=Instance(ExampleSession, _tags)
As you see, we are not instantiating the ExampleSession class directly, but using the Instance function 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 functions/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 function 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 function SessionEnabled(target):
8 def handler(req):
9 if not hasattr(req, 'session'):
10 req.session = Session.Session(req)
11 return util.apply_fs_data(target, req.form, req=req)
12 return handler
13
14
15 def _foo(req):
16 try:
17 delta = int(time()- req.session['last'])
18 except KeyError:
19 delta = 0
20 total = int(time()- req.session.created())
21 try:
22 req.session['hits'] += 1
23 except:
24 req.session['hits'] = 1
25
26 req.session['last'] = req.session.last_accessed()
27 req.session.save()
28 return """Session ID: %s
29 This session has been active for %d seconds
30 This session was last accessed %d seconds ago
31 This session has been accessed %d times""" % \
(req.session.id(), total, delta, req.session['hits'])
32
33 def _bar(req):
34 return """Another function called with session ID: %s""" % req.session.id()
35
36 foo=SessionEnabled(_foo)
37 bar=SessionEnabled(_bar)
This should give you a working starting point. From here the sky is the limit!