8
8
:copyright: (c) 2015 by Armin Ronacher.
9
9
:license: BSD, see LICENSE for more details.
10
10
"""
11
-
12
11
import os
13
12
import sys
14
13
from threading import Lock
15
14
from datetime import timedelta
16
15
from itertools import chain
17
16
from functools import update_wrapper
17
+ from collections import Mapping
18
18
19
19
from werkzeug .datastructures import ImmutableDict
20
20
from werkzeug .routing import Map , Rule , RequestRedirect , BuildError
21
21
from werkzeug .exceptions import HTTPException , InternalServerError , \
22
- MethodNotAllowed , BadRequest
22
+ MethodNotAllowed , BadRequest , default_exceptions
23
23
24
24
from .helpers import _PackageBoundObject , url_for , get_flashed_messages , \
25
25
locked_cached_property , _endpoint_from_view_func , find_package
33
33
_default_template_ctx_processor
34
34
from .signals import request_started , request_finished , got_request_exception , \
35
35
request_tearing_down , appcontext_tearing_down
36
- from ._compat import reraise , string_types , text_type , integer_types
36
+ from ._compat import reraise , string_types , text_type , integer_types , iterkeys
37
37
38
38
# a lock used for logger initialization
39
39
_logger_lock = Lock ()
@@ -1078,6 +1078,21 @@ def decorator(f):
1078
1078
return f
1079
1079
return decorator
1080
1080
1081
+ @staticmethod
1082
+ def _get_exc_class_and_code (exc_class_or_code ):
1083
+ """Ensure that we register only exceptions as handler keys"""
1084
+ if isinstance (exc_class_or_code , integer_types ):
1085
+ exc_class = default_exceptions [exc_class_or_code ]
1086
+ else :
1087
+ exc_class = exc_class_or_code
1088
+
1089
+ assert issubclass (exc_class , Exception )
1090
+
1091
+ if issubclass (exc_class , HTTPException ):
1092
+ return exc_class , exc_class .code
1093
+ else :
1094
+ return exc_class , None
1095
+
1081
1096
@setupmethod
1082
1097
def errorhandler (self , code_or_exception ):
1083
1098
"""A decorator that is used to register a function give a given
@@ -1136,16 +1151,21 @@ def register_error_handler(self, code_or_exception, f):
1136
1151
1137
1152
@setupmethod
1138
1153
def _register_error_handler (self , key , code_or_exception , f ):
1139
- if isinstance (code_or_exception , HTTPException ):
1140
- code_or_exception = code_or_exception .code
1141
- if isinstance (code_or_exception , integer_types ):
1142
- assert code_or_exception != 500 or key is None , \
1143
- 'It is currently not possible to register a 500 internal ' \
1144
- 'server error on a per-blueprint level.'
1145
- self .error_handler_spec .setdefault (key , {})[code_or_exception ] = f
1146
- else :
1147
- self .error_handler_spec .setdefault (key , {}).setdefault (None , []) \
1148
- .append ((code_or_exception , f ))
1154
+ """
1155
+ :type key: None|str
1156
+ :type code_or_exception: int|T<=Exception
1157
+ :type f: callable
1158
+ """
1159
+ if isinstance (code_or_exception , HTTPException ): # old broken behavior
1160
+ raise ValueError (
1161
+ 'Tried to register a handler for an exception instance {0!r}. '
1162
+ 'Handlers can only be registered for exception classes or HTTP error codes.'
1163
+ .format (code_or_exception ))
1164
+
1165
+ exc_class , code = self ._get_exc_class_and_code (code_or_exception )
1166
+
1167
+ handlers = self .error_handler_spec .setdefault (key , {}).setdefault (code , {})
1168
+ handlers [exc_class ] = f
1149
1169
1150
1170
@setupmethod
1151
1171
def template_filter (self , name = None ):
@@ -1386,22 +1406,46 @@ def url_defaults(self, f):
1386
1406
self .url_default_functions .setdefault (None , []).append (f )
1387
1407
return f
1388
1408
1409
+ def _find_error_handler (self , e ):
1410
+ """Finds a registered error handler for the request’s blueprint.
1411
+ If neither blueprint nor App has a suitable handler registered, returns None
1412
+ """
1413
+ exc_class , code = self ._get_exc_class_and_code (type (e ))
1414
+
1415
+ def find_superclass (handler_map ):
1416
+ if not handler_map :
1417
+ return None
1418
+ for superclass in exc_class .__mro__ :
1419
+ if superclass is BaseException :
1420
+ return None
1421
+ handler = handler_map .get (superclass )
1422
+ if handler is not None :
1423
+ handler_map [exc_class ] = handler # cache for next time exc_class is raised
1424
+ return handler
1425
+ return None
1426
+
1427
+ # try blueprint handlers
1428
+ handler = find_superclass (self .error_handler_spec .get (request .blueprint , {}).get (code ))
1429
+
1430
+ if handler is not None :
1431
+ return handler
1432
+
1433
+ # fall back to app handlers
1434
+ return find_superclass (self .error_handler_spec [None ].get (code ))
1435
+
1389
1436
def handle_http_exception (self , e ):
1390
1437
"""Handles an HTTP exception. By default this will invoke the
1391
1438
registered error handlers and fall back to returning the
1392
1439
exception as response.
1393
1440
1394
1441
.. versionadded:: 0.3
1395
1442
"""
1396
- handlers = self .error_handler_spec .get (request .blueprint )
1397
1443
# Proxy exceptions don't have error codes. We want to always return
1398
1444
# those unchanged as errors
1399
1445
if e .code is None :
1400
1446
return e
1401
- if handlers and e .code in handlers :
1402
- handler = handlers [e .code ]
1403
- else :
1404
- handler = self .error_handler_spec [None ].get (e .code )
1447
+
1448
+ handler = self ._find_error_handler (e )
1405
1449
if handler is None :
1406
1450
return e
1407
1451
return handler (e )
@@ -1443,20 +1487,15 @@ def handle_user_exception(self, e):
1443
1487
# wants the traceback preserved in handle_http_exception. Of course
1444
1488
# we cannot prevent users from trashing it themselves in a custom
1445
1489
# trap_http_exception method so that's their fault then.
1446
-
1447
- blueprint_handlers = ()
1448
- handlers = self .error_handler_spec .get (request .blueprint )
1449
- if handlers is not None :
1450
- blueprint_handlers = handlers .get (None , ())
1451
- app_handlers = self .error_handler_spec [None ].get (None , ())
1452
- for typecheck , handler in chain (blueprint_handlers , app_handlers ):
1453
- if isinstance (e , typecheck ):
1454
- return handler (e )
1455
-
1490
+
1456
1491
if isinstance (e , HTTPException ) and not self .trap_http_exception (e ):
1457
1492
return self .handle_http_exception (e )
1458
1493
1459
- reraise (exc_type , exc_value , tb )
1494
+ handler = self ._find_error_handler (e )
1495
+
1496
+ if handler is None :
1497
+ reraise (exc_type , exc_value , tb )
1498
+ return handler (e )
1460
1499
1461
1500
def handle_exception (self , e ):
1462
1501
"""Default exception handling that kicks in when an exception
@@ -1470,7 +1509,7 @@ def handle_exception(self, e):
1470
1509
exc_type , exc_value , tb = sys .exc_info ()
1471
1510
1472
1511
got_request_exception .send (self , exception = e )
1473
- handler = self .error_handler_spec [ None ]. get ( 500 )
1512
+ handler = self ._find_error_handler ( InternalServerError () )
1474
1513
1475
1514
if self .propagate_exceptions :
1476
1515
# if we want to repropagate the exception, we can attempt to
0 commit comments