Python on the Web

Python’s flexible nature means it can bend to almost any application you can imagine and web development is no exception. This article covers simple form handling and creating cookies and presents an example using everything demonstrated.


If you’ve ever seen Python in action, even at a distance, I’m sure you’ll agree that it looks pretty neat. And the combination of development speed, out-of-the-box power, and stunning syntax usually means you end up with one thing… elegant, maintainable solutions, quickly. And lets face it, that’s just what web developers are looking for right?

I’ll let you judge for yourself…

In this article we’re going to be using Python to write some small and (hopefully) interesting examples, starting with simple form handling and ending with a complete, full featured (upload limit, file types etc.) upload script that you can use in your websites!

If you haven’t guessed it already this is a Python article, so if you’re new to the language I highly recommend reading Martin Tsachev’s Getting Started with Python before reading this one. if you need a quick intro to Python CGI, skim over Preston Landers’ “Writing CGI Programs in Python”.

Before we get going, you’ll need a few things before you can run the examples in this article:

1. A web server set up to handle CGI (tested with Apache)
2. The latest version of Python from www.python.org

Creating a Form to Display User Details

Possibly the most important thing a web application does is collect user input. And like most things, Python has just the module we need in its Standard Library.

Let’s jump right in and write a small function to display the user’s details if any where entered. (For this we’ll assume our form looks like this… three input boxes called – ‘name’, ‘age’ and ‘email’. A check box called ‘done’ and a ‘submit’ button)

#!/usr/bin/env python

import cgi

form = cgi.FieldStorage()

def values(fields):
 for value in fields:
  if value in form: print form[value].value + ‘<br />’

if __name__ == ‘__main__’:
 
 print ‘Content-Type: text/htmln’

 if ‘submit’ and ‘done’ in form:
  values((‘name’, ‘age’, ‘email’))
 else:
  print ‘if you were directed here in error please visit here.com’

This is very simple but it gives you an idea of how easy working with forms is when you strip away all the crap!

In this example (and most of the examples in this article) we start by importing the ‘cgi’ module and creating an instance of the FieldStorage() class to store our form values. Next we define a new user function called values() which takes a sequence of field names and iterates over them, printing the value if the one was set. Finally before the function outputs anything we check if the ‘submit’ and ‘done’ fields exist.

Ok, chances are you noticed this but just for clarity: forms in Python work like any dictionary, with the exception that you have to follow the call by the variable you want access from that key i.e. form['key'].value gives you the value.

{mospagebreak title=Creating a Warning}

Like life itself, we seem to want more control. And this next function we’ll create will give us just that – and output a warning if the field wasn’t filled in correctly!

#!/usr/bin/env python

import cgi, re

form = cgi.FieldStorage()

def check(**fields):
 for field in fields:
  if field in form:
   value = form[field].value
   if re.search(fields[field], value): print value + ‘<br />’
   else: print field, ‘was not filled in correctly!<br />’

if __name__ == ‘__main__’:
 
 print ‘Content-Type: text/htmln’
 
 if ‘submit’ and ‘done’ in form:
  check(name = ‘^[a-zA-Z ]+$’, age = ‘^d{2}$’)
 else:
  print ‘if you were directed here in error please visit here.com’

Since this is pretty similar to our other function, I’m just going to skip over it quickly. If you’ve ever used Perl, then you’ve spotted the regular expressions hiding in there.

Regular expressions in Python are accessed though the ‘re’ module (Python’s regular expression library), for obvious reasons… regular expressions still seem like the best way to describe and check form values.

If you want to find out more about regular expressions and Python check out the ‘re’ module at http://www.python.org/doc/2.3.3/lib/module-re.html

Then if the value isn’t what we want, we get a warning instead of just ignoring the field.

Anyone wondering what **fields is all about? I’m sure someone is. The simplest explanation I can think of is that it tells the function to expect a variable number of arguments in X format (currying in functional programming) i.e.:

>>> def show(*args, **kwds):
…     print args, kwds

>>> show()
() {}
>>> show(‘interesting’, ‘dont ya think!’, arg1 = ‘str1′, arg2 = 2)
(‘interesting’, ‘dont ya think!’) {‘arg1′: ‘str1′, ‘arg2′: 2}
>>>

Simple, and very useful form time to time! Time for a cookie break… No, not the food… although, still pretty sweet over all.

{mospagebreak title=Cookies}

Next up we’ll use Python’s  ‘Cookie’ module to do a little baking! (Ironically I suck at making real cookies.)

