9 : Python on the Net using CGI and WSGI

Internet is wonderful. You can host your application anywhere and access it anywhere! There is no easier way to share an application with friends. It isn't hard to write your own little application. The web server Apache does all the hard networking work. Your application will print the output and it will be sent back to the browser making the request. The concept behind all this is the common gateway interface or cgi for short.

Apache in Fedora and Ubuntu is configured to handle python cgi code out of the box – only the location where we place the code is different. You will work with the default locations because configuring Apache is outside the scope of today's article.

In Fedora, you write your code in /var/www/cgi-bin and in Ubuntu, you write it in /usr/lib/cgi-bin. The configuration files are in /etc/httpd in Fedora and in /etc/apache2 in Ubuntu. Try the following code in hello.py:


# Tell the browser that it is just plain text

# \n => a blank line. Indicates end of header

print 'Content-Type: text/plain\n'

# The content

print 'Hello, Friends!'

The first line tells the shell to run this script using python; but the file hello.py should be executable, i.e

#chmod +x hello.py

Now, point your browser to 'localhost/cgi-bin/hello.py' and you should see 'Hello, Friends!'.

Getting Started with HTML

The web pages are normally not plain text. They are HTML documents where the tags tell the browser how to display the content. So, modify the hello.py to be an HTML document.


# Tell the browser that it is an html document

print 'Content-Type: text/html\n'

# Print the html document using triple quote for convenience.

