Using WSGI Middleware to build a foundation for Python web programming.
http://ianbicking.org/docs/pycon2005
WSGI Open Space, Wednesday 5:30-6:00, Room 310
Ian Bicking
Imaginary Landscape Web Development
imagescape.com
- We all realize that there is One True Framework
- We ostrasize everyone who doesn't agree
- The peer pressure / totalitarian solution
- Write a book Choosing Your Python Web Framework
- The "document your crap and it will start to smell good" solution
- We give up on the web, (after all, it's just based on primitive
1970's technology), and focus on the future: Avalon
- The "when all you have is a hammer, screws are stupid" solution
- We can't make code go away
- We can't suppress the diversity of aesthetic and structural opinions
- We can't abandon current developers
- We can't continue with the present course
- We must support legacy interfaces
- We must support future new frameworks
- We must mitigate the cost of diversity
- Aesthetic opinions should not require functional compromises
- Stop building from the top down
- Stop focusing on points which separate us (aesthetics, application
structure, application modeling)
- If not stop, at least pause for a little while...
- Build from the bottom up
- Start at the lowest layer: the server
- Move upward with robust and aesthetically neutral libraries
- Each step, look for low hanging fruit
My Experience With WSGIKit...
- Starting with a legacy API (Webware)...
- And a codebase with lots of coupling...
- Recreated the API as a set of independent components with thin
WSGI-based glue
- WSGIKit is a full-featured framework from scratch
- "Framework" is a facade; the "middleware" does all the heavy lifting
- Using Webware API just a way to ignore API design
A strategy to make Python a compelling web development language
- Decoupled libraries (few dependencies)
- Stable Dependencies Principle
- Testable libraries
- Thoroughly documented
- Libraries that can be "complete"
- Limited and well-defined scope
- Not easy, just possible
- Avoids points of disagreement (like specific templating languages or
threading vs. processes)
- Not a particular server
- Minimal, not convenient
- Robust, not pleasing
- Concrete where possible
- Explicit, not necessarily concise
- Web Server Gateway Interface
- PEP 333, written by Phillip Eby
- About 1 year old
- It connects "servers" to "applications", e.g., Apache to Zope, or
Twisted to Webware
- WSGI is already a foundational API
- This presentation shows how it is a framework for other foundational
elements
The one-minute introduction to WSGI, using WSGIKit and
wsgikit.webkit as an example...
(these slides abbreviate the standard)
from wsgikit.webkit.wsgiwebkit import webkit
my_application = webkit('/path/to/webkit/root')
- webkit creates a WSGI application
- The standard applies only to that application, not its construction
from wsgikit.cgiserver import run_with_cgi
run_with_cgi(my_application)
- run_with_cgi just a gateway to turn any WSGI application into a
CGI application
- Other "servers" may need different information, like a port or host
- The standard applies to what run_with_cgi does with
my_application
Application objects are called:
def run_with_cgi(app):
....
app_iter = app(environ, start_response)
...
The application returns an iterator that produces the body of the
response
app(environ, start_response)
- A dictionary
- Most keys are like CGI environmental variables
- "SCRIPT_NAME"
- "PATH_INFO"
- "QUERY_STRING", etc.
- Some additional keys...
app(environ, start_response)
- "wsgi.input": a file-like object, the body of the request
- under CGI: sys.stdin
- Metadata about the request (e.g., if the environment is threaded),
a file for simple error logging
app(environ, start_response)
- Extensions can add new keys to the dictionary,
e.g. "wsgikit.session"
- There's no standard for what goes in those keys
app(environ, start_response)
Another callable. The application calls start_response:
def my_app(environ, start_response):
...
start_response('200 OK', [('Content-type', 'text/html')])
return ['<html>...']
- WSGI is a well-specified
- All the state is in environ or passed between functions
- The process serves as a pipeline
- No classes, no inheritance, everything explicit and transparent
A simple example:
- Object that is a WSGI application
- Delegates to another WSGI application
- Acts as a server to that application
- Fetches a session for each request
- Saves session when request completes
- Sets session ID (using a cookie)
def session_middleware(wrapped_app):
def replacement_app(environ, start_response):
session_obj = Session(environ)
environ['wsgikit.session'] = session_obj
def replacement_start_response(status, headers):
if session_obj.session_created:
headers.append(
('Set-Cookie', 'SID=' + session_obj.sid))
return start_response(status, headers)
return wrapped_app(environ, replacement_start_response)
return replacement_app
def my_app(environ, start_response):
...
session = environ['wsgikit.session']
session.save_value('username', 'bob')
...
- urlparser uses PATH_INFO to find the a WSGI application on
disk
- This one WSGI application delegates to many applications
- When an application is found SCRIPT_NAME and PATH_INFO are
rewritten to reflect the new context
- Framework programmers
- The current WSGIKit middleware was created because the Webware API
(and featureset) required it
- All access to the middleware is hidden in the Webware API
- Other framework authors can wrap the functionality with their native
conventions
- APIs are the way frameworks present themselves
- APIs are a user interface for programmers
- Features are the logic underlying those APIs
- WSGI isn't pretty
- WSGI doesn't have rounded corners
- WSGI isn't exposed to "end users" (the majority of developers)
- These components are hard to write in a cross-framework manner
- They rely on the request and response
- We have no standard request and response objects
- Different frameworks can live side by side
- Components can only communicate through the environ and other
well-defined channels
- Components must be decoupled
- Components can be tackled separately
- We can finally share work
- New standards, e.g., standardize an API for
environ["session_v1.factory"]
- More frameworks presented as WSGI components
- Leading to more intimate inter-framework cooperation
- WSGI servers that address commercial hosting needs
- Solutions can finally stick
Using WSGI Middleware to build a foundation for Python web programming.
Ian Bicking
Imaginary Landscape Web Development
imagescape.com