Understanding Python WSGI with Examples
Coming from a strongly PHP background, initially looking at the web-development landscape whilst delving into Python seemed a little confusing. As Python was not developed for the web from an offset, a specification was accepted called PEP 333 which standardised the required interface between Web servers and Python Web Frameworks/Applications. Despite the additional complexity, the manner in which middle-ware applications can be integrated, along with the server choice add possibilities that I find hard to locate a comparable in PHP.
Simply put a WSGI (Web Sever Gateway Interface) compliant application must supply a callable (function, class) which accepts a ‘environ’ dictionary and ‘start_response’ function. For a familiar PHP comparison, you can think of the ‘environ’ dictionary as a combined ‘$_SERVER’, ‘$_GET’ and ‘$_POST’, with extra processing required. This callable is expected to invoke the ‘start_response’ function with the desired response-code/header-data, and then return a byte iterable with the response body.
def app(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [b'Hello, world!'] if __name__ == '__main__': try: from wsgiref.simple_server import make_server httpd = make_server('', 8080, app) print('Serving on port 8080...') httpd.serve_forever() except KeyboardInterrupt: print('Goodbye.')
Using the single-threaded WSGI reference implementation provided with Python is a great choice for experimenting with these lower-level concepts. You will notice that as the example is written for Python 3 we must return an iterable (in this case a list) with declared ‘byte’ content inside.
Now that we are familiar with the basic structure of a WSGI compliant application we can now experiment with a more practical example. Below we provide the client with a simple form which posts a supplied ‘name’ for the application to greet accordingly.
import cgi form = b''' <html> <head> <title>Hello User!</title> </head> <body> <form method="post"> <label>Hello</label> <input type="text" name="name"> <input type="submit" value="Go"> </form> </body> </html> ''' def app(environ, start_response): html = form if environ['REQUEST_METHOD'] == 'POST': post_env = environ.copy() post_env['QUERY_STRING'] = '' post = cgi.FieldStorage( fp=environ['wsgi.input'], environ=post_env, keep_blank_values=True ) html = b'Hello, ' + post['name'].value + '!' start_response('200 OK', [('Content-Type', 'text/html')]) return [html] if __name__ == '__main__': try: from wsgiref.simple_server import make_server httpd = make_server('', 8080, app) print('Serving on port 8080...') httpd.serve_forever() except KeyboardInterrupt: print('Goodbye.')
Although somewhat verbose we have been able to create a simple web application which handles supplied POST data using the CGI modules ‘FieldStorage’ class. These are the very simplified building blocks used in popular frameworks such as Flask and Django.