-
-
Save danijar/827bee07350a96bbf35b to your computer and use it in GitHub Desktop.
| import flask_restful | |
| class Api(flask_restful.Api): | |
| """ | |
| Patch Flask-style custom error handling into the Flask-RESTful api class. | |
| """ | |
| def __init__(self, *args, **kwargs): | |
| super(Api, self).__init__(*args, **kwargs) | |
| self._errorhandlers = [] | |
| def errorhandler(self, exception_type): | |
| """ | |
| Defined handlers for exceptions. Example: | |
| @api.errorhandler(ServerError): | |
| def handle_server_error(error): | |
| response = flask.jsonify({'message': error.message}) | |
| response.status_code = error.status_code | |
| return response | |
| """ | |
| def wrapper(func): | |
| self._errorhandlers.append((exception_type, func)) | |
| # Sort error handlers to have sub exceptions first, so that those | |
| # take preference over base exceptions. | |
| self._errorhandlers = sorted( | |
| self._errorhandlers, | |
| key=lambda x: x[0], | |
| cmp=self._inheritance_comparator) | |
| print(self._errorhandlers) | |
| return func | |
| return wrapper | |
| def handle_error(self, error, previous_errors=None): | |
| # Keep track of previous errors in the current chain of exception | |
| # handling in order to prevent infinite cycles that would occur if two | |
| # error handler raise the exception handled by the other. | |
| previous_errors = previous_errors or [] | |
| previous_errors.append(type(error)) | |
| # Try to find the first custom handler for the occured exception. | |
| for exception_type, handler in self._errorhandlers: | |
| if not isinstance(error, exception_type): | |
| continue | |
| try: | |
| return handler(error) | |
| except Exception as new_error: | |
| if type(new_error) not in previous_errors: | |
| return self.handle_error(new_error, previous_errors) | |
| break | |
| # If no matching handler was found or an infinite cycle is detected, | |
| # fall back to Flask-RESTful's error handling. | |
| return super(Api, self).handle_error(error) | |
| @staticmethod | |
| def _inheritance_comparator(lhs, rhs): | |
| lhs_sub = issubclass(lhs, rhs) | |
| rhs_sub = issubclass(lhs, rhs) | |
| if lhs_sub and not rhs_sub: | |
| return -1 | |
| if rhs_sub and not lhs_sub: | |
| return 1 | |
| return 0 |
Hi and sorry for the late reply. You can freely use this code under the MIT license.
Thanks for the patch since RF pull request seems given up :(
I tried your patch above, but my custom exception handler is not executed, it seems this conditions (Line 43) never True
for exception_type, handler in self._errorhandlers:
if not isinstance(error, exception_type):
continue
it always fall back to Flask-RESTful's error handling. Any idea?
Cheers,
Hi
Your code patch is really good. But I can't figure out the significance of previous_errors list.
From what I understand, every exception type is mapped with a method handler, and it doesn't handle its Base or Child exceptions. So how can possibly infinite cycle of exceptions may occur. Moreover, handle_error method of flask_restful.Api has no such implementation.
Can you just clarify the confusion?
Thanks
For anyone getting the error 'cmp' is an invalid keyword argument for this function, replace lines 27-30 with this snippet
import functools
...
self._errorhandlers = sorted(
self._errorhandlers,
key=functools.cmp_to_key(self._inheritance_comparator))
Hello, this is very helpful! I found this gist from following you comment on this issue:
flask-restful/flask-restful#274
If you are willing to share this code freely could you please add a license to the header?
Also, I created a fork which addresses an issue with python3.5 compatibility if you are interested.