The Web Server Gateway Interface (WSGI) is a standard interface between web server software and web applications written in Python. Having a standard interface makes it easy to use an application that supports WSGI with a number of different web servers.One implementation is wsgiref that was added to Python 2.5 Standard Library.
Here is a simple web application that writes "Hello World":
def index(environ, start_response): start_response("200 Ok", [('content-type', 'text/html')]) return ['Hello World!'] if __name__ == "__main__": from wsgiref.simple_server import make_server server = make_server(HOST, PORT, index) print 'Starting up HTTP server on port %i...' % PORT server.serve_forever()
This is nice, but is not very useful for us, so lets add some kind of controller(or url dispatcher):
from dispatcher import Dispatcher dispatcher = Dispatcher() dispatcher.add(r'^/$', 'views.index') dispatcher.add(r'^/hello/(?P<username>\w+)/$', 'views.hello') HOST = 'localhost' PORT = 8000 if __name__ == "__main__": from wsgiref.simple_server import make_server server = make_server(HOST, PORT, dispatcher) print 'Starting up HTTP server on port %i...' % PORT server.serve_forever()
As you can see I've used a regular expression for the url mapping, just like Django uses.
Here is the dispatcher(dispatcher.py):
import re class Dispatcher(object): def __init__(self, handle404 = None): self.urls = dict() self.request_path = '' if handle404: self.handle404 = handle404 else: self.handle404 = self._404 def __call__(self, environ, start_response): self.request_path = environ.get('PATH_INFO', '') for url in self.urls: regex = re.compile(url) if regex.match(self.request_path): m = regex.match(self.request_path) mod_name, func_name = self._get_mod_func(self.urls[url]) try: callback = getattr(__import__(mod_name, {}, {}, ['']), func_name) except ImportError, e: raise Exception, "Could not import %s. Error was: %s" % (mod_name, str(e)) except AttributeError, e: raise Exception, "Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e)) args = (environ, start_response) kwargs = dict() for i in regex.groupindex: kwargs[i] = m.group(i) # Run callback with environ, start_response and args return callback(*args, **kwargs) # No match with the defined urls return self.handle404(environ, start_response) def _get_mod_func(self, callback): """ Converts 'path.to.module.funtion' to ['path.to.module', 'function'] """ try: d = callback.rindex('.') except ValueError: return callback, '' return callback[:d], callback[d+1:] def _404(self, environ, start_response): start_response("404 Not Found", [('content-type', 'text/html')]) return ['Not Found'] def add(self, regex, handler): self.urls[regex] = handler
And here is the views(views.py):
def index(environ, start_response): start_response("200 Ok", [('content-type', 'text/html')]) return ['Index'] def hello(environ, start_response, username): start_response("200 Ok", [('content-type', 'text/html')]) return ['Hello %s' % username]
Simple eh?
This is just a start, now we can add more features, like encapsulate the start_response method and add some kind of HttpResponse.
I don't what do reinvent the wheel here and add another web framework to Python, just like Joe Gregorio says in this article. But to prove how trivial is to make a simple framework with WSGI and don't worry about the deployment at development phase.