wiki:HowDoesErrorHandlingWork
Warning: Can't synchronize with repository "(default)" (Unsupported version control system "svn": No module named svn). Look in the Trac log for more information.

Version 9 (modified by Luca <luca@…>, 9 years ago) (diff)

Avoid ClassNames? to be treated as WikiNames?

How does error handling work

WARNING: error handling is still in state of flux, the following page is primarily intended for developers.

1 Validation errors

Definitions:

error handler -- method called when an error occurs.

Let foo be a function defined as

@expose()
@validate(validators={"bar":validators.Int()}, "baz":validators.Int())
def foo(self, bar=None, bar=None):
    pass

1.1 Basics

Basic form of error handling is via decorator error_handler with error handler as argument:

@error_handler(my_eh)

my_eh can have an arbitrary signature, however one might wish to declare parameter tg_errors wherein errors (causing our error handler to be called) will be passed. Other parameters of interest are any named the same as arguments being validated, allowing us to fully reconstruct the original call (that failed validation). A more general approach would be using *identifier and identifier.

my_eh now looks something like:

def my_eh(self, tg_errors, bar):
    pass

Of curse nothing prevents us from using an existing (exposed) method as error handler.

A method can even be it's own error handler when error_handler is applied without any arguments:

@error_handler()

or by declaring parameter tg_errors:

def foo(self, bar=None, tg_errors=None):

Mind, tg_errors should be a keyword argument, when used in this manner (since for valid input the method will not get re-called as error handler, hence tg_errors will not get passed in).

If no appropriate error handler is defined and validation failed, NotImplementedError? exception (from standard library) is raised.

1.2 Dispatch rules

What if we want to handle invalid baz-es differently than invalid bar-s? Fortunately for us, error_handler takes a second keyword argument rules, making it possible to influence selection of appropriate error handler (actually a limited interface to RuleDispatch?[1]). Rules are arbitrary logical Python expressions passed as lists. For example:

@error_handler(baz_eh, "'baz' in tg_errors")
@error_handler(bar_eh)

The most specific applicable rule is selected, meaning if validation of baz fails, baz_eh will be called regardless of bar's state or in other words, bar_eh will be called if and only if validation of bar fails and baz passes.

Any number of calls to error_handler can be made with the same handler.

Since both arguments of error_handler are optional, specialisation is possible even when method is it's own error handler:

@error_handler(rules="'baz' in tg_errors")

1.3 Fail-safe mechanism

If validation fails for an input, said input also does not get to be converted to designated Python type, which could be rather inconvenient. A fail-safe mechanism is provided for such cases. Decorator validate can take two additional keyword arguments failsafe_scheme and failsafe_values to fine-tune how to failed inputs are handled.

Predefined schemas (see turbogears/errorhandling.py):

  • FailsafeSchema.none: Default. No conversions are preformed, input remains as is:
    @validate(validators={...})
    

or explicitly:

@validate(validators={...}, failsafe_scheme=errorhandling.FailsafeSchema.none)

failsafe_values is ignored.

  • FailsafeSchema.values: If failsafe_values is a dict, every input for which exists a key in failsafe_values is replaced with corresponding value. Otherwise all erroneous inputs are replaced with (same instance of!) failsafe_values.
    @validate(validators={"bar":validators.Int(), "baz":validators.Int()}, failsafe_schema=FailsafeSchema.values, failsafe_values={"bar":1, "baz":2})
    

If erroneous, bar is set to (int) 1, while baz to 2.

@validate(validators={"bar":validators.Int(), "baz":validators.Int()}, failsafe_schema=FailsafeSchema.values, failsafe_values=13)

Both bar and baz are set to 13 when erroneous.

  • FailsafeSchema.map_errors: Inputs are mapped to corresponding exceptions.
    @validate(validators={"bar":validators.Int(), "baz":validators.Int()}, failsafe_schema=FailsafeSchema.map_errors)
    

failsafe_values is ignored.

User defined schemas can be added by defining new specialisations for generic function errorhandling.dispatch_failsafe.

2 Exceptions

2.1 Basics

Analogue to error_handler and tg_errors, exception_handler and tg_exceptions are defined.

If no appropriate exception handler is defined, exception is passed to a lower layer (e.g. CherryPy?).

2.2 Combining with error handling

One method can have any number of exception handlers and error handlers.

If having the same handler for errors and exceptions is desired, one can use errorhandling.register_handler which is similar to error_handler and exception_handler but without inherent specialisations.

3 Advanced topics

3.1 Utilities

3.1.1 bind_args

3.1.2 bind_rules

3.2 Generic functions

3.3 Continuations


References

[1]  http://www-128.ibm.com/developerworks/library/l-cppeak2/index.html

Further reading

Source

http://trac.turbogears.org/turbogears/browser/trunk/turbogears/errorhandling.py

http://trac.turbogears.org/turbogears/browser/trunk/turbogears/tests/test_errorhandling.py

Generic functions

 http://peak.telecommunity.com/PyCon05Talk/

 http://peak.telecommunity.com/DevCenter/CombiningResults

 http://peak.telecommunity.com/DevCenter/VisitorRevisited

 http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html

Continuations

 http://www.ps.uni-sb.de/~duchier/python/continuations.html