PyCon US 2012 - State of WSGI 2Presentation Transcript
State of WSGI 2 Graham Dumpleton US PyCon March 2012 @GrahamDumpleton Graham.Dumpleton@gmail.com
1.0 (PEP 333)PEP created December 2003.A series of compromises. Iterable vs write(). Limited async capability.
1.0.1 (PEP 3333)PEP created September 2010.Clariﬁcations to PEP 333 to handle str/bytesdistinction in Python 3.X.Apache/mod_wsgi had already beenproviding support for Python 3.X since April2009.Was a quick and painless process. NOT!
ShortcomingsHard to write middleware correctly.
Easy targetsDrop start_response() and write().def app(environ): status = 200 OK content = Hello world headers = [ (Content-type, text/plain), (Content-length, str(len(content)))] return (status, headers, [content])
Unwanted advancesSupport for async systems. The killer for async is that how you interact with wsgi.input means it is blocking. Hard to change it to make it work for async systems without making it too complicated for sync systems.
Wishful thinkingStandardised high level request/responseobjects. WebOb Werkzeug
Interesting problemsCleaning up at end of request.Middleware and response content length.Unknown request content length. No compressed request content. No chunked requests. No full duplex HTTP.
Has the boat sailed?Too much legacy code relying on WSGI 1.0.Potential missed opportunity for signiﬁcantchange when updating speciﬁcation forPython 3.X.The possibilities of what could have beendone are now probably only of academicinterest.
Ideas I toyed withUse context managers to improve resourcemanagement.Implementing wsgi.input as an iterable.Support for chunked request content.Iterating through a list of applications untilone agrees to handle it.
Resource managementNeed to override close() of the iterable.Way too complicated for mere mortals.Makes wsgi.ﬁle_wrapper impotent.
How complicatedclass Generator: def __init__(self, iterable, callback, environ): self.__iterable = iterable self.__callback = callback self.__environ = environ def __iter__(self): for item in self.__iterable: yield item def close(self): try: if hasattr(self.__iterable, close): self.__iterable.close() finally: self.__callback(self.__environ)
Response as objectMake response a context manager.Response content is an attribute.Consumer of response executes: with response: for data in response.content: process(data)Can override __enter__()/__exit__().
Request as objectAlso make request a context manager.Request content is an attribute, but alsomake it an iterable.Reading all request content becomes: with request: content = .join(request.content)Content length becomes advisory only, so canimplement chunked content and mutatinginput ﬁlters.
Hello worlddef application(request, response): status = 200 OK headers = [(Content-type, text/plain),] output = Hello world! return response(status, headers, [output])
Consuming inputdef application(request, response): status = 200 OK headers = [(Content-type, text/plain),] with request: output = .join(request.content) return response(status, headers, [output])
Content ﬁltersclass Filter(object): def __init__(self, source, filter): self.source = source self.filter = filter def __iter__(self): with self.source: for data in self.source.content: yield self.filter(data)def application(request, response): status = 200 OK headers = [(Content-type, text/plain),] return response(status, headers, Filter(request, str))
I decline your request!def declined(request, response): return responsedef middleware(*applications): def _middleware(request, response): for application in applications: interim = application(request, response) if interim is not response: return interim else: return response return _middlewareapplication = middleware(declined, application)
Is it a lost cause?Is this all too late?Is anyone interested interested in WSGI 2.0anymore?Although an interesting API, it cannot bedone on top of WSGI as changes how oneinteracts with the web server.So, try and do this and it does need tosupplant WSGI 1.0.