“i drink my wsgi clear” a barebones introduction to python web programming. 3/14/2011
TRANSCRIPT
“I drink my WSGI clear”A barebones introduction to Python Web Programming.
3/14/2011
Who are we?
• Doug Morgan (@dougzor)
• Alex Conrad (@alexconrad)
• Whit Morriss (@whitmo)
What is WSGI?
WSGI
• Web Server Gateway Interface, pronounced ‘whizky’ (duh)
• PEP 333
• A standardized specification to define communication between a web server and your Python application.
What do you need?
• An HTTP server with WSGI support aka a Host (we’ll get into these later).
• A Python file (your app).
How does it work?
• 1) An HTTP request comes in to your server.• 2) The HTTP server calls your Python app with two
arguments: The environment and the “start response” function.
• 3) Your application returns a Response body which is sent back to the client.
• 4) …• 5) Profit!
Example
def application(environ, start_response):# Let’s setup some data to send back to the clientbody = “WSGI is awesome and tastes delicious!”headers = [(“Content-Type”, “text/plain”),
(“Content-Length, str(len(body)))]
# Send response headers back to the clientstart_response(“200 OK”, headers)
# Send the body back to the client to complete the Response
return body
Example
def application(environ, start_response):# Let’s setup some data to send back to the clientbody = “WSGI is awesome and tastes delicious!”headers = [(“Content-Type”, “text/plain”),
(“Content-Length, str(len(body)))]
# Send response headers back to the clientstart_response(“200 OK”, headers)
# Send the body back to the client to complete the Response
return body
Example
def application(environ, start_response):# Let’s setup some data to send back to the clientbody = “WSGI is awesome and tastes delicious!”headers = [(“Content-Type”, “text/plain”),
(“Content-Length, str(len(body)))]
# Send response headers back to the clientstart_response(“200 OK”, headers)
# Send the body back to the client to complete the Response
return body
Example
def application(environ, start_response):# Let’s setup some data to send back to the clientbody = “WSGI is awesome and tastes delicious!”headers = [(“Content-Type”, “text/plain”),
(“Content-Length, str(len(body)))]
# Send response headers back to the clientstart_response(“200 OK”, headers)
# Send the body back to the client to complete the Response
return body
Arguments & Return Value
• environ: A dictionary (or Map if you’re from the Java land) of data about the incoming request, i.e. HTTP Headers, HTTP Host, etc.
{"HTTP_HOST": "surveymonkey.com", “HTTP_PATH”: “/surveys” "REQUEST_METHOD": "GET", …}
• start_response: A Python function supplied by the server that your app calls to send headers back to the client.
start_response("200 OK", headers)
• What your app returns will be the response body.return "<b>WSGI rules</b>"
WSGI Hosts
• Python comes with a built-in one that you can use, wsgiref, batteries included.
• Other WSGI Hosts:Apache mod_wsgiGoogle App EnginePaste (Python)uWsgi (Nginx, Cherokee, Apache)
WSGIRef Example
docs.python.org/library/wsgiref.html
run the following as “python server.py”
from wsgiref.simple_server import make_server
def application(environ, start_response):…. (from previous example)
# Server application on port 8000httpd = make_server(‘’, 8000, application)httpd.serve_forever() # run into process is killed
That’s it!
WSGI on the rocks
WSGI on the Rocks
Libraries providing basic abstraction over raw WSGI providing request and response classes:
• Webob (apt-get install python-webob or easy_install webob)
• Werkzeug
WebOb Example
from webob import Responsefrom webob.dec import wsgify
@wsgifydef hello_world_app(request):
response = Response(“Hello World”)return response
WebOb Example
from webob import Responsefrom webob.dec import wsgify
@wsgifydef hello_world_app(request):
response = Response(“Hello World”)return response
And now for somethingmore useful
WebOb URL Routing
@wsgifydef main_app(request):
path = request.environ[‘PATH_INFO’]if path in app_paths:
return app_paths[path](request)return response.status = 404
def foo_app(request):return Response(“Got into /foo”)
app_paths = {‘/foo’: foo_app, ‘/bar’: bar_app, …}
WebOb URL Routing
@wsgifydef main_app(request):
path = request.environ[‘PATH_INFO’]if path in app_paths:
return app_paths[path](request)return response.status = 404
def foo_app(request):return Response(“Got into /foo”)
app_paths = {‘/foo’: foo_app, ‘/bar’: bar_app, …}
WebOb URL Routing
@wsgifydef main_app(request):
path = request.environ[‘PATH_INFO’]if path in app_paths:
return app_paths[path](request)return response.status = 404
def foo_app(request):return Response(“Got into /foo”)
app_paths = {‘/foo’: foo_app, ‘/bar’: bar_app, …}
WebOb URL Routing
@wsgifydef main_app(request):
path = request.environ[‘PATH_INFO’] # ‘/foo’if path in app_paths:
return app_paths[path](request)return response.status = 404
def foo_app(request):return Response(“Got into /foo”)
app_paths = {‘/foo’: foo_app, ‘/bar’: bar_app, …}
WebOb URL Routing
@wsgifydef main_app(request):
path = request.environ[‘PATH_INFO’]if path in app_paths:
return app_paths[path](request)return response.status = 404
def foo_app(request):return Response(“Got into /foo”)
app_paths = {‘/foo’: foo_app, ‘/bar’: bar_app, …}
WebOb URL Routing
@wsgifydef main_app(request):
path = request.environ[‘PATH_INFO’]if path in app_paths:
return app_paths[path](request)return response.status = 404
def foo_app(request):return Response(“Got into /foo”)
app_paths = {‘/foo’: foo_app, ‘/bar’: bar_app, …}
Middleware
• What we just built, a routing interface, is a ‘middleware’.
• A middleware is a WSGI application that sits between the web server the your main application which can do pre and post processing on the request and response.
• Takes as input, the request object, and the next application to call.
Uppercase Middleware Example
def app(req): response = Response("The quick brown fox jumped over buzz.") return response
@wsgify.middlewaredef upper_middleware(request, app):
response = app(request)response.body = response.body.upper()
return response
wrapper_app = upper_middleware(app)httpd = make_server("", 8000, wrapper_app)
Uppercase Middleware Example
def app(req): response = Response("The quick brown fox jumped over buzz.") return response
@wsgify.middlewaredef upper_middleware(request, app):
response = app(request)response.body = response.body.upper()
return response
wrapper_app = upper_middleware(app)httpd = make_server("", 8000, wrapper_app)
Uppercase Middleware Example
def app(req): response = Response("The quick brown fox jumped over buzz.") return response
@wsgify.middlewaredef upper_middleware(request, app):
response = app(request)response.body = response.body.upper()
return response
wrapper_app = upper_middleware(app)httpd = make_server("", 8000, wrapper_app)
Gzip Middleware Example
def gzip_middleware(request, app):# Don’t want to do any pre-processing so just call the
appresponse = app(request)
# Compress the body and set headerresponse.body = gzip(request.body)response.headers[‘Content-Encoding’] = ‘gzip’
return response
Middleware Considerations
• Two things:• Middlewares are not aware of anything before or after them in
the ‘pipeline.’• Middlewares should be reusable.
• The middleware you write should act more like a filter as opposed to having a lot of business logic. Rule of thumb: if the code is specific to your application (not reusable), it should probably be a library and not a middleware.
That was so easy...
• A cavema... Err anyone can do it!
• There are dozens of web frameworks that are available (most are open-source) for your use which do a lot of the leg work for you.Examples:• Pylons/Pyramid (What we use at SurveyMonkey)• Django• TurboGears• Repoze.bfg• Bottle• Flask
Questions?