>>> import Cookie
>>> cookie = Cookie.SimpleCookie()
>>> cookie['number1'] = ‘some values’
>>> cookie['number2'] = ‘some values’
>>> cookie['number3'] = ‘some values’
>>> cookie['number3']['expires'] = 3600
>>> print cookie
Set-Cookie: number1=”some values”;
Set-Cookie: number2=”some values”;
Set-Cookie: number3=”some values”; expires=Fri, 30-Jan-2004 12:03:20 GMT;

Ok, you have to agree, setting cookies couldn’t be much simpler, and the ‘Cookie’ module wraps it up nicely. The difficulty comes when you want to retrieve your values… ok maybe I’m exaggerating a little here.

#!/usr/bin/env python

import os

def monster():
 if ‘HTTP_COOKIE’ in os.environ:
  cookies = os.environ['HTTP_COOKIE']
  cookies = cookies.split(‘; ‘)
  handler = {}
  
  for cookie in cookies:
   cookie = cookie.split(‘=’)
   handler[cookie[0]] = cookie[1]
   
  return handler
  
if __name__ == ‘__main__’:
 
 import Cookie
 
 cookie = Cookie.SimpleCookie()
 cookie['monster'] = ‘cookievalue’
 print cookie
 
 print ‘Content-Type: text/htmln’
 
 print ‘Hit refresh to see the cookie!!!<br />’
 print ‘Hewwo, im the cookie’, monster()

This is all about parsing the ‘HTTP_COOKIE‘ header and inserting the cookie values into a dictionary.

We start by importing the ‘os’ module into our program and defining the monster() function – this checks if the ‘cookies’ string exists or not. If it does then we split it, leaving us with a list of ‘key=value’ strings. After that all we have left to do is loop over the list and split the values again, placing them into the dictionary.

Ok finally let’s do something a little useful with our cookies… and in keeping with our final example, we’re going to create a counter function, which allows you to set limits on different processes. Observe and enjoy.

#!/usr/bin/env python

import Cookie, os

def limits(count):
 if ‘HTTP_COOKIE’ in os.environ:
  cookies = os.environ['HTTP_COOKIE']
  cookies = cookies.split(‘; ‘)
  handler = {}
  for cookie in cookies:
   cookie = cookie.split(‘=’)
   handler[cookie[0]] = cookie[1]

  if ‘count’ in handler:
   number = int(handler['count'])
   if number < count:
    cookie = Cookie.SimpleCookie()
    cookie['count'] = number + 1
    cookie['count']['expires'] = 86400
    print cookie
    return True
 else:
  print Cookie.SimpleCookie(‘count=1′)
  return True

if __name__ == ‘__main__’:

if limits(5):
 print ‘user was under their limit, do this…’
else:
 print ‘user was over their limit, do nothing!’

The start of this is exactly the same as in our monster() function except that if there are no cookies set, then we create one and set its value to one. If there are any cookies to unpack, then we check for ‘count’ in the handler dictionary and type-cast its value to a ‘number’ using int()… ‘number’ is then compared to the ‘count’ variable to check if the user is under or over their limit. Provided they’re under their limit, then we update the cookie and set the expiry date to 24 hours before returning True; this is done so we can use if-else blocks to control the users access.

{mospagebreak title=Sending Email}

Ok, what about sending email? What sounds like a big, scary subject is actually pretty simple once you get down to it. And because of Python’s amazingly clear syntax it’s very clean, not to mention compact.

#!/usr/bin/env python

import smtplib

def mail(address, subject, message, host = ‘localhost’):

 headers = ‘From: %srnTo: %srnSubject: %srnrn%s’
 message = headers % (address[0], ‘,’.join(address[1:]), subject, message)

 server = smtplib.SMTP(host)
 server.sendmail(address[0], address[1:], message)
 server.quit()

if __name__ == ‘__main__’:

mail((‘someone@somewhere.com’, ‘sometwo@somewhere.com’), ‘subject’, ‘message’)

Admit it, how much smaller is that than you expected! Before we delve deeper into this function, you need to understand a little about how it’s being called or chances are you’ll end up with errors!

mail((‘from’, ‘to’, …), ‘subject’, ‘message’, ['host'])

The first argument here is a sequence of two or more addresses, the first being the ‘From’ address and the rest ‘To’ addresses. Followed by the ‘Subject’ and ‘Message’. You may also need to tell this function what host you want to use if one isn’t available locally.