print '''<html>



<h3>Hello, Friends</h3>

<p>Welcome - this is a paragraph.</p>



The structure of the html document is <tag>content</tag>, where the content may include other tags. You will be concerned with the body part. You now have two statements being displayed – one as a header and the other as a paragraph. You can try different types of header – h1 through h4 and see the difference.

Displaying a Form

You will build a small application of classifieds for your friends. You will have three fields, a description and a contact email-id. Write the following in cgi-bin/form.py


# Define the form

content_header = "Content-Type: text/html\n"

html_page = """


<head> </head>


<h1>Entry Form</h1>

<form method="post" action="form.py">

<p><label>Title: </label><input type="text" name="title" value="%(title)s"/ ></p >

<p>Description: </p>

<textarea name = "desc" rows=4 cols=60 >%(desc)s</textarea >

<p><label>Email-id: <input type="text" name="email" value="%(email)s" /></label></p >

<button type="submit">Submit</button>





# Python Code

message = email = title = desc = ""

# present the form

print content_header

print html_page % {"title":title, "email":email, "desc":desc, "message":message}

The string html_page is almost a static html page but with a few variables. The values for these variables is substituted in the print statement. Point your browser to http://localhost/cgi-bin/form.py

You can enter the data but not much happens. When you display the form initially, there will be no data in the form. The same program is being called upon actioning the form. So, your code has to be able to differentiate between the two states. You will need to make use of the cgi module and add some python code.

Adding Logic

Add the following code after the Python code comment above.

import cgi

def process_form(email, title, desc):

return "Your entry has been processed"

form = cgi.FieldStorage()

email = form.getvalue("email","")

title = form.getvalue("title","")

desc = form.getvalue("desc","")

if not email or not title:

message = "Please enter Title and Email"


message = process_form(email, title, desc)

email = title = desc = ""

# present the form

print content_header

print html_page % {"title":title, "email":email, "desc":desc, "message":message}

The cgi module does not differential between get and post actions. The getvalue method picks up the value from the form. The second parameter passed to this method assigns a default value in case the value is not available. The method process_form currently does nothing. It could contain the code for storing the data in a database or whatever else you may need.

Adding Some Style

However, the impression may be that pages generated through python are plain and ugly. We can make them even uglier, but colourful. The look and feel will be controlled by a style sheet exactly like the usual html pages. So, replace the <head></head> line above by:


<style type="text/css">

p {margin-left: 50px}

body {background-color: tan}

input {background-color: yellow}

button {background-color: orange}



The page now will be more colourful. Incidentally, the styles can, as is common, be read from a css file.


Now that you know how to write a simple application for the web, you may want to write an application server in Python. Chances are you would use one like Django, Turbo-Gears, Pylons, etc. But you can use the web server gateway interface as well which is supported by various application servers. You can find more about it at http://www.wsgi.org or find more detailed tutorials by just searching for 'wsgi tutorials', e.g. http://webpython.codepoint.net/wsgi_tutorial. Try the following simple code in a file test_app.py


from wsgiref.simple_server import make_server

def application(environ, start_response):

keys_of_interest = ['PATH_INFO', 'QUERY_STRING','REQUEST_METHOD']

env_vars = (key + ':' + value for key, value in environ.items() if key in keys_of_interest)

msg = '\n'.join(env_vars)

status = '200 OK'

response_headers = [('Content-Type', 'text/plain')]

start_response(status, response_headers)

return ["Environment Variables of Interest\n\n", msg]

# Start the server and handle just one request

httpd = make_server('localhost', 8080, application)


For creating a web server, you will need to have a method called application (not mandatory but desirable). The method requires two parameters, environ and start_response and returns a list of strings to be displayed. The first parameter is a dictionary containing various environment variables. Your program displays a few of them related to the web page. The second parameter is a method you call to, well, start the response for the request.

You can convert your code into a web server using the simple reference server included in Python. Your server would normally keep handling requests and instead of calling handle_request you would call serve_forever.

You may notice the use of () instead of [] for defining env_vars above using list comprehensions. The use of square brackets creates a list while the use of parentheses creates a generator for the list. Using a generator saves the creation of a temporary list which can be especially beneficial when dealing with potentially large lists.

You would run the application from the command prompt, 'python test_app.py', and pointing the browser to 'localhost:8080/anything?anyvar=anyvalue'. The result would be

Environment Variables of Interest




You can change the cgi application you wrote to work with wsgi instead. Your file, application.py, should then include:


from wsgiref.simple_server import make_server

from cgi import parse_qs

def application(environ, start_response):

path_info = environ['PATH_INFO']

if path_info.endswith('form.py'):

# for post method, params in a file object

req_size = int(environ['CONTENT_LENGTH'])

params = parse_qs(environ['wsgi.input'].read(req_size))

response = process_form(params)


response = display_form()

status = '200 OK'

response_headers = [('Content-Type', 'text/html')]

start_response('200 OK', [('Content-Type', 'text/html')])

return [response]

httpd = make_server('localhost', 8080, application)


The core application code is not very different from the earlier one. The form responds with a POST method and calls form.py, just because it had called it in the cgi version and you are using the same html page and you call process_form. For all other uri's, you display a blank form. A uri does not correspond to a file or a method name any more. You can use path_info and the parameters returned to perform whatever actions are required for the web application.

The parameters returned by GET method are in QUERY_STRING environment variable. However, the POST method may return large amounts of data; so it is available in a file object referred by wsgi.input. The method parse_qs parses a string and converts the parameters into a dictionary. Minimal examples of display_form and process_form you may write are:

html_page = defined as above

def display_form(title="", desc="", email="", msg=""):

return html_page % {'title':title, 'desc':desc,'email':email, 'message':msg}

# the logic

def process_form(params):

email = params.get("email",[""])[0]

title = params.get("title",[""])[0]

desc = params.get("desc",[""])[0]

if not email or not title:

return display_form(title=title, desc=desc, email=email,

msg="Please enter Title & Email")


# the code to save or process the data would appear here

return display_form(msg="Your message has been processed")

The data returned by POST is a list of values because a form may include multiple rows with the same field name. So, you pick the first value from the list.

The web server frameworks make it much easier to handle url's, manage complex web pages and associate form fields with database columns. So, you would normally use one of the frameworks. Managing classifieds would be a pretty simple task for any of the frameworks mentioned.

It is hoped that by the wider adoption of wsgi, it will be possible to write components which may be callable from various frameworks or web applications. Here's an example of plugging trac within a Pylons framework – http://wiki.pylonshq.com/display/pylonscookbook/A+Pylons+Controller+with+Trac+as+WSGI+Callable. So, you can easily extend your classifieds application with a wiki, using moin-moin and a fault tracking application using trac!

<Prev>  <Next>