Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

PEP 333 – Python Web Server Gateway Interface v1.0

Author:
Phillip J. Eby <pje at telecommunity.com>
Discussions-To:
Web-SIG list
Status:
Final
Type:
Informational
Created:
07-Dec-2003
Post-History:
07-Dec-2003, 08-Aug-2004, 20-Aug-2004, 27-Aug-2004, 27-Sep-2010
Superseded-By:
3333

Table of Contents

Preface

Note: For an updated version of this spec that supports Python 3.x andincludes community errata, addenda, and clarifications, pleaseseePEP 3333 instead.

Abstract

This document specifies a proposed standard interface between webservers and Python web applications or frameworks, to promote webapplication portability across a variety of web servers.

Rationale and Goals

Python currently boasts a wide variety of web application frameworks,such as Zope, Quixote, Webware, SkunkWeb, PSO, and Twisted Web – toname just a few[1]. This wide variety of choices can be a problemfor new Python users, because generally speaking, their choice of webframework will limit their choice of usable web servers, and viceversa.

By contrast, although Java has just as many web application frameworksavailable, Java’s “servlet” API makes it possible for applicationswritten with any Java web application framework to run in any webserver that supports the servlet API.

The availability and widespread use of such an API in web servers forPython – whether those servers are written in Python (e.g. Medusa),embed Python (e.g. mod_python), or invoke Python via a gatewayprotocol (e.g. CGI, FastCGI, etc.) – would separate choice offramework from choice of web server, freeing users to choose a pairingthat suits them, while freeing framework and server developers tofocus on their preferred area of specialization.

This PEP, therefore, proposes a simple and universal interface betweenweb servers and web applications or frameworks: the Python Web ServerGateway Interface (WSGI).

But the mere existence of a WSGI spec does nothing to address theexisting state of servers and frameworks for Python web applications.Server and framework authors and maintainers must actually implementWSGI for there to be any effect.

However, since no existing servers or frameworks support WSGI, thereis little immediate reward for an author who implements WSGI support.Thus, WSGImust be easy to implement, so that an author’s initialinvestment in the interface can be reasonably low.

Thus, simplicity of implementation onboth the server and frameworksides of the interface is absolutely critical to the utility of theWSGI interface, and is therefore the principal criterion for anydesign decisions.

Note, however, that simplicity of implementation for a frameworkauthor is not the same thing as ease of use for a web applicationauthor. WSGI presents an absolutely “no frills” interface to theframework author, because bells and whistles like response objects andcookie handling would just get in the way of existing frameworks’handling of these issues. Again, the goal of WSGI is to facilitateeasy interconnection of existing servers and applications orframeworks, not to create a new web framework.

Note also that this goal precludes WSGI from requiring anything thatis not already available in deployed versions of Python. Therefore,new standard library modules are not proposed or required by thisspecification, and nothing in WSGI requires a Python version greaterthan 2.2.2. (It would be a good idea, however, for future versionsof Python to include support for this interface in web serversprovided by the standard library.)

In addition to ease of implementation for existing and futureframeworks and servers, it should also be easy to create requestpreprocessors, response postprocessors, and other WSGI-based“middleware” components that look like an application to theircontaining server, while acting as a server for their containedapplications.

If middleware can be both simple and robust, and WSGI is widelyavailable in servers and frameworks, it allows for the possibilityof an entirely new kind of Python web application framework: oneconsisting of loosely-coupled WSGI middleware components. Indeed,existing framework authors may even choose to refactor theirframeworks’ existing services to be provided in this way, becomingmore like libraries used with WSGI, and less like monolithicframeworks. This would then allow application developers to choose“best-of-breed” components for specific functionality, rather thanhaving to commit to all the pros and cons of a single framework.

Of course, as of this writing, that day is doubtless quite far off.In the meantime, it is a sufficient short-term goal for WSGI toenable the use of any framework with any server.

Finally, it should be mentioned that the current version of WSGIdoes not prescribe any particular mechanism for “deploying” anapplication for use with a web server or server gateway. At thepresent time, this is necessarily implementation-defined by theserver or gateway. After a sufficient number of servers andframeworks have implemented WSGI to provide field experience withvarying deployment requirements, it may make sense to createanother PEP, describing a deployment standard for WSGI servers andapplication frameworks.

Specification Overview

The WSGI interface has two sides: the “server” or “gateway” side, andthe “application” or “framework” side. The server side invokes acallable object that is provided by the application side. Thespecifics of how that object is provided are up to the server orgateway. It is assumed that some servers or gateways will require anapplication’s deployer to write a short script to create an instanceof the server or gateway, and supply it with the application object.Other servers and gateways may use configuration files or othermechanisms to specify where an application object should beimported from, or otherwise obtained.

In addition to “pure” servers/gateways and applications/frameworks,it is also possible to create “middleware” components that implementboth sides of this specification. Such components act as anapplication to their containing server, and as a server to acontained application, and can be used to provide extended APIs,content transformation, navigation, and other useful functions.

Throughout this specification, we will use the term “a callable” tomean “a function, method, class, or an instance with a__call__method”. It is up to the server, gateway, or application implementingthe callable to choose the appropriate implementation technique fortheir needs. Conversely, a server, gateway, or application that isinvoking a callablemust not have any dependency on what kind ofcallable was provided to it. Callables are only to be called, notintrospected upon.

The Application/Framework Side

The application object is simply a callable object that acceptstwo arguments. The term “object” should not be misconstrued asrequiring an actual object instance: a function, method, class,or instance with a__call__ method are all acceptable foruse as an application object. Application objects must be ableto be invoked more than once, as virtually all servers/gateways(other than CGI) will make such repeated requests.

(Note: although we refer to it as an “application” object, thisshould not be construed to mean that application developers will useWSGI as a web programming API! It is assumed that applicationdevelopers will continue to use existing, high-level frameworkservices to develop their applications. WSGI is a tool forframework and server developers, and is not intended to directlysupport application developers.)

Here are two example application objects; one is a function, and theother is a class:

defsimple_app(environ,start_response):"""Simplest possible application object"""status='200 OK'response_headers=[('Content-type','text/plain')]start_response(status,response_headers)return['Hello world!\n']classAppClass:"""Produce the same output, but using a class    (Note: 'AppClass' is the "application" here, so calling it    returns an instance of 'AppClass', which is then the iterable    return value of the "application callable" as required by    the spec.    If we wanted to use *instances* of 'AppClass' as application    objects instead, we would have to implement a '__call__'    method, which would be invoked to execute the application,    and we would need to create an instance for use by the    server or gateway.    """def__init__(self,environ,start_response):self.environ=environself.start=start_responsedef__iter__(self):status='200 OK'response_headers=[('Content-type','text/plain')]self.start(status,response_headers)yield"Hello world!\n"

The Server/Gateway Side

