Skip to content

Commit

Permalink
Merge pull request #2314 from cerickson/errorhandler
Browse files Browse the repository at this point in the history
Added support for generic HTTPException handlers on app and blueprints
  • Loading branch information
davidism authored May 23, 2017
2 parents cd593bf + 361dba7 commit e206764
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 12 deletions.
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'


0 comments on commit e206764

Please sign in to comment.