Browse Source

Add a BuildError hook to url_for, #456.

pull/498/head
Ron DuPlain 13 years ago
parent
commit
bb31188ec3
  1. 20
      flask/app.py
  2. 14
      flask/helpers.py
  3. 12
      flask/testsuite/basic.py

20
flask/app.py

@ -329,6 +329,17 @@ class Flask(_PackageBoundObject):
#: decorator. #: decorator.
self.error_handler_spec = {None: self._error_handlers} self.error_handler_spec = {None: self._error_handlers}
#: If not `None`, this function is called when :meth:`url_for` raises
#: :exc:`~werkzeug.routing.BuildError`, with the call signature::
#:
#: self.build_error_handler(error, endpoint, **values)
#:
#: Here, `error` is the instance of `BuildError`, and `endpoint` and
#: `**values` are the arguments passed into :meth:`url_for`.
#:
#: .. versionadded:: 0.9
self.build_error_handler = None
#: A dictionary with lists of functions that should be called at the #: A dictionary with lists of functions that should be called at the
#: beginning of the request. The key of the dictionary is the name of #: beginning of the request. The key of the dictionary is the name of
#: the blueprint this function is active for, `None` for all requests. #: the blueprint this function is active for, `None` for all requests.
@ -1473,6 +1484,15 @@ class Flask(_PackageBoundObject):
for func in funcs: for func in funcs:
func(endpoint, values) func(endpoint, values)
def handle_build_error(self, error, endpoint, **values):
"""Handle :class:`~werkzeug.routing.BuildError` on :meth:`url_for`.
Calls :attr:`build_error_handler` if it is not `None`.
"""
if self.build_error_handler is None:
raise error
return self.build_error_handler(error, endpoint, **values)
def preprocess_request(self): def preprocess_request(self):
"""Called before the actual request dispatching and will """Called before the actual request dispatching and will
call every as :meth:`before_request` decorated function. call every as :meth:`before_request` decorated function.

14
flask/helpers.py

@ -20,6 +20,7 @@ import mimetypes
from time import time from time import time
from zlib import adler32 from zlib import adler32
from threading import RLock from threading import RLock
from werkzeug.routing import BuildError
from werkzeug.urls import url_quote from werkzeug.urls import url_quote
# try to load the best simplejson implementation available. If JSON # try to load the best simplejson implementation available. If JSON
@ -214,6 +215,10 @@ def url_for(endpoint, **values):
.. versionadded:: 0.9 .. versionadded:: 0.9
The `_anchor` and `_method` parameters were added. The `_anchor` and `_method` parameters were added.
.. versionadded:: 0.9
Calls :meth:`Flask.handle_build_error` on
:exc:`~werkzeug.routing.BuildError`.
:param endpoint: the endpoint of the URL (name of the function) :param endpoint: the endpoint of the URL (name of the function)
:param values: the variable arguments of the URL rule :param values: the variable arguments of the URL rule
:param _external: if set to `True`, an absolute URL is generated. :param _external: if set to `True`, an absolute URL is generated.
@ -260,6 +265,15 @@ def url_for(endpoint, **values):
anchor = values.pop('_anchor', None) anchor = values.pop('_anchor', None)
method = values.pop('_method', None) method = values.pop('_method', None)
appctx.app.inject_url_defaults(endpoint, values) appctx.app.inject_url_defaults(endpoint, values)
try:
rv = url_adapter.build(endpoint, values, method=method,
force_external=external)
except BuildError, error:
values['_external'] = external
values['_anchor'] = anchor
values['_method'] = method
return appctx.app.handle_build_error(error, endpoint, **values)
rv = url_adapter.build(endpoint, values, method=method, rv = url_adapter.build(endpoint, values, method=method,
force_external=external) force_external=external)
if anchor is not None: if anchor is not None:

12
flask/testsuite/basic.py

@ -19,6 +19,7 @@ from threading import Thread
from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning
from werkzeug.exceptions import BadRequest, NotFound from werkzeug.exceptions import BadRequest, NotFound
from werkzeug.http import parse_date from werkzeug.http import parse_date
from werkzeug.routing import BuildError
class BasicFunctionalityTestCase(FlaskTestCase): class BasicFunctionalityTestCase(FlaskTestCase):
@ -695,6 +696,17 @@ class BasicFunctionalityTestCase(FlaskTestCase):
self.assert_equal(flask.url_for('hello', name='test x', _external=True), self.assert_equal(flask.url_for('hello', name='test x', _external=True),
'http://localhost/hello/test%20x') 'http://localhost/hello/test%20x')
def test_build_error_handler(self):
app = flask.Flask(__name__)
with app.test_request_context():
self.assertRaises(BuildError, flask.url_for, 'spam')
def handler(error, endpoint, **values):
# Just a test.
return '/test_handler/'
app.build_error_handler = handler
with app.test_request_context():
self.assert_equal(flask.url_for('spam'), '/test_handler/')
def test_custom_converters(self): def test_custom_converters(self):
from werkzeug.routing import BaseConverter from werkzeug.routing import BaseConverter
class ListConverter(BaseConverter): class ListConverter(BaseConverter):

Loading…
Cancel
Save