[Web-SIG] Reviewing WSGI open issues, again...

Phillip J. Eby pje at telecommunity.com
Thu Sep 9 21:30:27 CEST 2004


At 11:32 AM 9/9/04 -0700, tony at lownds.com wrote:

>I still like the idea of having an exception that servers will always
>catch and send back to the user.

Currently, isn't that *every* exception?  I'm making the assumption that 
the server will want to log and display every non-fatal error.  (Except 
those occurring after the headers are sent, which can only be logged in the 
general case.)


>If an application doesn't know whether a
>server can display an error page, it will tend to include it's own
>error-displaying logic (made simpler by the start_response() above). But,
>if applications take care of displaying those exceptions, then exception
>catching middleware won't really be useful for those applications.

This seems circular to me: if the application throws an error that's 
actually an application-defined error message, then why is middleware going 
to be *useful* here?

You must have some other use case in mind besides the middleware presenting 
a friendly message, since presumably the application can produce a 
friendlier message (at least in the sense of being specific to the app and 
looking like the app).  Could you elaborate on your use case?


>As long as exceptions get logged, I think it is fine for there to be no
>requirement about sending error data back to the client, after the
>response is started.
>
>Without some other way for applications to send errors, then the
>additional requirements on start_response do make sense, even though it
>complicates some pretty tricky logic.

I'm not sure it's *that* bad...

     headers_set = []
     headers_sent = []

     def write(data):

         if not headers_set:
              raise AssertionError("write() before start_response()")

         elif not headers_sent:
             status, headers = headers_sent[:] = headers_set
             write(status+'\r\n')
             for header in headers:
                 write('%s: %s\r\n' % header)
             write('\r\n')

         # actual write() code goes here...

     def start_response(status,headers):
         if headers_sent:
             raise AssertionError("Headers already sent!")
         headers_set[:] = [status,headers]
         return write

     # ...
     result = application(environ, start_response)
     try:
         try:
             for data in result:
                 write(data)
             if not headers_sent:
                 write('')   # force headers to be sent
         except:
             if not headers_sent:
                 # call start_response() with a 500 error
                 # status, then write out an error message

             # re-raise the error

     finally:

         # XXX ensure client connection is closed first

         if hasattr(result,'close'):
             result.close()


Of course, all of the above should be wrapped in a try-except that logs any 
errors and continues the server.


>How does wsgi.fatal_errors help servers? Wouldn't servers have to make up
>specialized exceptions for inclusion in wsgi.fatal_errors, in order to
>avoid interfering with catching other exceptions? Now write() and
>start_response() need more logic, to throw only errors in
>wsgi.fatal_errors.

Hm.  Well, the alternative would be that the server has to track state to 
know its state is hosed.  That is, if you try to write() when a client 
connection is lost, subsequent write() calls should fail.  Similarly, 
start_response() after write() should fail, but then so should subsequent 
write() calls.

It seemed to me that it was simpler to raise a fatal error in that case, 
which the application would allow to pass through.  But, if the server has 
to consider the possibility that the app might not be able to enforce this 
(e.g. because of bare 'except:' clauses), then I suppose we might as well 
just have the complexity of state checking and ignore the fatal errors issue.

OTOH, the purpose of fatal_errors is to allow the *app* to know that it's 
pointless to go on, and that it *should* abort.  This still seems somewhat 
useful to me, although it could also be argued that virtually *any* 
exception raised by start_response() and write() should be considered fatal.

Cascading errors are also a potential problem.  Let's say the application 
doesn't propagate a fatal error, but instead "converts" it to a different 
kind of error.  Now, the server must catch the application's error, while 
still knowing that it erred internally first.  Sigh.

This suggests to me that start_response() and write() must have exception 
handlers that set a flag when they have an uncaught exception, so that they 
know to ignore the application's later errors if the problem originated 
within the server.  Ugh.

I suppose the bright side is that we wouldn't need 'wsgi.fatal_errors' any 
more, but my "not so bad" code above now needs some additional error 
handling and an 'internal_errors' state variable.


>And servers can't rely on applications adhering to the
>rules in the specs.

I'm not sure what you mean here, but maybe it's what I just said 
above?  (about apps maybe being broken in their handling of fatal_errors).



More information about the Web-SIG mailing list