The server or gateway invokes the application callable once for eachrequest it receives from an HTTP client, that is directed at theapplication. To illustrate, here is a simple CGI gateway, implementedas a function taking an application object. Note that this simpleexample has limited error handling, because by default an uncaughtexception will be dumped tosys.stderr and logged by the webserver.

importos,sysdefrun_with_cgi(application):environ=dict(os.environ.items())environ['wsgi.input']=sys.stdinenviron['wsgi.errors']=sys.stderrenviron['wsgi.version']=(1,0)environ['wsgi.multithread']=Falseenviron['wsgi.multiprocess']=Trueenviron['wsgi.run_once']=Trueifenviron.get('HTTPS','off')in('on','1'):environ['wsgi.url_scheme']='https'else:environ['wsgi.url_scheme']='http'headers_set=[]headers_sent=[]defwrite(data):ifnotheaders_set:raiseAssertionError("write() before start_response()")elifnotheaders_sent:# Before the first output, send the stored headersstatus,response_headers=headers_sent[:]=headers_setsys.stdout.write('Status:%s\r\n'%status)forheaderinresponse_headers:sys.stdout.write('%s:%s\r\n'%header)sys.stdout.write('\r\n')sys.stdout.write(data)sys.stdout.flush()defstart_response(status,response_headers,exc_info=None):ifexc_info:try:ifheaders_sent:# Re-raise original exception if headers sentraiseexc_info[0],exc_info[1],exc_info[2]finally:exc_info=None# avoid dangling circular refelifheaders_set:raiseAssertionError("Headers already set!")headers_set[:]=[status,response_headers]returnwriteresult=application(environ,start_response)try:fordatainresult:ifdata:# don't send headers until body appearswrite(data)ifnotheaders_sent:write('')# send headers now if body was emptyfinally:ifhasattr(result,'close'):result.close()

Middleware: Components that Play Both Sides

Note that a single object may play the role of a server with respectto some application(s), while also acting as an application withrespect to some server(s). Such “middleware” components can performsuch functions as:

  • Routing a request to different application objects based on thetarget URL, after rewriting theenviron accordingly.
  • Allowing multiple applications or frameworks to run side by sidein the same process
  • Load balancing and remote processing, by forwarding requests andresponses over a network
  • Perform content postprocessing, such as applying XSL stylesheets

The presence of middleware in general is transparent to both the“server/gateway” and the “application/framework” sides of theinterface, and should require no special support. A user whodesires to incorporate middleware into an application simplyprovides the middleware component to the server, as if it werean application, and configures the middleware component toinvoke the application, as if the middleware component were aserver. Of course, the “application” that the middleware wrapsmay in fact be another middleware component wrapping anotherapplication, and so on, creating what is referred to as a“middleware stack”.

For the most part, middleware must conform to the restrictionsand requirements of both the server and application sides ofWSGI. In some cases, however, requirements for middlewareare more stringent than for a “pure” server or application,and these points will be noted in the specification.

Here is a (tongue-in-cheek) example of a middleware component thatconvertstext/plain responses to pig Latin, using Joe Strout’spiglatin.py. (Note: a “real” middleware component wouldprobably use a more robust way of checking the content type, andshould also check for a content encoding. Also, this simpleexample ignores the possibility that a word might be split acrossa block boundary.)

frompiglatinimportpiglatinclassLatinIter:"""Transform iterated output to piglatin, if it's okay to do so    Note that the "okayness" can change until the application yields    its first non-empty string, so 'transform_ok' has to be a mutable    truth value.    """def__init__(self,result,transform_ok):ifhasattr(result,'close'):self.close=result.closeself._next=iter(result).nextself.transform_ok=transform_okdef__iter__(self):returnselfdefnext(self):ifself.transform_ok:returnpiglatin(self._next())else:returnself._next()classLatinator:# by default, don't transform outputtransform=Falsedef__init__(self,application):self.application=applicationdef__call__(self,environ,start_response):transform_ok=[]defstart_latin(status,response_headers,exc_info=None):# Reset ok flag, in case this is a repeat calldeltransform_ok[:]forname,valueinresponse_headers:ifname.lower()=='content-type'andvalue=='text/plain':transform_ok.append(True)# Strip content-length if present, else it'll be wrongresponse_headers=[(name,value)forname,valueinresponse_headersifname.lower()!='content-length']breakwrite=start_response(status,response_headers,exc_info)iftransform_ok:defwrite_latin(data):write(piglatin(data))returnwrite_latinelse:returnwritereturnLatinIter(self.application(environ,start_latin),transform_ok)# Run foo_app under a Latinator's control, using the example CGI gatewayfromfoo_appimportfoo_apprun_with_cgi(Latinator(foo_app))

Specification Details

The application object must accept two positional arguments. Forthe sake of illustration, we have named themenviron andstart_response, but they are not required to have these names.A server or gatewaymust invoke the application object usingpositional (not keyword) arguments. (E.g. by callingresult=application(environ,start_response) as shown above.)

Theenviron parameter is a dictionary object, containing CGI-styleenvironment variables. This objectmust be a builtin Pythondictionary (not a subclass,UserDict or other dictionaryemulation), and the application is allowed to modify the dictionaryin any way it desires. The dictionary must also include certainWSGI-required variables (described in a later section), and mayalso include server-specific extension variables, named accordingto a convention that will be described below.

Thestart_response parameter is a callable accepting tworequired positional arguments, and one optional argument. For the sakeof illustration, we have named these argumentsstatus,response_headers, andexc_info, but they are not required tohave these names, and the applicationmust invoke thestart_response callable using positional arguments (e.g.start_response(status,response_headers)).

Thestatus parameter is a status string of the form"999Messagehere", andresponse_headers is a list of(header_name,header_value) tuples describing the HTTP responseheader. The optionalexc_info parameter is described below in thesections onThe start_response() Callable andError Handling.It is used only when the application has trapped an error and isattempting to display an error message to the browser.

Thestart_response callable must return awrite(body_data)callable that takes one positional parameter: a string to be writtenas part of the HTTP response body. (Note: thewrite() callable isprovided only to support certain existing frameworks’ imperative outputAPIs; it should not be used by new applications or frameworks if itcan be avoided. See theBuffering and Streaming section for moredetails.)

When called by the server, the application object must return aniterable yielding zero or more strings. This can be accomplished in avariety of ways, such as by returning a list of strings, or by theapplication being a generator function that yields strings, orby the application being a class whose instances are iterable.Regardless of how it is accomplished, the application object mustalways return an iterable yielding zero or more strings.

The server or gateway must transmit the yielded strings to the clientin an unbuffered fashion, completing the transmission of each stringbefore requesting another one. (In other words, applicationsshould perform their own buffering. See theBuffering andStreaming section below for more on how application output must behandled.)

