WSGI Middleware and WSGIKit +++++++++++++++++++++++++++ .. future: *techniques for advancing the state of the art in Python web programming* Ian Bicking Imaginary Landscape LLC www.imagescape.com WSGI Middleware and WSGIKit =========================== .. raw:: html 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 The Problem =========== .. raw:: html
Solutions ========= Solution 1 ---------- * We all realize that there is One True Framework * We ostrasize everyone who doesn't agree * The peer pressure / totalitarian solution Solutions ========= Solution 2 ---------- * Write a book *Choosing Your Python Web Framework* * The "document your crap and it will start to smell good" solution Solutions ========= Solution 3 ---------- .. image:: ./longhorn_logo.jpg :align: right * 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 The Solution ============ Restrictions of a Practical 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 The Solution ============ Directions for a Practical Solution ----------------------------------- * 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 The Solution ============ Strategy for a Practical Solution --------------------------------- * 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... The Solution ============ Positive Steps for a Practical Solution --------------------------------------- * 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 On WSGIKit ========== My Experience With WSGIKit... Alternate Titles ================ WSGIKit: Deconstructing a legacy framework into WSGI middleware --------------------------------------------------------------- * 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 Alternate Titles ================ WSGIKit: Creating a framework from a WSGI middleware stack ---------------------------------------------------------- * 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 Alternate Titles ================ WSGIKit: Utilizing WSGI as a platform for inter-framework cooperation --------------------------------------------------------------------- * Deconstucting any and all frameworks into WSGI components * Allowing applications to live beside other applications written for other APIs * WSGI as container and a communication medium Python web programming ====================== WSGI, and WSGIKit ----------------- *A strategy to make Python a compelling web development language* Foundation ========== What is a Foundation -------------------- * Decoupled libraries (few dependencies) * Stable Dependencies Principle * Testable libraries * Thoroughly documented Foundation ========== * 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) Foundation ========== What isn't a Foundation ----------------------- * Not a particular server * Minimal, not convenient * Robust, not pleasing * Concrete where possible * Explicit, not necessarily concise Part II: WSGI ============= What Is WSGI? ------------- * Web Server Gateway Interface * PEP 333, written by Phillip Eby * About 1 year old WSGI ==== WSGI as a Foundation -------------------- * 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 WSGI ==== WSGI: a brief explanation ------------------------- The one-minute introduction to WSGI, using WSGIKit and ``wsgikit.webkit`` as an example... (these slides abbreviate the standard) The Application =============== :: 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 The Server ========== :: 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`` The 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 environ =========== ``environ`` ----------- :: app(environ, start_response) * A dictionary * Most keys are like CGI environmental variables - ``"SCRIPT_NAME"`` - ``"PATH_INFO"`` - ``"QUERY_STRING"``, etc. * Some additional keys... environ =========== :: 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 environ =========== :: 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 start_response ================== ``start_response`` ------------------ :: 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 ['...'] WSGI as a Framework =================== * 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 WSGI Middleware =============== A simple example: .. image:: simple-middleware.png :align: center WSGI Middleware =============== .. image:: full-middleware.png :align: center WSGI Middleware =============== Middleware ---------- * Object that is a WSGI application * Delegates to another WSGI application * Acts as a server to that application WSGI Middleware =============== ``session`` ----------- * Fetches a session for each request * Saves session when request completes * Sets session ID (using a cookie) WSGI Middleware =============== ``session`` psuedocode ---------------------- :: 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 WSGI Middleware =============== Using ``session`` ----------------- :: def my_app(environ, start_response): ... session = environ['wsgikit.session'] session.save_value('username', 'bob') ... WSGI Middleware =============== ``urlparser`` ------------- * ``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 Middleware ========== Who Uses Middleware? -------------------- * 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 Presentation ============ Separating Logic From Presentation ---------------------------------- * APIs are the way frameworks present themselves * APIs are a user interface for programmers * Features are the logic underlying those APIs Presentation ============ WSGI As Logic Without Presentation ---------------------------------- * WSGI isn't pretty * WSGI doesn't have rounded corners * WSGI isn't exposed to "end users" (the majority of developers) Why? ==== Why Is This Interesting? ------------------------ * 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 Why? ==== * 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 Future ====== * 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 . =========================== WSGI Middleware and WSGIKit --------------------------- Using WSGI Middleware to build a foundation for Python web programming. :: Ian Bicking Imaginary Landscape Web Development imagescape.com