Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for generic HTTPException handlers on app and blueprints #2314

Merged
merged 5 commits into from
May 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions flask/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -1460,31 +1460,31 @@ def url_defaults(self, f):
return f

def _find_error_handler(self, e):
"""Finds a registered error handler for the request’s blueprint.
Otherwise falls back to the app, returns None if not a suitable
handler is found.
"""Find a registered error handler for a request in this order:
blueprint handler for a specific code, app handler for a specific code,
blueprint generic HTTPException handler, app generic HTTPException handler,
and returns None if a suitable handler is not found.
"""
exc_class, code = self._get_exc_class_and_code(type(e))

def find_handler(handler_map):
if not handler_map:
return

for cls in exc_class.__mro__:
handler = handler_map.get(cls)
if handler is not None:
# cache for next time exc_class is raised
handler_map[exc_class] = handler
return handler

# try blueprint handlers
handler = find_handler(self.error_handler_spec
.get(request.blueprint, {})
.get(code))
if handler is not None:
return handler
# check for any in blueprint or app
for name, c in ((request.blueprint, code), (None, code),
(request.blueprint, None), (None, None)):
handler = find_handler(self.error_handler_spec.get(name, {}).get(c))

# fall back to app handlers
return find_handler(self.error_handler_spec[None].get(code))
if handler:
return handler

def handle_http_exception(self, e):
"""Handles an HTTP exception. By default this will invoke the
Expand Down
57 changes: 56 additions & 1 deletion tests/test_user_error_handler.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# -*- coding: utf-8 -*-
from werkzeug.exceptions import Forbidden, InternalServerError
from werkzeug.exceptions import (
Forbidden,
InternalServerError,
HTTPException,
NotFound
)
import flask


Expand Down Expand Up @@ -138,3 +143,53 @@ def app_test():

assert c.get('/error').data == b'app-error'
assert c.get('/bp/error').data == b'bp-error'


def test_default_error_handler():
bp = flask.Blueprint('bp', __name__)

@bp.errorhandler(HTTPException)
def bp_exception_handler(e):
assert isinstance(e, HTTPException)
assert isinstance(e, NotFound)
return 'bp-default'

@bp.errorhandler(Forbidden)
def bp_exception_handler(e):
assert isinstance(e, Forbidden)
return 'bp-forbidden'

@bp.route('/undefined')
def bp_registered_test():
raise NotFound()

@bp.route('/forbidden')
def bp_forbidden_test():
raise Forbidden()

app = flask.Flask(__name__)

@app.errorhandler(HTTPException)
def catchall_errorhandler(e):
assert isinstance(e, HTTPException)
assert isinstance(e, NotFound)
return 'default'

@app.errorhandler(Forbidden)
def catchall_errorhandler(e):
assert isinstance(e, Forbidden)
return 'forbidden'

@app.route('/forbidden')
def forbidden():
raise Forbidden()

app.register_blueprint(bp, url_prefix='/bp')

c = app.test_client()
assert c.get('/bp/undefined').data == b'bp-default'
assert c.get('/bp/forbidden').data == b'bp-forbidden'
assert c.get('/undefined').data == b'default'
assert c.get('/forbidden').data == b'forbidden'