Friday, April 11, 2008

python script as wsgi, cgi, or standalone

EDIT:
See below for original, I realized this could be done cleanly with a decorator.
The decorator wrapplication takes the number of the port to use when called as a standalone server. The EMiddle class is unnecessary, it's just used as middleware to update the environ to show it came via wsgi. If there's a cleaner way, let me know.

#!/usr/bin/python
import os

class EMiddle(object):
def __init__(self, app):
self.app = app
def __call__(self, env, start_response):
env['hello'] = 'wsgi'
return self.app(env, start_response)

def wrapplication(port):
def wrapper(wsgi_app):
if 'TERM' in os.environ:
print "serving on port: %i" % port
os.environ['hello'] = 'standalone'
from wsgiref.simple_server import make_server
make_server('', port, wsgi_app).serve_forever()

elif 'CGI' in os.environ.get('GATEWAY_INTERFACE',''):
os.environ['hello'] = 'cgi'
import wsgiref.handlers
wsgiref.handlers.CGIHandler().run(wsgi_app)
else:
return EMiddle(wsgi_app)
return wrapper

@wrapplication(3000)
def application(environ, start_response):
start_response("200 OK", [('Content-Type', 'text/plain')])
yield "How do you like the teaches of peaches?\n"
yield "from " + environ['hello']




ORIGINAL VERSION:

If you write a script with the application entry point that fits the wsgi spec, it's simple to make it run via mod_wsgi, cgi, or via standalone server depending on the context. I believe this is common knowledge, but for my own reference here's an example with the extra setup (which will work for any script) to do this:


#!/usr/bin/python

def application(environ, start_response):
start_response("200 OK", [('Content-Type', 'text/plain')])
yield environ['QUERY_STRING']

if __name__ == "__main__":
try:
from wsgiref.simple_server import make_server
import sys
port = int(sys.argv[1])
print "server on port: %i" % port
make_server('', port, application).serve_forever()
except Exception, e:

import wsgiref.handlers
wsgiref.handlers.CGIHandler().run(application)

To change between wsgi and cgi toggle between
AddHandler cgi-script .py
AddHandler wsgi-script .py

For the stand alone server, just run it with an argument indicating the port:
python app.py 3000

to use the cherrypy server instead of the wsgiref, replace the make_server() line with:

from cherrypy import wsgiserver
server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', port), [('/', application)], server_name='')
try:
server.start()
except KeyboardInterrupt:
server.stop()

That also handles the server shutdown more politely.

No comments: