Browse Source

Merge pull request #2362 from davidism/remove-error-handler-cache

Remove error handler cache
pull/1934/merge
David Lord 8 years ago committed by GitHub
parent
commit
b80cf057fc
  1. 4
      CHANGES
  2. 26
      flask/app.py
  3. 33
      tests/test_basic.py

4
CHANGES

@ -76,6 +76,9 @@ Major release, unreleased
- Extract JSON handling to a mixin applied to both the request and response - Extract JSON handling to a mixin applied to both the request and response
classes used by Flask. This adds the ``is_json`` and ``get_json`` methods to classes used by Flask. This adds the ``is_json`` and ``get_json`` methods to
the response to make testing JSON response much easier. (`#2358`_) the response to make testing JSON response much easier. (`#2358`_)
- Removed error handler caching because it caused unexpected results for some
exception inheritance hierarchies. Register handlers explicitly for each
exception if you don't want to traverse the MRO. (`#2362`_)
.. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1489: https://github.com/pallets/flask/pull/1489
.. _#1621: https://github.com/pallets/flask/pull/1621 .. _#1621: https://github.com/pallets/flask/pull/1621
@ -98,6 +101,7 @@ Major release, unreleased
.. _#2352: https://github.com/pallets/flask/pull/2352 .. _#2352: https://github.com/pallets/flask/pull/2352
.. _#2354: https://github.com/pallets/flask/pull/2354 .. _#2354: https://github.com/pallets/flask/pull/2354
.. _#2358: https://github.com/pallets/flask/pull/2358 .. _#2358: https://github.com/pallets/flask/pull/2358
.. _#2362: https://github.com/pallets/flask/pull/2362
Version 0.12.2 Version 0.12.2
-------------- --------------

26
flask/app.py

@ -1484,30 +1484,26 @@ class Flask(_PackageBoundObject):
return f return f
def _find_error_handler(self, e): def _find_error_handler(self, e):
"""Find a registered error handler for a request in this order: """Return a registered error handler for an exception in this order:
blueprint handler for a specific code, app handler for a specific code, blueprint handler for a specific code, app handler for a specific code,
blueprint generic HTTPException handler, app generic HTTPException handler, blueprint handler for an exception class, app handler for an exception
and returns None if a suitable handler is not found. class, or ``None`` if a suitable handler is not found.
""" """
exc_class, code = self._get_exc_class_and_code(type(e)) exc_class, code = self._get_exc_class_and_code(type(e))
def find_handler(handler_map): for name, c in (
(request.blueprint, code), (None, code),
(request.blueprint, None), (None, None)
):
handler_map = self.error_handler_spec.setdefault(name, {}).get(c)
if not handler_map: if not handler_map:
return continue
for cls in exc_class.__mro__: for cls in exc_class.__mro__:
handler = handler_map.get(cls) 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
# 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))
if handler: if handler is not None:
return handler return handler
def handle_http_exception(self, e): def handle_http_exception(self, e):

33
tests/test_basic.py

@ -980,6 +980,39 @@ def test_http_error_subclass_handling(app, client):
assert client.get('/3').data == b'apple' assert client.get('/3').data == b'apple'
def test_errorhandler_precedence(app, client):
class E1(Exception):
pass
class E2(Exception):
pass
class E3(E1, E2):
pass
@app.errorhandler(E2)
def handle_e2(e):
return 'E2'
@app.errorhandler(Exception)
def handle_exception(e):
return 'Exception'
@app.route('/E1')
def raise_e1():
raise E1
@app.route('/E3')
def raise_e3():
raise E3
rv = client.get('/E1')
assert rv.data == b'Exception'
rv = client.get('/E3')
assert rv.data == b'E2'
def test_trapping_of_bad_request_key_errors(app, client): def test_trapping_of_bad_request_key_errors(app, client):
@app.route('/fail') @app.route('/fail')
def fail(): def fail():

Loading…
Cancel
Save