Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

PyCon US 2012 - State of WSGI 2


Published on

Published in: Technology
  • Be the first to comment

PyCon US 2012 - State of WSGI 2

  1. 1. State of WSGI 2 Graham Dumpleton US PyCon March 2012 @GrahamDumpleton
  2. 2. 1.0 (PEP 333)PEP created December 2003.A series of compromises. Iterable vs write(). Limited async capability.
  3. 3. 1.0.1 (PEP 3333)PEP created September 2010.Clarifications 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!
  4. 4. ShortcomingsHard to write middleware correctly.
  5. 5. 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])
  6. 6. 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.
  7. 7. Wishful thinkingStandardised high level request/responseobjects. WebOb Werkzeug
  8. 8. 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.
  9. 9. Has the boat sailed?Too much legacy code relying on WSGI 1.0.Potential missed opportunity for significantchange when updating specification forPython 3.X.The possibilities of what could have beendone are now probably only of academicinterest.
  10. 10. 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.
  11. 11. Resource managementNeed to override close() of the iterable.Way too complicated for mere mortals.Makes wsgi.file_wrapper impotent.
  12. 12. 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)
  13. 13. Not done yetclass ExecuteOnCompletion: def __init__(self, application, callback): self.__application = application self.__callback = callback def __call__(self, environ, start_response): try: result = self.__application(environ, start_response) except: self.__callback(environ) raise return Generator(result, self.__callback, environ)
  14. 14. 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__().
  15. 15. 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 filters.
  16. 16. Hello worlddef application(request, response): status = 200 OK headers = [(Content-type, text/plain),] output = Hello world! return response(status, headers, [output])
  17. 17. Consuming inputdef application(request, response): status = 200 OK headers = [(Content-type, text/plain),] with request: output = .join(request.content) return response(status, headers, [output])
  18. 18. Content filtersclass 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))
  19. 19. Middlewaredef middleware(application): def _middleware(request, response): response = application(request, response) headers = list(itertools.dropwhile( lambda h: h[0].lower() == content-length, response.headers)) return response(headers=headers, content=Filter(response, lambda x: 2*x)) return _middlewareapplication = middleware(application)
  20. 20. Content length is painfuldef application(request, response): status = 200 OK headers = [(Content-type, text/plain),] output = Hello world! return response(status, headers, [output], len(output))
  21. 21. Hiding the paindef middleware(application): def _middleware(request, response): response = application(request, response) return response( content=Filter(response, lambda x: 2*x), length=None) return _middlewareapplication = middleware(application)
  22. 22. 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)
  23. 23. 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.