An Introduction to Web Programming with WSGI

Talk given at:EuroPython 2007
By:Michele Simionato
Organization: StatPro Italy
Date: 2007-07-11


This is as a talk for beginners, only knowledge of CGI and a bit of HTTP protocol is expected (non-beginners -> "Zope on a Paste")

Ok, now about me

What I have done



Short history of WSGI

WSGI key concepts

  1. WSGI application:

    (env, resp) -> chunks of text

    env = environment dictionary of the server; resp = function sending to the client the HTTP headers

  2. WSGI middleware:

    WSGI app -> enhanced WSGI app

Hello World

from wsgiref import simple_server

def app(env, resp):
     '200 OK', [('Content-type', 'text/html')])
    return ['<h1>Hello, World!</h1>']

server=simple_server.make_server('', 8000, app)

A real-life example

Let me show a real problem we had at StatPro


The history plotter

It was easy to write a simple command line history plotter

Going on the Web

Without a framework

WSGI is the answer!

The web plotter

$ python

Click here for the live demonstration

Some code (I)

def app(env, resp):
 form = getformdict(env)
 if form.get('submitted'):
    fname = make_graph(form.get('code'), batch=True)
   except Exception, e:
    resp('500 ERR', [('Content-type', 'text/plain')])
    return [traceback.format_exc()]
    resp('200 OK', [('Content-type', 'image/png')])
    return file(fname)

Some code (II)

 resp('200 OK', [('Content-type', 'text/html')])
 return [
  'Try values such as <pre>fri-gb;AVE</pre>',
  '<pre>fri-gb;TSCO</pre> <pre>fri-us;DELL</pre>',
  '<form>', 'insert code ',
  '<input type="text" name="code"/>',
  '<input type="submit", name="submitted",'
  ' value="submit" />',

Some code (III)

def getformdict(env):
   qs = env.get('QUERY_STRING')
   if qs:
       return dict((k, v[0])
              for k, v in cgi.parse_qsl(qs))
       return {}


A common objection

Object publishing (I)

class Example(object):
 def __init__(self, sitename):
  self.sitename = sitename
 def __call__(self):
  yield '<h1>%s: index page</h1>' % self.sitename
  yield 'goto <a href="./page1">page1</a><br/>'
  yield 'goto <a href="./page2">page2</a><br/>'
  yield 'goto <a href="subsite">subsite</a><br/>'
 def page1(self): yield 'page1'
 def page2(self): yield 'page2' = = True

Object publishing (II)

class WSGIObjectPublisher(object):
 def __init__(self, root):
  self.root = root
 def __call__(self, env, resp):
  return self.getsubpage(self.root,env,resp)()
 def getsubpage(self, root, env, resp):
  script_name = util.shift_path_info(env)
  if not script_name: # We've arrived!
    resp('200 OK',[('content-type','text/html')])
    return root

Object publishing (III)

 page = getattr(root, script_name)
except AttributeError:
 resp('404 Not Found',[('content-type','text/plain')])
 return lambda:['missing page %r'%script_name]
exposed = getattr(page, 'exposed', False)
if not exposed:
 resp('404 Not Found',[('content-type','text/plain')])
 return lambda : [
  '%r is not exposed!' % script_name]
return self.getsubpage(page, env, resp)

WSGI vs. frameworks


WSGI vs. frameworks


And now middleware

No middleware in the standard library, but lots of useful middleware from third party sources. For instance, authentication middleware:

from paste.auth.basic import AuthBasicHandler

def only_for_pippo(env, user, passwd):
    return user == 'pippo'

auth_app = AuthBasicHandler(
    app, 'app realm', only_for_pippo)

Debugging WSGI apps

from wsgiref.simple_server import make_server
from paste.evalexception import EvalException

a, b = 1,0

def app(env, resp):
  resp('200 OK',[('Content-type','text/html')])
  return [str(a/b)]


Show evalexception


That's all, folks!

(P.S. at StatPro, we are hiring! ;)