[Web-SIG] [WSGI] mod_python wrapper: minimal first attempt
Robert Brewer
fumanchu at amor.org
Thu Oct 14 07:13:21 CEST 2004
Phillip J. Eby wrote:
>
> At 02:06 PM 10/13/04 -0700, Robert Brewer wrote:
> >In order to test my application's WSGI interface, I wrote a quick
> >mod_python server interface for WSGI. It's not bulletproof, but the
> >parts I use work. Sorry, Phillip, I didn't subclass
> >wsgiref.handlers.BaseHandler yet. ;(
>
> That's okay; you've given me several of the pieces I would
> need to do it myself. :)
I was hoping someone would say that. :)
> Anyway, a mod_python handler would probably look something like:
>
> from wsgiref.handlers import BaseCGIHandler
>
> class ModPyHandler(BaseCGIHandler):
>
> def __init__(self,req):
> req.add_common_vars()
> BaseCGIHandler.__init__(self,
> stdin = ModPythonInputWrapper(req),
> stdout = None,
> stderr = ModPythonErrorWrapper(req),
> environ = dict(req.subprocess_env.items()),
> multiprocess = True, # XXX
> multithread = True, # XXX
> )
1. I found apache.build_cgi_env(req) tonight, which does the
add_common_vars() and dict() shoving for you. Unfortunately, it's got a
bug. So I just stole code from it.
2. I think apache.mpm_query() is what we want for
multithreading/process. But it was introduced in version 3.1, so that
needs to be trapped. I went with optional arguments to
ModPyHandler.__init__
> def wsgi_handler(req):
> handler = ModPyHandler(req)
> options = req.get_options()
> appmod,appname = options['application'].split('::')
> d = {}
> exec ("from %(appmod)s import %(appname) as application" %
> locals()) in d
> handler.run(d[application])
> from mod_python import apache
> return apache.OK
Eeew. exec. Smelly. :) I'll stick with plain Python code over
PythonOption, thanks, and make my app developers do a few lines of extra
work *once* instead of every deployer on every install. To each his
own... :P
> Let me know if this code works for you, and if so I'll add it to the
> wsgiref library.
Here's the revised version. I haven't tested everything; for example,
reading straight from wsgi.input or writing to .errors. I'll wait for
the bug reports. :)
class ModPythonInputWrapper(object):
def __init__(self, req):
self.req = req
def read(self, size=-1):
return self.req.read(size)
def readline(self):
return self.req.readline()
def readlines(self, hint=-1):
return self.req.readlines(hint)
def __iter__(self):
return iter(self.req.readlines())
class ModPythonErrorWrapper(object):
def __init__(self, req):
self.req = req
def flush(self):
pass
def write(self, content):
self.req.log_error(content)
def writelines(self, seq):
for content in seq:
self.req.log_error(content)
from wsgiref.handlers import BaseCGIHandler
class ModPyHandler(BaseCGIHandler):
def __init__(self, req, threaded=None, forked=None):
from mod_python import apache
try:
q = apache.mpm_query
except AttributeError:
if (threaded is None) or (forked is None):
m = ("You must provide 'threaded' and 'forked' args to
"
"ModPyHandler when running mod_python < 3.1")
raise ValueError(m)
else:
threaded = apache.mpm_query(apache.AP_MPMQ_IS_THREADED)
forked = apache.mpm_query(apache.AP_MPMQ_IS_FORKED)
req.add_common_vars()
env = req.subprocess_env.copy()
if req.path_info:
env["SCRIPT_NAME"] = req.uri[:-len(req.path_info)]
else:
env["SCRIPT_NAME"] = req.uri
env["GATEWAY_INTERFACE"] = "Python-CGI/1.1"
# you may want to comment this out for better security
if req.headers_in.has_key("authorization"):
env["HTTP_AUTHORIZATION"] = req.headers_in["authorization"]
BaseCGIHandler.__init__(self,
stdin=ModPythonInputWrapper(req),
stdout=None,
stderr=ModPythonErrorWrapper(req),
environ=env,
multiprocess=forked,
multithread=threaded
)
self.request = req
self._write = req.write
def _flush(self):
pass
def send_headers(self):
self.cleanup_headers()
self.headers_sent = True
self.request.status = int(self.status[:3])
for key, val in self.headers.items():
self.request.headers_out[key] = val
Robert Brewer
MIS
Amor Ministries
fumanchu at amor.org
More information about the Web-SIG
mailing list