At 10:57 AM 3/30/05 -0600, Ian Bicking wrote:
Phillip J. Eby wrote:
That's not the use case. The parameter exists so error handling code doesn't have to care whether start_response has already been called.
Thus, applications and middleware can be simpler because they don't need to track that bit of state information that the server is already tracking. So, calling start_response when it has already been called causes the error handler to abort and fall back to the next higher error handler, all the way up to the "real" server. IOW, it's a way of guaranteeing immediate request termination if an error occurs once the response has begun.
Of course, any logging or notification error handlers in the stack will receive the error in the normal way; it's just that if they also try to start a response, they'll be aborted and the error will bubble up to the next handler. Does that make more sense now?

I guess, but it seems to complicate most middleware for the benefit of a small number of middlewares. My current middleware (all of which is written ignorant of this argument) does something like:


class ErrorMiddleware(object):

    def __init__(self, application, show_exceptions=True,
                 email_exceptions_to=[], smtp_server='localhost'):
        self.application = application
        self.show_exceptions = show_exceptions
        self.email_exceptions_to = email_exceptions_to
        self.smtp_server = smtp_server

    def __call__(self, environ, start_response):
        # We want to be careful about not sending headers twice,
        # and the content type that the app has committed to (if there
        # is an exception in the iterator body of the response)
        started = []

        def detect_start_response(status, headers):
            started.append(True)
            return start_response(status, headers)

        try:
            app_iter = self.application(environ, detect_start_response)
            return self.catching_iter(app_iter, environ)
        except:
            if not started:
                start_response('500 Internal Server Error',
                               [('content-type', 'text/html')])
            # @@: it would be nice to deal with bad content types here
            dummy_file = StringIO()
            response = self.exception_handler(sys.exc_info(), environ)
            return [response]


It really should capture the headers, and maybe buffer them itself (in which case it would also have to intercept the writer), so that it can deal more gracefully with a case where content type is set or something. But all that annoying stuff is better kept to this one piece of middleware, instead of making everything more difficult with that extra argument to start_response.

Um, the argument is *precisely* there so you don't need all of that! Try this:


def __call__(self, environ, start_response):
try:
app_iter = self.application(environ, detect_start_response)
except:
start_response('500 Internal Server Error',
[('content-type', 'text/html')], sys.exc_info())
# etc...


No need to track the startedness here, because the upstream start_response will reraise the error if you've already started, thus breaking out of the middleware and getting back to the calling server.

_______________________________________________
Web-SIG mailing list
Web-SIG@python.org
Web SIG: http://www.python.org/sigs/web-sig
Unsubscribe: 
http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com

Reply via email to