diff --git a/flask/app.py b/flask/app.py index 6baee3a6..1f3d39ce 100644 --- a/flask/app.py +++ b/flask/app.py @@ -14,7 +14,7 @@ from threading import Lock from datetime import timedelta from itertools import chain from functools import update_wrapper -from collections import Mapping +from collections import Mapping, deque from werkzeug.datastructures import ImmutableDict from werkzeug.routing import Map, Rule, RequestRedirect, BuildError @@ -1411,27 +1411,37 @@ class Flask(_PackageBoundObject): If neither blueprint nor App has a suitable handler registered, returns None """ exc_class, code = self._get_exc_class_and_code(type(e)) - - def find_superclass(handler_map): + + def find_handler(handler_map): if not handler_map: - return None - for superclass in exc_class.__mro__: - if superclass is BaseException: - return None - handler = handler_map.get(superclass) + return + queue = deque(exc_class.__mro__) + # Protect from geniuses who might create circular references in + # __mro__ + done = set() + + while True: + cls = queue.popleft() + if cls in done: + continue + done.add(cls) + handler = handler_map.get(cls) if handler is not None: - handler_map[exc_class] = handler # cache for next time exc_class is raised + # cache for next time exc_class is raised + handler_map[exc_class] = handler return handler - return None - + + queue.extend(cls.__mro__) + # try blueprint handlers - handler = find_superclass(self.error_handler_spec.get(request.blueprint, {}).get(code)) - + handler = find_handler(self.error_handler_spec + .get(request.blueprint, {}) + .get(code)) if handler is not None: return handler - + # fall back to app handlers - return find_superclass(self.error_handler_spec[None].get(code)) + return find_handler(self.error_handler_spec[None].get(code)) def handle_http_exception(self, e): """Handles an HTTP exception. By default this will invoke the