The server or gateway should treat the yielded strings as binary bytesequences: in particular, it should ensure that line endings arenot altered. The application is responsible for ensuring that thestring(s) to be written are in a format suitable for the client. (Theserver or gatewaymay apply HTTP transfer encodings, or performother transformations for the purpose of implementing HTTP featuressuch as byte-range transmission. SeeOther HTTP Features, below,for more details.)

If a call tolen(iterable) succeeds, the server must be ableto rely on the result being accurate. That is, if the iterablereturned by the application provides a working__len__()method, itmust return an accurate result. (SeetheHandling the Content-Length Header section for informationon how this would normally be used.)

If the iterable returned by the application has aclose() method,the server or gatewaymust call that method upon completion of thecurrent request, whether the request was completed normally, orterminated early due to an error (this is to support resource releaseby the application). This protocol is intended to complementPEP 325’sgenerator support, and other common iterables withclose() methods.

(Note: the applicationmust invoke thestart_response()callable before the iterable yields its first body string, so that theserver can send the headers before any body content. However, thisinvocationmay be performed by the iterable’s first iteration, soserversmust not assume thatstart_response() has been calledbefore they begin iterating over the iterable.)

Finally, servers and gatewaysmust not directly use any otherattributes of the iterable returned by the application, unless it is aninstance of a type specific to that server or gateway, such as a “filewrapper” returned bywsgi.file_wrapper (seeOptionalPlatform-Specific File Handling). In the general case, onlyattributes specified here, or accessed via e.g. thePEP 234 iterationAPIs are acceptable.

environ Variables

Theenviron dictionary is required to contain these CGIenvironment variables, as defined by the Common Gateway Interfacespecification[2]. The following variablesmust be present,unless their value would be an empty string, in which case theymay be omitted, except as otherwise noted below.

REQUEST_METHOD
The HTTP request method, such as"GET" or"POST". Thiscannot ever be an empty string, and so is always required.
SCRIPT_NAME
The initial portion of the request URL’s “path” that corresponds tothe application object, so that the application knows its virtual“location”. Thismay be an empty string, if the applicationcorresponds to the “root” of the server.
PATH_INFO
The remainder of the request URL’s “path”, designating the virtual“location” of the request’s target within the application. Thismay be an empty string, if the request URL targets theapplication root and does not have a trailing slash.
QUERY_STRING
The portion of the request URL that follows the"?", if any.May be empty or absent.
CONTENT_TYPE
The contents of anyContent-Type fields in the HTTP request.May be empty or absent.
CONTENT_LENGTH
The contents of anyContent-Length fields in the HTTP request.May be empty or absent.
SERVER_NAME,SERVER_PORT
When combined withSCRIPT_NAME andPATH_INFO, these variablescan be used to complete the URL. Note, however, thatHTTP_HOST,if present, should be used in preference toSERVER_NAME forreconstructing the request URL. See theURL Reconstructionsection below for more detail.SERVER_NAME andSERVER_PORTcan never be empty strings, and so are always required.
SERVER_PROTOCOL
The version of the protocol the client used to send the request.Typically this will be something like"HTTP/1.0" or"HTTP/1.1"and may be used by the application to determine how to treat anyHTTP request headers. (This variable should probably be calledREQUEST_PROTOCOL, since it denotes the protocol used in therequest, and is not necessarily the protocol that will be used in theserver’s response. However, for compatibility with CGI we have tokeep the existing name.)
HTTP_ Variables
Variables corresponding to the client-supplied HTTP request headers(i.e., variables whose names begin with"HTTP_"). The presence orabsence of these variables should correspond with the presence orabsence of the appropriate HTTP header in the request.

A server or gatewayshould attempt to provide as many other CGIvariables as are applicable. In addition, if SSL is in use, the serveror gatewayshould also provide as many of the Apache SSL environmentvariables[3] as are applicable, such asHTTPS=on andSSL_PROTOCOL. Note, however, that an application that uses any CGIvariables other than the ones listed above are necessarily non-portableto web servers that do not support the relevant extensions. (Forexample, web servers that do not publish files will not be able toprovide a meaningfulDOCUMENT_ROOT orPATH_TRANSLATED.)

A WSGI-compliant server or gatewayshould document what variablesit provides, along with their definitions as appropriate. Applicationsshould check for the presence of any variables they require, andhave a fallback plan in the event such a variable is absent.

Note: missing variables (such asREMOTE_USER when noauthentication has occurred) should be left out of theenvirondictionary. Also note that CGI-defined variables must be strings,if they are present at all. It is a violation of this specificationfor a CGI variable’s value to be of any type other thanstr.

In addition to the CGI-defined variables, theenviron dictionarymay also contain arbitrary operating-system “environment variables”,andmust contain the following WSGI-defined variables:

VariableValue
wsgi.versionThe tuple(1,0), representing WSGIversion 1.0.
wsgi.url_schemeA string representing the “scheme” portion ofthe URL at which the application is beinginvoked. Normally, this will have the value"http" or"https", as appropriate.
wsgi.inputAn input stream (file-like object) from whichthe HTTP request body can be read. (The serveror gateway may perform reads on-demand asrequested by the application, or it maypre-read the client’s request body and bufferit in-memory or on disk, or use any othertechnique for providing such an input stream,according to its preference.)
wsgi.errorsAn output stream (file-like object) to whicherror output can be written, for the purpose ofrecording program or other errors in astandardized and possibly centralized location.This should be a “text mode” stream; i.e.,applications should use"\n" as a lineending, and assume that it will be converted tothe correct line ending by the server/gateway.

For many servers,wsgi.errors will be theserver’s main error log. Alternatively, thismay besys.stderr, or a log file of somesort. The server’s documentation shouldinclude an explanation of how to configure thisor where to find the recorded output. A serveror gateway may supply different error streamsto different applications, if this is desired.

wsgi.multithreadThis value should evaluate true if theapplication object may be simultaneouslyinvoked by another thread in the same process,and should evaluate false otherwise.
wsgi.multiprocessThis value should evaluate true if anequivalent application object may besimultaneously invoked by another process,and should evaluate false otherwise.
wsgi.run_onceThis value should evaluate true if the serveror gateway expects (but does not guarantee!)that the application will only be invoked thisone time during the life of its containingprocess. Normally, this will only be true fora gateway based on CGI (or something similar).

Finally, theenviron dictionary may also contain server-definedvariables. These variables should be named using only lower-caseletters, numbers, dots, and underscores, and should be prefixed witha name that is unique to the defining server or gateway. Forexample,mod_python might define variables with names likemod_python.some_variable.

Input and Error Streams

The input and error streams provided by the server must supportthe following methods:

MethodStreamNotes
read(size)input1
readline()input1, 2
readlines(hint)input1, 3
__iter__()input
flush()errors4
write(str)errors
writelines(seq)errors

