Building a Python Web Application, Part 1

Edit: I've cleaned up the longer example, using Python's string.Template module for the templates. I've also set up a git repo for the source that will go along with posts to this series: Python Webapp Gitweb

Recently, I’ve been interested in writing web applications in Python, and one of the fun things that I discovered was the Python Web Server Gateway Interface, which is a standard interface for Python web servers, web applications, and something called middleware which can sit between the two.

One of the coolest things about WSGI is the fact that you now don’t have to decide on a specific web server before you start coding. In fact, the Python wsgiref module comes with a built-in simple web server which allows you to start coding up your web application with nothing but a bare install of Python 2.5 (or higher, of course)!

There are plenty of overviews of WSGI out there, so I won’t bother creating yet another in-depth explanation. What I will do, though, is show you how easy it is to get started.

Your basic “Hello, World!” application can be accomplished, server and all, with as little as the following:

    def handle_request(environment, start_response):
        start_response('200 OK', [('content-type', 'text/html')])
        return ['Hello, World!']
    
    if __name__ == '__main__':
        from wsgiref import simple_server
        simple_server.make_server('', 8080, handle_request).serve_forever()

So what’s going on there is that the simple_server passes each request to the handle_request function, which calls start_response with the HTTP status code and a list of tuples of header names and values.

Then, an iterable is returned. This iterable will be iterated through for the output to send to the browser. In more complex applications, this iterable would most likely be a generator or some sort (a function which yields values or a generator expression, perhaps), rather than simply putting the entire output into a list.

The environment parameter contains all the information that any regular web application might need such as request information, server information, and any extra values set up by previously run middleware.

Using the basics we have from above, and a couple of nice utility functions from wsgiref.util, it’s a simple step to add a bit more and make this a full-fledged mini-framework.

    from wsgiref import util
    from string import Template
    
    # Templates
    wrapper = Template("""
    <html><head><title>$title</title></head><body>
    $body
    </body></html>
    """)
    
    four_oh_four = Template("""
    <html><body>
      <h1>404-ed!</h1>
      The requested URL <i>$url</i> was not found.
    </body></html>""")
    
    # Template Variables for each page
    pages = {
        'index': { 'title': "Hello There",
                   'body':  
                   """This is a test of the WSGI system.
                    Perhaps you would also be interested in
                    <a href="this_page">this page</a>?"""
                  },
        'this_page': { 'title': "You're at this page",
                       'body': 
                       """Hey, you're at this page.
                       <a href="/">Go back</a>?"""
                       }
        }
    
    def handle_request(environment, start_response):
        try:
            fn = util.shift_path_info(environment)
            if not fn:
                fn = 'index'
            response = wrapper.substitute(**pages[fn])
            start_response('200 OK', [('content-type', 'text/html')])
        except:
            start_response('404 Not Found', [('content-type', 'text/html')])
            response = four_oh_four.substitute(url=util.request_url(environ))
        return [response]
    
    if __name__ == '__main__':
        from wsgiref import simple_server
    
        print("Starting server on port 8080...")
        try:
            simple_server.make_server('', 8080, handle_request).serve_forever()
        except KeyboardInterrupt:
            print("Ctrl-C caught, Server exiting...")

This now handles different URLs by rendering the wrapper template with the variables from the corresponding item in the pages dict, or it sends a 404 page if the requested page doesn’t exist (or if there are any other exceptions, actually).

With the addition of a templating engine and some way to get at your data, this would be something resembling a real web application.

For now, that’s all you’re getting out of me, though. I’ve shown that it’s pretty easy to get started with Python web development without anything but plain-ol-Python at your disposal, putting off choices on things like what server to use so you can get to the important part of actually writing some code. I’ll probably do another post in the next few days, probably starting to use some third-party packages to really start developing something useful.

Until then, if you want some more, see the WSGI tutorials at the link I posted above, especially A Do-It-Yourself Framework, which gives you a bunch more information of parsing requests, sending response headers, and using middleware. Keep in mind that some of the code there is Paste-specific, not that that is a bad thing. Since Paste is intended to be fairly lightweight it won’t really limit your options if you use it, and it doesn’t make sense to write a whole bunch of code to do the mundane parts from scratch anyway.

blog comments powered by Disqus