Skip to content

Commit bb31188

Browse files
committed
Add a BuildError hook to url_for, pallets#456.
1 parent d90f0af commit bb31188

File tree

3 files changed

+46
-0
lines changed

3 files changed

+46
-0
lines changed

flask/app.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,17 @@ def __init__(self, import_name, static_path=None, static_url_path=None,
329329
#: decorator.
330330
self.error_handler_spec = {None: self._error_handlers}
331331

332+
#: If not `None`, this function is called when :meth:`url_for` raises
333+
#: :exc:`~werkzeug.routing.BuildError`, with the call signature::
334+
#:
335+
#: self.build_error_handler(error, endpoint, **values)
336+
#:
337+
#: Here, `error` is the instance of `BuildError`, and `endpoint` and
338+
#: `**values` are the arguments passed into :meth:`url_for`.
339+
#:
340+
#: .. versionadded:: 0.9
341+
self.build_error_handler = None
342+
332343
#: A dictionary with lists of functions that should be called at the
333344
#: beginning of the request. The key of the dictionary is the name of
334345
#: the blueprint this function is active for, `None` for all requests.
@@ -1473,6 +1484,15 @@ def inject_url_defaults(self, endpoint, values):
14731484
for func in funcs:
14741485
func(endpoint, values)
14751486

1487+
def handle_build_error(self, error, endpoint, **values):
1488+
"""Handle :class:`~werkzeug.routing.BuildError` on :meth:`url_for`.
1489+
1490+
Calls :attr:`build_error_handler` if it is not `None`.
1491+
"""
1492+
if self.build_error_handler is None:
1493+
raise error
1494+
return self.build_error_handler(error, endpoint, **values)
1495+
14761496
def preprocess_request(self):
14771497
"""Called before the actual request dispatching and will
14781498
call every as :meth:`before_request` decorated function.

flask/helpers.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from time import time
2121
from zlib import adler32
2222
from threading import RLock
23+
from werkzeug.routing import BuildError
2324
from werkzeug.urls import url_quote
2425

2526
# try to load the best simplejson implementation available. If JSON
@@ -214,6 +215,10 @@ def url_for(endpoint, **values):
214215
.. versionadded:: 0.9
215216
The `_anchor` and `_method` parameters were added.
216217
218+
.. versionadded:: 0.9
219+
Calls :meth:`Flask.handle_build_error` on
220+
:exc:`~werkzeug.routing.BuildError`.
221+
217222
:param endpoint: the endpoint of the URL (name of the function)
218223
:param values: the variable arguments of the URL rule
219224
:param _external: if set to `True`, an absolute URL is generated.
@@ -260,6 +265,15 @@ def url_for(endpoint, **values):
260265
anchor = values.pop('_anchor', None)
261266
method = values.pop('_method', None)
262267
appctx.app.inject_url_defaults(endpoint, values)
268+
try:
269+
rv = url_adapter.build(endpoint, values, method=method,
270+
force_external=external)
271+
except BuildError, error:
272+
values['_external'] = external
273+
values['_anchor'] = anchor
274+
values['_method'] = method
275+
return appctx.app.handle_build_error(error, endpoint, **values)
276+
263277
rv = url_adapter.build(endpoint, values, method=method,
264278
force_external=external)
265279
if anchor is not None:

flask/testsuite/basic.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning
2020
from werkzeug.exceptions import BadRequest, NotFound
2121
from werkzeug.http import parse_date
22+
from werkzeug.routing import BuildError
2223

2324

2425
class BasicFunctionalityTestCase(FlaskTestCase):
@@ -695,6 +696,17 @@ def hello():
695696
self.assert_equal(flask.url_for('hello', name='test x', _external=True),
696697
'http://localhost/hello/test%20x')
697698

699+
def test_build_error_handler(self):
700+
app = flask.Flask(__name__)
701+
with app.test_request_context():
702+
self.assertRaises(BuildError, flask.url_for, 'spam')
703+
def handler(error, endpoint, **values):
704+
# Just a test.
705+
return '/test_handler/'
706+
app.build_error_handler = handler
707+
with app.test_request_context():
708+
self.assert_equal(flask.url_for('spam'), '/test_handler/')
709+
698710
def test_custom_converters(self):
699711
from werkzeug.routing import BaseConverter
700712
class ListConverter(BaseConverter):

0 commit comments

Comments
 (0)