Hi,

in order not to over-bloat the trac I think it is best if I submit my
patch/proposal (see attachment) here first.

Presented is an implementation of JP's idea with some enchantments. It
allows defining error handlers as follows:

  @expose(html='etc')
  # Something evil happend to our DB
  @error_handler(sql_error, "isinstance(errors, SQLObjectIntegrityError)")
  # Default
  @error_handler(submit_error)
  @input(form=form)
  def submit(self, **data):
       ...

Each exposed method can have multiple error handlers if generic
functions are harnessed.

Execution of decorated method is encapsulated in a try-catch block
passing any resulting exceptions to an appropriate error handler.

Compatibility with existing way of handling errors is retained.

There are three things I would like to add or change eventually but I am
unsure what would be the best/cleanest way of doing it:

1) Introduce a global default error handler. Perhaps in form of a
decorator. For example:

  @default_error_handler
  def my_default_error_handler(self, errors, *args, **kw):
    ...

2) Integrate Ian Bicking's excellent EvalException [1] for use during debugging/development.

3) Currently all exceptions raised by the decorated method are caught.
Such behaviour is at times a bit counter-productive as it can obscure
trivial errors such as syntax errors and the like.
We could catch just base exceptions of all modules but since TG is composed of many different projects this will most likely end up quite messy. Even worse, any such filtering reduces flexibility and clarity.ΕΎ
I suppose 2) would help in this matter as well.


Cheers,
Simon

[1] http://blog.ianbicking.org/ajaxy-exception-catching.html


10a11
> from dispatch import generic, strategy
143c146
<                     output = _call_with_errors(func, self, *args, **kw)
---
>                     output = _dispatch_error(func, self, errors, *args, **kw)
144a148
>                 try:
145a150,151
>                 except Exception, errors:
>                     output = _dispatch_error(func, self, errors, *args, **kw)
287a292,333
>
> def _dispatch_error(error_source, self, errors, *args, **kw):
>     """ Dispatch error.
>
>     Error handler is a function registered via error_handler decorator or
>     the method that triggered the error (hence forth referred to as
>     "old-style").
>     """
> _dispatch_error = generic()(_dispatch_error)
>
> def _oldstyle_error_handling(error_source, self, errors, *args, **kw):
>     """ Dispatch error old-style like. """
>     return _call_with_errors(error_source, self, *args, **kw)
> _oldstyle_error_handling = _dispatch_error.when(strategy.default)(
>                            _oldstyle_error_handling)
>
> def error_handler(handler, dispatch_rules=None):
>     """ Register handler as an error handler for func.
>
>     dispatch_rules can be a string containing an arbitrary logical Python
>     expression to be used as dispatch rule allowing multiple error handlers
>     for a single method.
>
>     error_handler decorator is an invariant.
>     """
>     def register(func):
>         when = "error_source.__name__ == '%s'" % func.__name__
>         if dispatch_rules:
>             when += " and (%s)" % dispatch_rules
>
>         # First argument of _dispatch_error is only used in dispatch rules,
>         # therefore application code need not declare it.
>         def normalize(func):
>             def prepend_arg(error_source, self, errors, *args, **kw):
>                 return func(self, errors, *args, **kw)
>             return prepend_arg
>
>         _dispatch_error.when(when)(normalize(handler))
>         return func
>     return register
>
>
309c355
< __all__ = ["expose", "validate", "redirect",
---
> __all__ = ["expose", "validate", "redirect", "error_handler",

Reply via email to