bor.borygmus

A programming weblog by Hao Lian. • A long walk through an angry forest. • A series of memory leaks brought on by senility.

All modern Python servers—except mod_python—talk to the application using the same pseudo-protocol called the Python Web Server Gateway Interface (WSGI).

Old-timies out there will recognize the parallels in naming the WSGI after the Common Gateway Interface, which was spectacularly simple. A browser connects to the web server. The web server runs perl website.pl or python website.py. Your HTTP request, headers and all, is sent through via standard input. Your HTTP response, headers and text and all, is sent through via standard output.

One process for every request. Simple. Slow.

Starting up an interpreter each time bogs you down. A better solution is to keep the interpreter around for multiple requests. Or, better yet, start a whole farm of fuzzy little interpreters. They run around, eagerly trying to fulfill your HTTP requests until they’re plump enough to be brutally decapitated in a mass orgy of death-worship in order to become your HTTP bacon sandwiches.

What to do

One solution is to stay with CGI but keep the processes around intelligently. This is the FastCGI model. You get to keep your old code.

Another solution is to embed the interpreter into the web server. This is the Apache module model.

Another solution is to design a new language from the ground up that’s intended to coexist with the web server and ignore all modern programming language practices at the expense of your purity of soul. This is the PHP model.

Now that you’ve solved that problem, what if you could replace standard input and standard output—streams—with functions and objects? One might say that you would invent something like WSGI.

Ants: Good segue.

Thanks, ants.

Anyway, with WSGI, now a web server (or a minimal layer above it) takes care of parsing HTTP requests and holds your hand while you make your response.

What is WSGI?

What, you can’t read the PEP?

What is WSGI?

It’s best to model WSGI as a conversation.

  • Web Server: I have a bunch of request variables (an environment) and a respond callback .
  • You: Could I have it?
  • Web Server: I don’t know. You look like a … (whispers) Rails developer.
  • You: Come on, I’m freezing out here. Python just warmed up some hot cocoa, and C++ is making sexy-eyes at me.
  • Web Server: (shudders) OK. You’ll need a box to hold this.

That box is a single function. Are you ready?

Writing your first WSGI application

An adventure wrapped in silk.

def app(environ, respond):
    anything you want

“Anything I want?” Not really. You have to return an iterable of strings.

def app(environ, respond):
    return 'Please free me!\n'

“Like this?” A single string is not iterable. What WSGI will see is this: [‘P’, ‘l’, ‘e’, ‘a’, ‘s’, ‘e’, ‘help me i am being forced to proofread this article’].

def app(environ, respond):
    return ['Seriously, rescue me!\n']

“Like this then?” No, you have to send some headers. And an HTTP status! The default HTTP status is ‘200 OK’ and let’s use this opportunity to tell the browser we’re text/plain. Not text/fancy. text/plain.

Headers must be an iterable of tuples.

def app(environ, respond):
    headers = [('Content-type', 'text/plain')]
    respond('200 OK', headers)
    return ['I have a wife and children!\n']

“Like this then?”

Yes. I’m ruffling your hair now, like the fatherly figure you never had.

All that remains is to tell your web server about this application with a file like httpd.conf or .htaccess or development.ini. Or you might pass it to an HTTP server class like a layer over BaseHTTPServer or CGIHandler().run or run_wsgi_app.

The manly server swoops in, makes up an environ and a respond callback, gives it to you, reads your return, and writes up your HTTP response.

Of course, we must completely destroy this function and replace it with something usable. But that’s to come.

Hooray.

[(January 23, 2009) .]

Abandon your ideas.

Use Markdown+, but not HTML. In code blocks, beware angle brackets.