[mod_python] Success: PythonCGI (not the other way)

Mike Looijmans mike.looijmans at asml.com
Fri Mar 21 07:35:40 EST 2003


Since not all our servers are Apache 2.0.xx yet, and mod_python isn't
available on those, I wanted to remain compatible with the CGI interface.

But sacrificing performance by using the cgihandler (which made the server
effectively single-threaded because of the stdout sharing) did not seem a good
idea.

Instead, I put it the other way around. The native connection mode is
mod_python, and I let the CGI handler simulate the Request interface. By not
using too big a subset, I managed to quite easily do this. The net effect is
that the mod_python script now run much faster than the cgihandler conversion
I used first, and the CGI still works at roughly the same speed (the impact on
performance by creating the Request object is neglegible. The main part of the
code is below.

Each module has a main() function, that takes some parameters. One can use
"main(req, form)" for example, or "main(req, db, auth, form)". The main must
write to req.write(..) just like a mod_python script would. The script does
not know whether it's run from CGI or mod_python.

Comments are welcome!

##
class Request:
    def __init__(self):
        environ = os.environ
        self.subprocess_env = environ
        self.content_type = "text/plain"
        self.status = 200
        self.args = environ.get('QUERY_STRING', '')
        self.path_info = environ.get('PATH_INFO', '')
        self.method  = environ['REQUEST_METHOD']
        self.unparsed_uri = environ['REQUEST_URI']
        self.uri = environ['SCRIPT_NAME']
        self.content_length = -1
        self._headed = 0
        self.headers_out = self
        self.headers = ""
        self.read = sys.stdin.read
        hin = {}
        v = environ.get('HTTP_REFERER', '')
        hin['Referer'] = v
        hin['referer'] = v
        if environ.has_key('HTTP_COOKIE'):
            v = environ['HTTP_COOKIE']
            hin['Cookie'] = v
            hin['cookie'] = v
        self.headers_in = hin
    def add(self, key, value):
        self.headers += '%s: %s\n' % (key, value)
    def set_content_length(self, len):
        self.content_length = len
    def _writeheaders(self):
        self._headed = 1
        sys.stdout.write('status: %s\n' % self.status)
        sys.stdout.write('Content-Type: %s\n' % self.content_type)
        if self.content_length >= 0:
            sys.stdout.write('Content-Length: %s\n' % self.content_length)
        sys.stdout.write(self.headers)
        sys.stdout.write('\n')
        self.write = sys.stdout.write
    def write(self, s):
        if not self._headed:
            self._writeheaders()
        sys.stdout.write(s)

try:
    req = Request()
    program = req.path_info
    if program[0] == '/':
        program = program[1:]
    if not program:
        raise ERedirect, 'ops_webrellist'
    try:
        module = __import__(program)
        try:
            main = module.main
        except AttributeError:
            raise ENoMain, path
        kwargs = {}
        kwargs['req'] = req
        expected = main.func_code.co_varnames[:main.func_code.co_argcount]
        db = None
        if 'form' in expected:
            import cgi
            form = cgi.FieldStorage()
            kwargs['form'] = form
            req.form = form
# Some more stuff about database connections left out #
        apply(module.main, (), kwargs)

    except ImportError:
        admin = os.environ['SERVER_ADMIN']
        print '''Status: 404 Not Found
Content-Type: text/html


--
Mike Looijmans
ASML: http://www5nl.asml.nl/~mlooijma
Private: http://www.milosoftware.com





More information about the Mod_python mailing list