The semantics of each method are as documented in the Python LibraryReference, except for these notes as listed in the table above:

  1. The server is not required to read past the client’s specifiedContent-Length, and is allowed to simulate an end-of-filecondition if the application attempts to read past that point.The applicationshould not attempt to read more data than isspecified by theCONTENT_LENGTH variable.
  2. The optional “size” argument toreadline() is not supported,as it may be complex for server authors to implement, and is notoften used in practice.
  3. Note that thehint argument toreadlines() is optional forboth caller and implementer. The application is free not tosupply it, and the server or gateway is free to ignore it.
  4. Since theerrors stream may not be rewound, servers and gatewaysare free to forward write operations immediately, without buffering.In this case, theflush() method may be a no-op. Portableapplications, however, cannot assume that output is unbufferedor thatflush() is a no-op. They must callflush() ifthey need to ensure that output has in fact been written. (Forexample, to minimize intermingling of data from multiple processeswriting to the same error log.)

The methods listed in the table abovemust be supported by allservers conforming to this specification. Applications conformingto this specificationmust not use any other methods or attributesof theinput orerrors objects. In particular, applicationsmust not attempt to close these streams, even if they possessclose() methods.

Thestart_response() Callable

The second parameter passed to the application object is a callableof the formstart_response(status,response_headers,exc_info=None).(As with all WSGI callables, the arguments must be suppliedpositionally, not by keyword.) Thestart_response callable isused to begin the HTTP response, and it must return awrite(body_data) callable (see theBuffering and Streamingsection, below).

Thestatus argument is an HTTP “status” string like"200OK"or"404NotFound". That is, it is a string consisting of aStatus-Code and a Reason-Phrase, in that order and separated by asingle space, with no surrounding whitespace or other characters.(SeeRFC 2616, Section 6.1.1 for more information.) The stringmust not contain control characters, and must not be terminatedwith a carriage return, linefeed, or combination thereof.

Theresponse_headers argument is a list of(header_name,header_value) tuples. It must be a Python list; i.e.type(response_headers)isListType, and the servermay changeits contents in any way it desires. Eachheader_name must be avalid HTTP header field-name (as defined byRFC 2616, Section 4.2),without a trailing colon or other punctuation.

Eachheader_valuemust not includeany control characters,including carriage returns or linefeeds, either embedded or at the end.(These requirements are to minimize the complexity of any parsing thatmust be performed by servers, gateways, and intermediate responseprocessors that need to inspect or modify response headers.)

In general, the server or gateway is responsible for ensuring thatcorrect headers are sent to the client: if the application omitsa header required by HTTP (or other relevant specifications that are ineffect), the server or gatewaymust add it. For example, the HTTPDate: andServer: headers would normally be supplied by theserver or gateway.

(A reminder for server/gateway authors: HTTP header names arecase-insensitive, so be sure to take that into consideration whenexamining application-supplied headers!)

Applications and middleware are forbidden from using HTTP/1.1“hop-by-hop” features or headers, any equivalent features in HTTP/1.0,or any headers that would affect the persistence of the client’sconnection to the web server. These features are theexclusive province of the actual web server, and a server or gatewayshould consider it a fatal error for an application to attemptsending them, and raise an error if they are supplied tostart_response(). (For more specifics on “hop-by-hop” features andheaders, please see theOther HTTP Features section below.)

Thestart_response callablemust not actually transmit theresponse headers. Instead, it must store them for the server orgateway to transmitonly after the first iteration of theapplication return value that yields a non-empty string, or uponthe application’s first invocation of thewrite() callable. Inother words, response headers must not be sent until there is actualbody data available, or until the application’s returned iterable isexhausted. (The only possible exception to this rule is if theresponse headers explicitly include aContent-Length of zero.)

This delaying of response header transmission is to ensure that bufferedand asynchronous applications can replace their originally intendedoutput with error output, up until the last possible moment. Forexample, the application may need to change the response status from“200 OK” to “500 Internal Error”, if an error occurs while the body isbeing generated within an application buffer.

Theexc_info argument, if supplied, must be a Pythonsys.exc_info() tuple. This argument should be supplied by theapplication only ifstart_response is being called by an errorhandler. Ifexc_info is supplied, and no HTTP headers have beenoutput yet,start_response should replace the currently-storedHTTP response headers with the newly-supplied ones, thus allowing theapplication to “change its mind” about the output when an error hasoccurred.

However, ifexc_info is provided, and the HTTP headers have alreadybeen sent,start_responsemust raise an error, andshouldraise theexc_info tuple. That is:

raiseexc_info[0],exc_info[1],exc_info[2]

This will re-raise the exception trapped by the application, and inprinciple should abort the application. (It is not safe for theapplication to attempt error output to the browser once the HTTPheaders have already been sent.) The applicationmust not trapany exceptions raised bystart_response, if it calledstart_response withexc_info. Instead, it should allowsuch exceptions to propagate back to the server or gateway. SeeError Handling below, for more details.

The applicationmay callstart_response more than once, if andonly if theexc_info argument is provided. More precisely, it isa fatal error to callstart_response without theexc_infoargument ifstart_response has already been called within thecurrent invocation of the application. (See the example CGIgateway above for an illustration of the correct logic.)

Note: servers, gateways, or middleware implementingstart_responseshould ensure that no reference is held to theexc_infoparameter beyond the duration of the function’s execution, to avoidcreating a circular reference through the traceback and framesinvolved. The simplest way to do this is something like:

defstart_response(status,response_headers,exc_info=None):ifexc_info:try:# do stuff w/exc_info herefinally:exc_info=None# Avoid circular ref.

The example CGI gateway provides another illustration of thistechnique.

Handling theContent-Length Header

If the application does not supply aContent-Length header, aserver or gateway may choose one of several approaches to handlingit. The simplest of these is to close the client connection whenthe response is completed.

Under some circumstances, however, the server or gateway may beable to either generate aContent-Length header, or at leastavoid the need to close the client connection. If the applicationdoesnot call thewrite() callable, and returns an iterablewhoselen() is 1, then the server can automatically determineContent-Length by taking the length of the first string yieldedby the iterable.

And, if the server and client both support HTTP/1.1“chunked encoding”,then the servermay use chunked encoding to senda chunk for eachwrite() call or string yielded by the iterable,thus generating aContent-Length header for each chunk. Thisallows the server to keep the client connection alive, if it wishesto do so. Note that the servermust comply fully withRFC 2616when doing this, or else fall back to one of the other strategies fordealing with the absence ofContent-Length.

(Note: applications and middlewaremust not apply any kind ofTransfer-Encoding to their output, such as chunking or gzipping;as “hop-by-hop” operations, these encodings are the province of theactual web server/gateway. SeeOther HTTP Features below, formore details.)

Buffering and Streaming

Generally speaking, applications will achieve the best throughputby buffering their (modestly-sized) output and sending it all atonce. This is a common approach in existing frameworks such asZope: the output is buffered in a StringIO or similar object, thentransmitted all at once, along with the response headers.

