2008-12-29

 

mod_python just got cooler - for me at least

Ok - for some this might be old news, but I thought I share it in any case. one reason is for all that still needs to discover this cool feature, and the other reason is that this is my way of documenting features like this - because I forget :-)

So here's the deal: Have a simple mod_python script that can handle both GET and POST requests - but also have the ability to modify your handler without requiring an Apache restart! It was easy enough in mod_perl - but in mod_python it's just too easy!

Let's cook up some configs first... Asume your mod_python modules lives in /opt/mod_python. Here we have two files: /opt/mod_python/mptest.py (the main handler) and /opt/mod_python/content_mptest.py (our "processing" module that processes the request and produces return content).

The main handler has only two functions:
  1. Monitor if the processing module has changed, and reload if it has
  2. Pass the request to the processing module get the result and pass back to the browser
First the Apache config to set this up:
<Location /test>
SetHandler python-program
PythonPath "sys.path + ['/opt/mod_python']"
PythonHandler mptest
</Location>


Warning: don't use test.py, as Python will load another module/script by the same name.

Now the script:

/opt/mod_python/mptest.py:

#!/usr/bin/python

from mod_python import apache
import sys
sys.path.append( '/opt/mod_python' )

content = apache.import_module( 'content_mptest' )

def handler(req):
req.content_type = "text/plain"
req.write( content.process( req ) )
return apache.OK


Cool - The "content_mptest" module will do all the hard work. All you need to do with this module is to modify it to handle exceptions and errors returned by the processing module. Other then that, this module will never be more then a couple of lines and once done will probably *never* change.

The rest of the processing is done in /opt/mod_python/content_mptest.py:
#!/usr/bin/python

from mod_python import apache
import traceback

def process( req ):
if req.method == "POST":
try:
clen = int( req.headers_in[ "content-length" ] )
posted_data = req.read( clen )
return "POST processed. here is your data back:\n\n" + str( posted_data )
except:
return "ERR: Method is post, but something went wrong while processing. Here is the trace:\n\n" + traceback.format_exc()
# this will be the default for GET requests
return "The sub call worked - amazing"



And that's that... You can now test by GET'ing and POST'ing various data to /test on your web server. From here on it's pretty standard stuff... You can also modify /opt/mod_python/content_mptest.py without restarting Apache - way cool!

References:

Comments:
You should not overlap the mod_python module importer directories with sys.path. Ie., don't have:

sys.path.append( '/opt/mod_python' )

It is a bit like crossing the streams in Ghost Busters, bad things can happen with duplicate module imports if you do this.

If things are working as they should, you should be getting warning messages in Apache error log that you are doing bad things with this. The warning only appears once per process instance though.

The importing should still work with out sys.path being changed as mod_python module importer will look in the local directory first. You can also just use 'import' instead of apache.import_module().

Do note though that if you have Python packages in that directory, they will not work. They should be moved outside of the document directory and sys.path pointed at that other location.

The mod_python documentation on the apache.import_module() function explains these things.
 
Thanks for the heads up. I will definitely have a look at the docs.
 
Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?