diff --git a/CHANGES.rst b/CHANGES.rst index cb1e6239..bdf0c896 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -19,11 +19,16 @@ Unreleased Python version. (`#2825`_) - :func:`send_file` handles an ``attachment_filename`` that is a native Python 2 string (bytes) with UTF-8 coded bytes. (`#2933`_) +- A catch-all error handler registered for ``HTTPException`` will not + handle ``RoutingExcpetion``, which is used internally during + routing. This fixes the unexpected behavior that had been introduced + in 1.0. (`#2986`_) .. _#2766: https://github.com/pallets/flask/issues/2766 .. _#2765: https://github.com/pallets/flask/pull/2765 .. _#2825: https://github.com/pallets/flask/pull/2825 .. _#2933: https://github.com/pallets/flask/issues/2933 +.. _#2986: https://github.com/pallets/flask/pull/2986 Version 1.0.2 diff --git a/flask/app.py b/flask/app.py index 49a025cd..c570a95b 100644 --- a/flask/app.py +++ b/flask/app.py @@ -20,7 +20,8 @@ from threading import Lock from werkzeug.datastructures import Headers, ImmutableDict from werkzeug.exceptions import BadRequest, BadRequestKeyError, HTTPException, \ InternalServerError, MethodNotAllowed, default_exceptions -from werkzeug.routing import BuildError, Map, RequestRedirect, Rule +from werkzeug.routing import BuildError, Map, RequestRedirect, \ + RoutingException, Rule from . import cli, json from ._compat import integer_types, reraise, string_types, text_type @@ -1631,6 +1632,16 @@ class Flask(_PackageBoundObject): registered error handlers and fall back to returning the exception as response. + .. versionchanged:: 1.0.3 + ``RoutingException``, used internally for actions such as + slash redirects during routing, is not passed to error + handlers. + + .. versionchanged:: 1.0 + Exceptions are looked up by code *and* by MRO, so + ``HTTPExcpetion`` subclasses can be handled with a catch-all + handler for the base ``HTTPException``. + .. versionadded:: 0.3 """ # Proxy exceptions don't have error codes. We want to always return @@ -1638,6 +1649,12 @@ class Flask(_PackageBoundObject): if e.code is None: return e + # RoutingExceptions are used internally to trigger routing + # actions, such as slash redirects raising RequestRedirect. They + # are not raised or handled in user code. + if isinstance(e, RoutingException): + return e + handler = self._find_error_handler(e) if handler is None: return e diff --git a/tests/test_user_error_handler.py b/tests/test_user_error_handler.py index 18d5f277..f4a08f58 100644 --- a/tests/test_user_error_handler.py +++ b/tests/test_user_error_handler.py @@ -184,6 +184,10 @@ def test_default_error_handler(): def forbidden(): raise Forbidden() + @app.route("/slash/") + def slash(): + return "slash" + app.register_blueprint(bp, url_prefix='/bp') c = app.test_client() @@ -191,5 +195,5 @@ def test_default_error_handler(): assert c.get('/bp/forbidden').data == b'bp-forbidden' assert c.get('/undefined').data == b'default' assert c.get('/forbidden').data == b'forbidden' - - + # Don't handle RequestRedirect raised when adding slash. + assert c.get("/slash", follow_redirects=True).data == b"slash"