The corresponding approach in WSGI is for the application to simplyreturn a single-element iterable (such as a list) containing theresponse body as a single string. This is the recommended approachfor the vast majority of application functions, that renderHTML pages whose text easily fits in memory.

For large files, however, or for specialized uses of HTTP streaming(such as multipart “server push”), an application may need to provideoutput in smaller blocks (e.g. to avoid loading a large file intomemory). It’s also sometimes the case that part of a response maybe time-consuming to produce, but it would be useful to send ahead theportion of the response that precedes it.

In these cases, applications will usually return an iterator (oftena generator-iterator) that produces the output in a block-by-blockfashion. These blocks may be broken to coincide with multipartboundaries (for “server push”), or just before time-consumingtasks (such as reading another block of an on-disk file).

WSGI servers, gateways, and middlewaremust not delay thetransmission of any block; theymust either fully transmitthe block to the client, or guarantee that they will continuetransmission even while the application is producing its next block.A server/gateway or middleware may provide this guarantee in one ofthree ways:

  1. Send the entire block to the operating system (and requestthat any O/S buffers be flushed) before returning controlto the application, OR
  2. Use a different thread to ensure that the block continuesto be transmitted while the application produces the nextblock.
  3. (Middleware only) send the entire block to its parentgateway/server

By providing this guarantee, WSGI allows applications to ensurethat transmission will not become stalled at an arbitrary pointin their output data. This is critical for proper functioningof e.g. multipart “server push” streaming, where data betweenmultipart boundaries should be transmitted in full to the client.

Middleware Handling of Block Boundaries

In order to better support asynchronous applications and servers,middleware componentsmust not block iteration waiting formultiple values from an application iterable. If the middlewareneeds to accumulate more data from the application before it canproduce any output, itmust yield an empty string.

To put this requirement another way, a middleware componentmustyield at least one value each time its underlying applicationyields a value. If the middleware cannot yield any other value,it must yield an empty string.

This requirement ensures that asynchronous applications and serverscan conspire to reduce the number of threads that are requiredto run a given number of application instances simultaneously.

Note also that this requirement means that middlewaremustreturn an iterable as soon as its underlying application returnsan iterable. It is also forbidden for middleware to use thewrite() callable to transmit data that is yielded by anunderlying application. Middleware may only use their parentserver’swrite() callable to transmit data that theunderlying application sent using a middleware-providedwrite()callable.

Thewrite() Callable

Some existing application framework APIs support unbufferedoutput in a different manner than WSGI. Specifically, theyprovide a “write” function or method of some kind to writean unbuffered block of data, or else they provide a buffered“write” function and a “flush” mechanism to flush the buffer.

Unfortunately, such APIs cannot be implemented in terms ofWSGI’s “iterable” application return value, unless threadsor other special mechanisms are used.

Therefore, to allow these frameworks to continue using animperative API, WSGI includes a specialwrite() callable,returned by thestart_response callable.

New WSGI applications and frameworksshould not use thewrite() callable if it is possible to avoid doing so. Thewrite() callable is strictly a hack to support imperativestreaming APIs. In general, applications should produce theiroutput via their returned iterable, as this makes it possiblefor web servers to interleave other tasks in the same Python thread,potentially providing better throughput for the server as a whole.

Thewrite() callable is returned by thestart_response()callable, and it accepts a single parameter: a string to bewritten as part of the HTTP response body, that is treated exactlyas though it had been yielded by the output iterable. In otherwords, beforewrite() returns, it must guarantee that thepassed-in string was either completely sent to the client, orthat it is buffered for transmission while the applicationproceeds onward.

An applicationmust return an iterable object, even if ituseswrite() to produce all or part of its response body.The returned iterablemay be empty (i.e. yield no non-emptystrings), but if itdoes yield non-empty strings, that outputmust be treated normally by the server or gateway (i.e., it must besent or queued immediately). Applicationsmust not invokewrite() from within their return iterable, and therefore anystrings yielded by the iterable are transmitted after all stringspassed towrite() have been sent to the client.

Unicode Issues

HTTP does not directly support Unicode, and neither does thisinterface. All encoding/decoding must be handled by the application;all strings passed to or from the server must be standard Python bytestrings, not Unicode objects. The result of using a Unicode objectwhere a string object is required, is undefined.

Note also that strings passed tostart_response() as a status oras response headersmust followRFC 2616 with respect to encoding.That is, they must either be ISO-8859-1 characters, or useRFC 2047MIME encoding.

On Python platforms where thestr orStringType type is infact Unicode-based (e.g. Jython, IronPython, Python 3000, etc.), all“strings” referred to in this specification must contain onlycode points representable in ISO-8859-1 encoding (\u0000 through\u00FF, inclusive). It is a fatal error for an application tosupply strings containing any other Unicode character or code point.Similarly, servers and gatewaysmust not supplystrings to an application containing any other Unicode characters.

Again, all strings referred to in this specificationmust beof typestr orStringType, andmust not be of typeunicode orUnicodeType. And, even if a given platform allowsfor more than 8 bits per character instr/StringType objects,only the lower 8 bits may be used, for any value referred to inthis specification as a “string”.

Error Handling

In general, applicationsshould try to trap their own, internalerrors, and display a helpful message in the browser. (It is upto the application to decide what “helpful” means in this context.)

However, to display such a message, the application must not haveactually sent any data to the browser yet, or else it risks corruptingthe response. WSGI therefore provides a mechanism to either allow theapplication to send its error message, or be automatically aborted:theexc_info argument tostart_response. Here is an exampleof its use:

try:# regular application code herestatus="200 Froody"response_headers=[("content-type","text/plain")]start_response(status,response_headers)return["normal body goes here"]except:# XXX should trap runtime issues like MemoryError, KeyboardInterrupt#     in a separate handler before this bare 'except:'...status="500 Oops"response_headers=[("content-type","text/plain")]start_response(status,response_headers,sys.exc_info())return["error body goes here"]

If no output has been written when an exception occurs, the call tostart_response will return normally, and the application willreturn an error body to be sent to the browser. However, if any outputhas already been sent to the browser,start_response will reraisethe provided exception. This exceptionshould not be trapped bythe application, and so the application will abort. The server orgateway can then trap this (fatal) exception and abort the response.

