Skip to content

Commit 2053d04

Browse files
committed
Improved interface for the URL build error handler
1 parent e78e2a1 commit 2053d04

File tree

3 files changed

+29
-25
lines changed

3 files changed

+29
-25
lines changed

flask/app.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from functools import update_wrapper
2020

2121
from werkzeug.datastructures import ImmutableDict
22-
from werkzeug.routing import Map, Rule, RequestRedirect
22+
from werkzeug.routing import Map, Rule, RequestRedirect, BuildError
2323
from werkzeug.exceptions import HTTPException, InternalServerError, \
2424
MethodNotAllowed, BadRequest
2525

@@ -341,16 +341,14 @@ def __init__(self, import_name, static_path=None, static_url_path=None,
341341
#: decorator.
342342
self.error_handler_spec = {None: self._error_handlers}
343343

344-
#: If not `None`, this function is called when :meth:`url_for` raises
345-
#: :exc:`~werkzeug.routing.BuildError`, with the call signature::
346-
#:
347-
#: self.build_error_handler(error, endpoint, **values)
348-
#:
349-
#: Here, `error` is the instance of `BuildError`, and `endpoint` and
350-
#: `**values` are the arguments passed into :meth:`url_for`.
344+
#: A list of functions that are called when :meth:`url_for` raises a
345+
#: :exc:`~werkzeug.routing.BuildError`. Each function registered here
346+
#: is called with `error`, `endpoint` and `values`. If a function
347+
#: returns `None` or raises a `BuildError` the next function is
348+
#: tried.
351349
#:
352350
#: .. versionadded:: 0.9
353-
self.build_error_handler = None
351+
self.url_build_error_handlers = []
354352

355353
#: A dictionary with lists of functions that should be called at the
356354
#: beginning of the request. The key of the dictionary is the name of
@@ -1490,19 +1488,24 @@ def inject_url_defaults(self, endpoint, values):
14901488
for func in funcs:
14911489
func(endpoint, values)
14921490

1493-
def handle_build_error(self, error, endpoint, **values):
1491+
def handle_url_build_error(self, error, endpoint, values):
14941492
"""Handle :class:`~werkzeug.routing.BuildError` on :meth:`url_for`.
1495-
1496-
Calls :attr:`build_error_handler` if it is not `None`.
14971493
"""
1498-
if self.build_error_handler is None:
1499-
exc_type, exc_value, tb = sys.exc_info()
1500-
if exc_value is error:
1501-
# exception is current, raise in context of original traceback.
1502-
raise exc_type, exc_value, tb
1503-
else:
1504-
raise error
1505-
return self.build_error_handler(error, endpoint, **values)
1494+
exc_type, exc_value, tb = sys.exc_info()
1495+
for handler in self.url_build_error_handlers:
1496+
try:
1497+
rv = handler(error, endpoint, values)
1498+
if rv is not None:
1499+
return rv
1500+
except BuildError, error:
1501+
pass
1502+
1503+
# At this point we want to reraise the exception. If the error is
1504+
# still the same one we can reraise it with the original traceback,
1505+
# otherwise we raise it from here.
1506+
if error is exc_value:
1507+
raise exc_type, exc_value, tb
1508+
raise error
15061509

15071510
def preprocess_request(self):
15081511
"""Called before the actual request dispatching and will

flask/helpers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from __future__ import with_statement
1313

14-
import imp
1514
import os
1615
import sys
1716
import pkgutil
@@ -303,10 +302,12 @@ def external_url_handler(error, endpoint, **values):
303302
rv = url_adapter.build(endpoint, values, method=method,
304303
force_external=external)
305304
except BuildError, error:
305+
# We need to inject the values again so that the app callback can
306+
# deal with that sort of stuff.
306307
values['_external'] = external
307308
values['_anchor'] = anchor
308309
values['_method'] = method
309-
return appctx.app.handle_build_error(error, endpoint, **values)
310+
return appctx.app.handle_url_build_error(error, endpoint, values)
310311

311312
rv = url_adapter.build(endpoint, values, method=method,
312313
force_external=external)

flask/testsuite/basic.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -712,13 +712,13 @@ def test_build_error_handler(self):
712712
try:
713713
raise RuntimeError('Test case where BuildError is not current.')
714714
except RuntimeError:
715-
self.assertRaises(BuildError, app.handle_build_error, error, 'spam')
715+
self.assertRaises(BuildError, app.handle_url_build_error, error, 'spam', {})
716716

717717
# Test a custom handler.
718-
def handler(error, endpoint, **values):
718+
def handler(error, endpoint, values):
719719
# Just a test.
720720
return '/test_handler/'
721-
app.build_error_handler = handler
721+
app.url_build_error_handlers.append(handler)
722722
with app.test_request_context():
723723
self.assert_equal(flask.url_for('spam'), '/test_handler/')
724724

0 commit comments

Comments
 (0)