[Web-SIG] wsgi.fatal_errors

tony at lownds.com tony at lownds.com
Tue Aug 31 08:15:55 CEST 2004


> Here are some changes I've proposed in the last few days to resolve issues
> people brought up, but which I haven't gotten much feedback on:
>
> * 'wsgi.fatal_errors' key for exceptions that apps and middleware
> shouldn't
> trap
>

What about defining an exception class that applications can raise with an
HTML payload, which servers are supposed to send the to the client?
Middleware should be free to alter the payload as much as they like. The
server should not send the payload when content-type is not html.

By using exceptions as a backchannel, the application and middleware do
not have to keep track of the state to sanely handle an error.

With these examples, the FormatExceptions middleware really needs to be
the "innermost" middleware. I think exception-handling middleware
independent of how it is stacked is a non-goal.

For example,

def an_application(env, start_response):
  try:
     form = read_form(env)
     html = do_work(form)
     write = start_response('200 OK', [('Content-type', 'text/html')])
     return [html]
  except:
     import cgitb
     cgitb.html
     raise env['wsgi.error_class'], cgitb.html(sys.exc_info())

...and middleware that formats the exception:

def FormatExceptions(app):
  import sys, cgitb
  def middleware(env, start_response):
    try:
      return app(env, start_response)
    except:
      raise env['wsgi.error_class'], cgitb.html(sys.exc_info())
  return middleware

...and more complicated middleware that uses this concept:

class AddContent:
  def __init__(self, app, header='', footer=''):
     self.app = app
     self.header = header
     self.footer = footer

  def __call__(self, env, start_response):
    return AddContentHandler(env, start_response, self).run()

  def add_length(self, length):
     return length + len(self.header) + len(self.footer)

class AddContentHandler:
  def __init__(self, add_content, env, start_response):
    self.env = env
    self.orig_start_response = start_response
    self.add_content = add_content
    self.written_header = False
    self.publish_extension()

  def publish_extension(self):
    self.env['wsgi.extensions'].append('add_content')
    self.env['add_content.instance'].append(add_content)

  def start_response(self, status, headers):
    self.set_headers(headers)
    self.check_content_length()
    self.orig_write = self.orig_start_response(status,
self.rebuild_headers())
    return self.write

  def write(self, data):
    if not self.written_header:
      self.orig_write(self.add_content.header)
      self.written_header = True
    return self.orig_write(data)

  def run(self):
     try:
       result = self.add_content.app(self.env, self.start_response)
     except self.env['wsgi.error_class'], e:
       # wrap exception html -- try not to duplicate header
       html = str(e)
       if self.written_header:
         self.written_header = True
         html = self. add_content.header + html
       html += self. add_content.footer
       raise self.env['wsgi.error_class'], html
     else:
       self.result = iter(result)
       return self

  def __iter__(self):
     if not self.written_header:
       self.written_header = True
       yield self.add_content.header
     for i in self.result:
       yield i
     yield self.add_content.footer

-Tony



More information about the Web-SIG mailing list