Serversshould trap and log any exception that aborts anapplication or the iteration of its return value. If a partialresponse has already been written to the browser when an applicationerror occurs, the server or gatewaymay attempt to add an errormessage to the output, if the already-sent headers indicate atext/* content type that the server knows how to modify cleanly.

Some middleware may wish to provide additional exception handlingservices, or intercept and replace application error messages. Insuch cases, middleware may choose tonot re-raise theexc_infosupplied tostart_response, but instead raise a middleware-specificexception, or simply return without an exception after storing thesupplied arguments. This will then cause the application to returnits error body iterable (or invokewrite()), allowing the middlewareto capture and modify the error output. These techniques will work aslong as application authors:

  1. Always provideexc_info when beginning an error response
  2. Never trap errors raised bystart_response whenexc_info isbeing provided

HTTP 1.1 Expect/Continue

Servers and gateways that implement HTTP 1.1must providetransparent support for HTTP 1.1’s “expect/continue” mechanism. Thismay be done in any of several ways:

  1. Respond to requests containing anExpect:100-continue requestwith an immediate “100 Continue” response, and proceed normally.
  2. Proceed with the request normally, but provide the applicationwith awsgi.input stream that will send the “100 Continue”response if/when the application first attempts to read from theinput stream. The read request must then remain blocked until theclient responds.
  3. Wait until the client decides that the server does not supportexpect/continue, and sends the request body on its own. (Thisis suboptimal, and is not recommended.)

Note that these behavior restrictions do not apply for HTTP 1.0requests, or for requests that are not directed to an applicationobject. For more information on HTTP 1.1 Expect/Continue, seeRFC 2616, sections 8.2.3 and 10.1.1.

Other HTTP Features

In general, servers and gateways should “play dumb” and allow theapplication complete control over its output. They should only makechanges that do not alter the effective semantics of the application’sresponse. It is always possible for the application developer to addmiddleware components to supply additional features, so server/gatewaydevelopers should be conservative in their implementation. In a sense,a server should consider itself to be like an HTTP “gateway server”,with the application being an HTTP “origin server”. (SeeRFC 2616,section 1.3, for the definition of these terms.)

However, because WSGI servers and applications do not communicate viaHTTP, whatRFC 2616 calls “hop-by-hop” headers do not apply to WSGIinternal communications. WSGI applicationsmust not generate any“hop-by-hop” headers,attempt to use HTTP features that wouldrequire them to generate such headers, or rely on the content ofany incoming “hop-by-hop” headers in theenviron dictionary.WSGI serversmust handle any supported inbound “hop-by-hop” headerson their own, such as by decoding any inboundTransfer-Encoding,including chunked encoding if applicable.

Applying these principles to a variety of HTTP features, it should beclear that a servermay handle cache validation via theIf-None-Match andIf-Modified-Since request headers and theLast-Modified andETag response headers. However, it isnot required to do this, and the applicationshould perform itsown cache validation if it wants to support that feature, sincethe server/gateway is not required to do such validation.

Similarly, a servermay re-encode or transport-encode anapplication’s response, but the applicationshould use asuitable content encoding on its own, andmust not apply atransport encoding. A servermay transmit byte ranges of theapplication’s response if requested by the client, and theapplication doesn’t natively support byte ranges. Again, however,the applicationshould perform this function on its own if desired.

Note that these restrictions on applications do not necessarily meanthat every application must reimplement every HTTP feature; many HTTPfeatures can be partially or fully implemented by middlewarecomponents, thus freeing both server and application authors fromimplementing the same features over and over again.

Thread Support

Thread support, or lack thereof, is also server-dependent.Servers that can run multiple requests in parallel,should alsoprovide the option of running an application in a single-threadedfashion, so that applications or frameworks that are not thread-safemay still be used with that server.

Implementation/Application Notes

Server Extension APIs

Some server authors may wish to expose more advanced APIs, thatapplication or framework authors can use for specialized purposes.For example, a gateway based onmod_python might wish to exposepart of the Apache API as a WSGI extension.

In the simplest case, this requires nothing more than defining anenviron variable, such asmod_python.some_api. But, in manycases, the possible presence of middleware can make this difficult.For example, an API that offers access to the same HTTP headers thatare found inenviron variables, might return different data ifenviron has been modified by middleware.

In general, any extension API that duplicates, supplants, or bypassessome portion of WSGI functionality runs the risk of being incompatiblewith middleware components. Server/gateway developers shouldnotassume that nobody will use middleware, because some frameworkdevelopers specifically intend to organize or reorganize theirframeworks to function almost entirely as middleware of various kinds.

So, to provide maximum compatibility, servers and gateways thatprovide extension APIs that replace some WSGI functionality,mustdesign those APIs so that they are invoked using the portion of theAPI that they replace. For example, an extension API to access HTTPrequest headers must require the application to pass in its currentenviron, so that the server/gateway may verify that HTTP headersaccessible via the API have not been altered by middleware. If theextension API cannot guarantee that it will always agree withenviron about the contents of HTTP headers, it must refuse serviceto the application, e.g. by raising an error, returningNoneinstead of a header collection, or whatever is appropriate to the API.

Similarly, if an extension API provides an alternate means of writingresponse data or headers, it should require thestart_responsecallable to be passed in, before the application can obtain theextended service. If the object passed in is not the same one thatthe server/gateway originally supplied to the application, it cannotguarantee correct operation and must refuse to provide the extendedservice to the application.

These guidelines also apply to middleware that adds information suchas parsed cookies, form variables, sessions, and the like toenviron. Specifically, such middleware should provide thesefeatures as functions which operate onenviron, rather than simplystuffing values intoenviron. This helps ensure that informationis calculated fromenvironafter any middleware has done any URLrewrites or otherenviron modifications.

It is very important that these “safe extension” rules be followed byboth server/gateway and middleware developers, in order to avoid afuture in which middleware developers are forced to delete any and allextension APIs fromenviron to ensure that their mediation isn’tbeing bypassed by applications using those extensions!

Application Configuration

This specification does not define how a server selects or obtains anapplication to invoke. These and other configuration options arehighly server-specific matters. It is expected that server/gatewayauthors will document how to configure the server to execute aparticular application object, and with what options (such asthreading options).

Framework authors, on the other hand, should document how to create anapplication object that wraps their framework’s functionality. Theuser, who has chosen both the server and the application framework,must connect the two together. However, since both the framework andthe server now have a common interface, this should be merely amechanical matter, rather than a significant engineering effort foreach new server/framework pair.

Finally, some applications, frameworks, and middleware may wish touse theenviron dictionary to receive simple string configurationoptions. Servers and gatewaysshould support this by allowingan application’s deployer to specify name-value pairs to be placed inenviron. In the simplest case, this support can consist merely ofcopying all operating system-supplied environment variables fromos.environ into theenviron dictionary, since the deployer inprinciple can configure these externally to the server, or in theCGI case they may be able to be set via the server’s configurationfiles.

Applicationsshould try to keep such required variables to aminimum, since not all servers will support easy configuration ofthem. Of course, even in the worst case, persons deploying anapplication can create a script to supply the necessary configurationvalues:

fromthe_appimportapplicationdefnew_app(environ,start_response):environ['the_app.configval1']='something'returnapplication(environ,start_response)

But, most existing applications and frameworks will probably only needa single configuration value fromenviron, to indicate the locationof their application or framework-specific configuration file(s). (Ofcourse, applications should cache such configuration, to avoid havingto re-read it upon each invocation.)

URL Reconstruction

If an application wishes to reconstruct a request’s complete URL, itmay do so using the following algorithm, contributed by Ian Bicking:

fromurllibimportquoteurl=environ['wsgi.url_scheme']+'://'ifenviron.get('HTTP_HOST'):url+=environ['HTTP_HOST']else:url+=environ['SERVER_NAME']ifenviron['wsgi.url_scheme']=='https':ifenviron['SERVER_PORT']!='443':url+=':'+environ['SERVER_PORT']else:ifenviron['SERVER_PORT']!='80':url+=':'+environ['SERVER_PORT']url+=quote(environ.get('SCRIPT_NAME',''))url+=quote(environ.get('PATH_INFO',''))ifenviron.get('QUERY_STRING'):url+='?'+environ['QUERY_STRING']

Note that such a reconstructed URL may not be precisely the same URIas requested by the client. Server rewrite rules, for example, mayhave modified the client’s originally requested URL to place it in acanonical form.

Supporting Older (<2.2) Versions of Python

Some servers, gateways, or applications may wish to support older(<2.2) versions of Python. This is especially important if Jythonis a target platform, since as of this writing a production-readyversion of Jython 2.2 is not yet available.

For servers and gateways, this is relatively straightforward:servers and gateways targeting pre-2.2 versions of Python mustsimply restrict themselves to using only a standard “for” loop toiterate over any iterable returned by an application. This is theonly way to ensure source-level compatibility with both the pre-2.2iterator protocol (discussed further below) and “today’s” iteratorprotocol (seePEP 234).

(Note that this technique necessarily applies only to servers,gateways, or middleware that are written in Python. Discussion ofhow to use iterator protocol(s) correctly from other languages isoutside the scope of this PEP.)

For applications, supporting pre-2.2 versions of Python is slightlymore complex:

  • You may not return a file object and expect it to work as an iterable,since before Python 2.2, files were not iterable. (In general, youshouldn’t do this anyway, because it will perform quite poorly mostof the time!) Usewsgi.file_wrapper or an application-specificfile wrapper class. (SeeOptional Platform-Specific File Handlingfor more onwsgi.file_wrapper, and an example class you can useto wrap a file as an iterable.)
  • If you return a custom iterable, itmust implement the pre-2.2iterator protocol. That is, provide a__getitem__ method thataccepts an integer key, and raisesIndexError when exhausted.(Note that built-in sequence types are also acceptable, since theyalso implement this protocol.)

Finally, middleware that wishes to support pre-2.2 versions of Python,and iterates over application return values or itself returns aniterable (or both), must follow the appropriate recommendations above.

(Note: It should go without saying that to support pre-2.2 versionsof Python, any server, gateway, application, or middleware must alsouse only language features available in the target version, use1 and 0 instead ofTrue andFalse, etc.)

Optional Platform-Specific File Handling

Some operating environments provide special high-performancefile-transmission facilities, such as the Unixsendfile() call.Servers and gatewaysmay expose this functionality via an optionalwsgi.file_wrapper key in theenviron. An applicationmay use this “file wrapper” to convert a file or file-like objectinto an iterable that it then returns, e.g.:

if'wsgi.file_wrapper'inenviron:returnenviron['wsgi.file_wrapper'](filelike,block_size)else:returniter(lambda:filelike.read(block_size),'')

If the server or gateway supplieswsgi.file_wrapper, it must bea callable that accepts one required positional parameter, and oneoptional positional parameter. The first parameter is the file-likeobject to be sent, and the second parameter is an optional blocksize “suggestion” (which the server/gateway need not use). Thecallablemust return an iterable object, andmust not performany data transmission until and unless the server/gateway actuallyreceives the iterable as a return value from the application.(To do otherwise would prevent middleware from being able to interpretor override the response data.)

To be considered “file-like”, the object supplied by the applicationmust have aread() method that takes an optional size argument.Itmay have aclose() method, and if so, the iterable returnedbywsgi.file_wrappermust have aclose() method thatinvokes the original file-like object’sclose() method. If the“file-like” object has any other methods or attributes with namesmatching those of Python built-in file objects (e.g.fileno()),thewsgi.file_wrappermay assume that these methods orattributes have the same semantics as those of a built-in file object.

The actual implementation of any platform-specific file handlingmust occurafter the application returns, and the server orgateway checks to see if a wrapper object was returned. (Again,because of the presence of middleware, error handlers, and the like,it is not guaranteed that any wrapper created will actually be used.)

Apart from the handling ofclose(), the semantics of returning afile wrapper from the application should be the same as if theapplication had returnediter(filelike.read,''). In other words,transmission should begin at the current position within the “file”at the time that transmission begins, and continue until the end isreached.

Of course, platform-specific file transmission APIs don’t usuallyaccept arbitrary “file-like” objects. Therefore, awsgi.file_wrapper has to introspect the supplied object forthings such as afileno() (Unix-like OSes) or ajava.nio.FileChannel (under Jython) in order to determine ifthe file-like object is suitable for use with the platform-specificAPI it supports.

Note that even if the object isnot suitable for the platform API,thewsgi.file_wrappermust still return an iterable that wrapsread() andclose(), so that applications using file wrappersare portable across platforms. Here’s a simple platform-agnosticfile wrapper class, suitable for old (pre 2.2) and new Pythons alike:

classFileWrapper:def__init__(self,filelike,blksize=8192):self.filelike=filelikeself.blksize=blksizeifhasattr(filelike,'close'):self.close=filelike.closedef__getitem__(self,key):data=self.filelike.read(self.blksize)ifdata:returndataraiseIndexError

and here is a snippet from a server/gateway that uses it to provideaccess to a platform-specific API:

environ['wsgi.file_wrapper']=FileWrapperresult=application(environ,start_response)try:ifisinstance(result,FileWrapper):# check if result.filelike is usable w/platform-specific# API, and if so, use that API to transmit the result.# If not, fall through to normal iterable handling# loop below.fordatainresult:# etc.finally:ifhasattr(result,'close'):result.close()

Questions and Answers

  1. Why mustenviron be a dictionary? What’s wrong with using asubclass?

    The rationale for requiring a dictionary is to maximize portabilitybetween servers. The alternative would be to define some subset ofa dictionary’s methods as being the standard and portableinterface. In practice, however, most servers will probably find adictionary adequate to their needs, and thus framework authors willcome to expect the full set of dictionary features to be available,since they will be there more often than not. But, if some serverchoosesnot to use a dictionary, then there will beinteroperability problems despite that server’s “conformance” tospec. Therefore, making a dictionary mandatory simplifies thespecification and guarantees interoperability.

    Note that this does not prevent server or framework developers fromoffering specialized services as custom variablesinside theenviron dictionary. This is the recommended approach foroffering any such value-added services.

  2. Why can you callwrite()and yield strings/return aniterable? Shouldn’t we pick just one way?

    If we supported only the iteration approach, then currentframeworks that assume the availability of “push” suffer. But, ifwe only support pushing viawrite(), then server performancesuffers for transmission of e.g. large files (if a worker threadcan’t begin work on a new request until all of the output has beensent). Thus, this compromise allows an application framework tosupport both approaches, as appropriate, but with only a littlemore burden to the server implementor than a push-only approachwould require.

  3. What’s theclose() for?

    When writes are done during the execution of an applicationobject, the application can ensure that resources are releasedusing a try/finally block. But, if the application returns aniterable, any resources used will not be released until theiterable is garbage collected. Theclose() idiom allows anapplication to release critical resources at the end of a request,and it’s forward-compatible with the support for try/finally ingenerators that’s proposed byPEP 325.

  4. Why is this interface so low-level? I want feature X! (e.g.cookies, sessions, persistence, …)

    This isn’t Yet Another Python Web Framework. It’s just a way forframeworks to talk to web servers, and vice versa. If you wantthese features, you need to pick a web framework that provides thefeatures you want. And if that framework lets you create a WSGIapplication, you should be able to run it in most WSGI-supportingservers. Also, some WSGI servers may offer additional services viaobjects provided in theirenviron dictionary; see theapplicable server documentation for details. (Of course,applications that use such extensions will not be portable to otherWSGI-based servers.)

  5. Why use CGI variables instead of good old HTTP headers? And whymix them in with WSGI-defined variables?

    Many existing web frameworks are built heavily upon the CGI spec,and existing web servers know how to generate CGI variables. Incontrast, alternative ways of representing inbound HTTP informationare fragmented and lack market share. Thus, using the CGI“standard” seems like a good way to leverage existingimplementations. As for mixing them with WSGI variables,separating them would just require two dictionary arguments to bepassed around, while providing no real benefits.

  6. What about the status string? Can’t we just use the number,passing in200 instead of"200OK"?

    Doing this would complicate the server or gateway, by requiringthem to have a table of numeric statuses and correspondingmessages. By contrast, it is easy for an application or frameworkauthor to type the extra text to go with the specific response codethey are using, and existing frameworks often already have a tablecontaining the needed messages. So, on balance it seems better tomake the application/framework responsible, rather than the serveror gateway.

  7. Why iswsgi.run_once not guaranteed to run the app only once?

    Because it’s merely a suggestion to the application that it should“rig for infrequent running”. This is intended for applicationframeworks that have multiple modes of operation for caching,sessions, and so forth. In a “multiple run” mode, such frameworksmay preload caches, and may not write e.g. logs or session data todisk after each request. In “single run” mode, such frameworksavoid preloading and flush all necessary writes after each request.

    However, in order to test an application or framework to verifycorrect operation in the latter mode, it may be necessary (or atleast expedient) to invoke it more than once. Therefore, anapplication should not assume that it will definitely not be runagain, just because it is called withwsgi.run_once set toTrue.

  8. Feature X (dictionaries, callables, etc.) are ugly for use inapplication code; why don’t we use objects instead?

    All of these implementation choices of WSGI are specificallyintended todecouple features from one another; recombining thesefeatures into encapsulated objects makes it somewhat harder towrite servers or gateways, and an order of magnitude harder towrite middleware that replaces or modifies only small portions ofthe overall functionality.

    In essence, middleware wants to have a “Chain of Responsibility”pattern, whereby it can act as a “handler” for some functions,while allowing others to remain unchanged. This is difficult to dowith ordinary Python objects, if the interface is to remainextensible. For example, one must use__getattr__ or__getattribute__ overrides, to ensure that extensions (such asattributes defined by future WSGI versions) are passed through.

    This type of code is notoriously difficult to get 100% correct, andfew people will want to write it themselves. They will thereforecopy other people’s implementations, but fail to update them whenthe person they copied from corrects yet another corner case.

    Further, this necessary boilerplate would be pure excise, adeveloper tax paid by middleware developers to support a slightlyprettier API for application framework developers. But,application framework developers will typically only be updatingone framework to support WSGI, and in a very limited part oftheir framework as a whole. It will likely be their first (andmaybe their only) WSGI implementation, and thus they will likelyimplement with this specification ready to hand. Thus, the effortof making the API “prettier” with object attributes and suchlikewould likely be wasted for this audience.

    We encourage those who want a prettier (or otherwise improved) WSGIinterface for use in direct web application programming (as opposedto web framework development) to develop APIs or frameworks thatwrap WSGI for convenient use by application developers. In thisway, WSGI can remain conveniently low-level for server andmiddleware authors, while not being “ugly” for applicationdevelopers.

Proposed/Under Discussion

These items are currently being discussed on the Web-SIG and elsewhere,or are on the PEP author’s “to-do” list:

  • Shouldwsgi.input be an iterator instead of a file? This wouldhelp for asynchronous applications and chunked-encoding inputstreams.
  • Optional extensions are being discussed for pausing iteration of anapplication’s output until input is available or until a callbackoccurs.
  • Add a section about synchronous vs. asynchronous apps and servers,the relevant threading models, and issues/design goals in theseareas.

Acknowledgements

Thanks go to the many folks on the Web-SIG mailing list whosethoughtful feedback made this revised draft possible. Especially:

  • Gregory “Grisha” Trubetskoy, author ofmod_python, who beat upon the first draft as not offering any advantages over “plain oldCGI”, thus encouraging me to look for a better approach.
  • Ian Bicking, who helped nag me into properly specifying themultithreading and multiprocess options, as well as badgering me toprovide a mechanism for servers to supply custom extension data toan application.
  • Tony Lownds, who came up with the concept of astart_responsefunction that took the status and headers, returning awritefunction. His input also guided the design of the exception handlingfacilities, especially in the area of allowing for middleware thatoverrides application error messages.
  • Alan Kennedy, whose courageous attempts to implement WSGI-on-Jython(well before the spec was finalized) helped to shape the “supportingolder versions of Python” section, as well as the optionalwsgi.file_wrapper facility.
  • Mark Nottingham, who reviewed the spec extensively for issues withHTTP RFC compliance, especially with regard to HTTP/1.1 features thatI didn’t even know existed until he pointed them out.

References

[1]
The Python Wiki “Web Programming” topic(http://www.python.org/cgi-bin/moinmoin/WebProgramming)
[2]
The Common Gateway Interface Specification, v 1.1, 3rd Draft(https://datatracker.ietf.org/doc/html/draft-coar-cgi-v11-03)
[3]
mod_ssl Reference, “Environment Variables”(http://www.modssl.org/docs/2.8/ssl_reference.html#ToC25)

Copyright

This document has been placed in the public domain.


Source:https://github.com/python/peps/blob/main/peps/pep-0333.rst

Last modified:2025-02-01 08:59:27 GMT


[8]ページ先頭

©2009-2025 Movatter.jp