[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