Catching exceptions within application processors

238 views
Skip to first unread message

Matteo Landi

unread,
May 19, 2013, 6:46:34 AM5/19/13
to we...@googlegroups.com
Hi there,

What is the best way to manage certain type of exceptions so that they
don't produce a '500 Internal Error' HTTP status?

First I thought about request decorators but then I realized that
decorating *each* request seem to be a bit overkill. Then I thought
about application decorators: the documentation even contains an
example where a try-except block is put within an handler. So I come
up with the following handler:

def manage_content_exceptions(handler):
web.ctx.logger.info("before_manage")
try:
return handler()
except app.exceptions.ResponseContent as r:
return r.content
finally:
web.ctx.logger.info("after_manage")

What happens here is that the finally block is never executed and
consequently the exception is caught by the application and an
internal error is sent back to the client.

Could you please shed some light on the subject and tell me what's
wrong with my handler? I even found another discussion [1] about same
issue but unfortunately it is unanswered.


Regards,

Matteo

[1] https://groups.google.com/d/msg/webpy/f7IBxCgvIoY/GQxeHKVw8pcJ

Shannon Cruey

unread,
May 19, 2013, 12:19:24 PM5/19/13
to we...@googlegroups.com
The problem is the processor function in the application.py module has a try block in it, so it's redirecting to the internal error handler.  I have successfully overloaded this function in my main module and am able to return different status codes as I like.  Here's what I did in my main code:
 (You can see the entire file at 

    app = ExceptionHandlingApplication(urls, globals(), autoreload=True)


Then here's that class:

class ExceptionHandlingApplication(web.application):

    """

    This is an overload of the standard web.application class.    

    Main reason? In application.py, the 'handle_with_processors' function

        converts *any* exception into the generic web.py _InternalError.

        

    This interferes, because we wanna trap the original error an make determinations

        on how to reply to the client.

        

    So, we overloaded the function and fixed the error handing.

    """

    def handle_with_processors(self):

        def process(processors):

            try:

                if processors:

                    p, processors = processors[0], processors[1:]

                    return p(lambda: process(processors))

                else:

                    return self.handle()

            except (web.HTTPError, KeyboardInterrupt, SystemExit):

                raise

            except InfoException as ex:

                # I use a custom HTTP status code to indicate 'information' back to the user.

                web.ctx.status = "280 Informational Response"

                logger.exception(ex.__str__())

                return ex.__str__()

            except SessionError as ex:

                logger.exception(ex.__str__())

                # now, all our ajax calls are from jQuery, which sets a header - X-Requested-With

                # so if we have that header, it's ajax, otherwise we can redirect to the login page.

                

                # a session error means we kill the session

                session.kill()

                

                if web.ctx.env.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest":

                    web.ctx.status = "480 Session Error"

                    return ex.__str__()

                else:

                    logger.debug("Standard Request - redirecting to the login page...")

                    raise web.seeother('/static/login.html')

            except Exception as ex:

                # web.ctx.env.get('HTTP_X_REQUESTED_WITH')

                web.ctx.status = "400 Bad Request"

                logger.exception(ex.__str__())

                return ex.__str__()             

        

        return process(self.processors)




--
You received this message because you are subscribed to the Google Groups "web.py" group.
To unsubscribe from this group and stop receiving emails from it, send an email to webpy+un...@googlegroups.com.
To post to this group, send email to we...@googlegroups.com.
Visit this group at http://groups.google.com/group/webpy?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.



Matteo Landi

unread,
May 20, 2013, 12:50:55 PM5/20/13
to we...@googlegroups.com
On Sun, May 19, 2013 at 6:19 PM, Shannon Cruey
<shanno...@cloudsidekick.com> wrote:
> The problem is the processor function in the application.py module has a try
> block in it, so it's redirecting to the internal error handler. I have

I am looking at the code of the method ``handle_with_processors`` of
the application module: am I wrong saying that the try-catch block
could be safely moved out of the ``process`` function and put around
the line containing ``return process(self.processors)``? This way
processors will be able to handle certain kind of exceptions and still
propagate unhandled ones to the framework (to enable default exception
management).

What do you think about that? If you don't see any issue I can
provide a pull request with these changes.


Regards,

Matteo

Shannon Cruey

unread,
May 20, 2013, 1:11:44 PM5/20/13
to we...@googlegroups.com
I fiddled with that a lot to get it right, so my memory of the process is a little hazy.  Give it a try!

My issue was that I needed handle_with_processors to *not* catch the exceptions at all - because I wanted to do my own error handling end-to-end by actually returning specific exception types and handling them.  Handle_with_processors was hijacking the exceptions.

I'd suggest the best case for usability would be that handle_with_processors not have a try block, and leave it up to a higher level wrapper to handle them appropriately.  However, I didn't dig deep enough into web.py to see if there's a reason it's done like that, and my overload is working just fine.

Matteo Landi

unread,
May 20, 2013, 2:13:30 PM5/20/13
to we...@googlegroups.com
On Mon, May 20, 2013 at 7:11 PM, Shannon Cruey
<shanno...@cloudsidekick.com> wrote:
> I fiddled with that a lot to get it right, so my memory of the process is a
> little hazy. Give it a try!
>

I will.

> My issue was that I needed handle_with_processors to *not* catch the
> exceptions at all - because I wanted to do my own error handling end-to-end
> by actually returning specific exception types and handling them.
> Handle_with_processors was hijacking the exceptions.
>

If application processors were allowed to catch exceptions, would you
still be in need of subclassing web.Application? Probably not, you
could simply chain a custom processor with a custom try-catch and
everything should work like a charm. (Please, correct me if I am
wrong)


Matteo

Shannon Cruey

unread,
May 20, 2013, 3:56:19 PM5/20/13
to we...@googlegroups.com
No, it's the opposite.  If the processor code catches and handles the exception, it would still not raise it back up to my main app, even if it did move on to the next processor.

If handle_with_processors didn't have any try block at all, then any exceptions would be raised to the calling app.  For my use case that's fine but I suspect for a great many simpler use cases it might be annoying to require the user to write exception handler.

I certainly don't mind overloading.

One compromise solution might be to extend handle_with_processors with an optional argument of whether or not to trap errors, set default to True.  But, that doesn't seem very pythonic.

I'm happy enough with what I've had to do, and will leave it up to better experts than me to determine how (or even if) this should result in an enhancement.

Peace... NSC

Matteo Landi

unread,
May 21, 2013, 6:03:30 PM5/21/13
to we...@googlegroups.com
On Tue, May 21, 2013 at 11:02 PM, Matteo Landi <mat...@matteolandi.net> wrote:
> On Mon, May 20, 2013 at 9:56 PM, Shannon Cruey
> <shanno...@cloudsidekick.com> wrote:
>> No, it's the opposite. If the processor code catches and handles the
>> exception, it would still not raise it back up to my main app, even if it
>> did move on to the next processor.
>
> But if one of your custom application processors handles custom
> exceptions to take specific actions, then there won't be any reason to
> propagate the exceptions up to the application (I adapted the code you
> posted previously):
>
> def custom_processor(handler):
> try:
> return handler()
>> If handle_with_processors didn't have any try block at all, then any
>> exceptions would be raised to the calling app. For my use case that's fine
>> but I suspect for a great many simpler use cases it might be annoying to
>> require the user to write exception handler.
>
> If users don't use exceptions as flow control mechanism, then I expect
> they won't need to catch real *exceptions* (unless they want to log a
> message): an internal error sent back to the client should be enough.
>
> However I think here we have two problems: the first is to handle
> application exceptions, the second is logging a message when an
> exception is caught by the application. The former could be easily
> solved using application processors, while I think the latter is
> harder to fix because the framework actually lacks of a common logging
> interface.
FYI here is the link to the pull request:
https://github.com/webpy/webpy/pull/229


Cheers,

Matteo

Matteo Landi

unread,
May 21, 2013, 5:02:09 PM5/21/13
to we...@googlegroups.com
On Mon, May 20, 2013 at 9:56 PM, Shannon Cruey
<shanno...@cloudsidekick.com> wrote:
> No, it's the opposite. If the processor code catches and handles the
> exception, it would still not raise it back up to my main app, even if it
> did move on to the next processor.

But if one of your custom application processors handles custom
exceptions to take specific actions, then there won't be any reason to
propagate the exceptions up to the application (I adapted the code you
posted previously):

def custom_processor(handler):
try:
return handler()
> If handle_with_processors didn't have any try block at all, then any
> exceptions would be raised to the calling app. For my use case that's fine
> but I suspect for a great many simpler use cases it might be annoying to
> require the user to write exception handler.

If users don't use exceptions as flow control mechanism, then I expect
they won't need to catch real *exceptions* (unless they want to log a
message): an internal error sent back to the client should be enough.

However I think here we have two problems: the first is to handle
application exceptions, the second is logging a message when an
exception is caught by the application. The former could be easily
solved using application processors, while I think the latter is
harder to fix because the framework actually lacks of a common logging
interface.

>
Reply all
Reply to author
Forward
0 new messages