For this example we started by importing the ‘smtplib‘ module, which provides functions/classes for sending emails from Python… so mail() is really just a wrapper over what you would normally do to send email, but it does make things easier!

That is to say, inside mail() we do two main things…

  1. Create a MIME header containing our ‘From’ and ‘To’ addresses… as well as any other data we want to send i.e. Subject, Message, Content-Type. When complete this is assigned to the ‘message’ variable.
  2. Connect to the mail server using the SMTP() class. If the connection is successful then the addresses and ‘message’ header are sent to using the sendmail() method before quitting.

If you plan on playing with ‘smptlib’ or similar modules, then you’ll probably end up thanking the programmer who wrote it, for the set_debuglevel() method! As is my experience, anything that could go wrong will go wrong the first few times i.e.:

server = smtplib.SMTP(host)

server.set_debuglevel(1)

server.sendmail(from, to, message)
server.quit()

{mospagebreak title=A Last Example}

So far you’ve seen how you can use Python to get forms data, create cookies and send email… in this last example we’ll be using as much of what you’ve learned here as we can (without going over the top).

#!/usr/bin/env python

import cgi, os, sys

sys.stderr = sys.stdout

def uploads(form, name, path, *args):
 if form.has_key(name):
  
  #If the form field exists if ‘form’ then parse the filename to point
  #at the desired location.
  
  path = os.path.join(path, os.path.basename(form[name].filename))

  for each in args:
   
   #Loop over any available file types and check if the file being
   # uploaded is the right format. Checks if the file already exists
   
   if path.endswith(each) and not os.path.isfile(path):
    file(path, ‘wb’).write(str(form[name].value))
    
    #Return True to indicate the file was uploaded successfully.
    return True
 
if __name__ == ‘__main__’:
 
 form = cgi.FieldStorage()
 
 print ‘Content-Type: text/htmln’
 
 if uploads(form, ‘upload’, ”, ‘.txt’, ‘.zip’):
  
  #If the upload was successful then print a message.
  
  print ‘Finished uploading file…’
 else:
  print ‘Failed to upload file. Please visit our help center at…’

If you want to give this a go, you’ll need a form set up for file upload; something like this one…

<form name=”upload” method=”POST” action=”upload.py” enctype=”multipart/form-data”>
  <input name=”upload” type=”file” /><input type=”submit” name=”submit” />
</form>

This is pretty small as functions go, but there’s quite a lot going on right from the beginning!

As in our other examples, this starts by importing the modules we need into the programs global namespace. Unlike these examples, our next line redirects errors to standard output; this simply sends error messages, as you would get from Python normally to the web browser instead of the error log.

If you’re going to use Python for CGI, then you should definitely take a look at the ‘cgitb’ module at http://www.python.org/doc/2.2.3/lib/module-cgitb.html

{mospagebreak title=Inside uploads()… }

We first need to make sure that the field ‘name’ provided is in our ‘form’. As long as it is then we get the path (as it appeared in the file field) of the file we want to upload and get the files name using os.path.basename(). We then join it to the ‘path’ using the os.path.join() function… this lets us to upload files to different places and keep the original filename.

Next we loop over the available file types in ‘args’ and check the files extension against each type. If the file is a valid type, then we make sure the file doesn’t already exists (files won’t be uploaded if the file exists to prevent overwriting) before writing the file to the server.

As in the limits() example uploads() can be used with an if statement to check if the function was successful or not. Or even use it with limits() so the user can only upload a set number of files per day.

Ok, by now you should have a fair idea of what exactly writing web applications with python is about and why you would want to, as well as have some idea of what’s possible… but this is only the tip of the iceberg!

Hope you’ve had fun. If you want to learn more about Python or the subjects covered here, then start clicking:

http://www.python.org – Python homepage
http://www.python.org/doc/2.3.3/tut/tut.html - Python tutorial
http://www.python.org/doc/2.3.3 – Python Documentation online!
http://www.apache.org – Apache homepage
http://httpd.apache.org/docs – Apache Documentation
http://www.modpython.org - mod_python homepage!
http://www.zope.org - Zope homepage!
http://spyce.sourceforge.net – Spyce [Python Server Pages] homepage!

Note: All the sample programs shown and discussed in this article were tested on Windows XP running Python 2.3 under Apache 1.3. Upload script was tested on remote Linux server.

[gp-comments width="770" linklove="off" ]
antalya escort bayan antalya escort bayan