Browse Source

Improved interface for the URL build error handler

pull/502/merge
Armin Ronacher 13 years ago
parent
commit
2053d04db0
  1. 43
      flask/app.py
  2. 5
      flask/helpers.py
  3. 6
      flask/testsuite/basic.py

43
flask/app.py

@ -19,7 +19,7 @@ from itertools import chain
from functools import update_wrapper from functools import update_wrapper
from werkzeug.datastructures import ImmutableDict from werkzeug.datastructures import ImmutableDict
from werkzeug.routing import Map, Rule, RequestRedirect from werkzeug.routing import Map, Rule, RequestRedirect, BuildError
from werkzeug.exceptions import HTTPException, InternalServerError, \ from werkzeug.exceptions import HTTPException, InternalServerError, \
MethodNotAllowed, BadRequest MethodNotAllowed, BadRequest
@ -341,16 +341,14 @@ 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 #: A list of functions that are called when :meth:`url_for` raises a
#: :exc:`~werkzeug.routing.BuildError`, with the call signature:: #: :exc:`~werkzeug.routing.BuildError`. Each function registered here
#: #: is called with `error`, `endpoint` and `values`. If a function
#: self.build_error_handler(error, endpoint, **values) #: returns `None` or raises a `BuildError` the next function is
#: #: tried.
#: Here, `error` is the instance of `BuildError`, and `endpoint` and
#: `**values` are the arguments passed into :meth:`url_for`.
#: #:
#: .. versionadded:: 0.9 #: .. versionadded:: 0.9
self.build_error_handler = None self.url_build_error_handlers = []
#: 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
@ -1490,19 +1488,24 @@ 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): def handle_url_build_error(self, error, endpoint, values):
"""Handle :class:`~werkzeug.routing.BuildError` on :meth:`url_for`. """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: exc_type, exc_value, tb = sys.exc_info()
exc_type, exc_value, tb = sys.exc_info() for handler in self.url_build_error_handlers:
if exc_value is error: try:
# exception is current, raise in context of original traceback. rv = handler(error, endpoint, values)
raise exc_type, exc_value, tb if rv is not None:
else: return rv
raise error except BuildError, error:
return self.build_error_handler(error, endpoint, **values) pass
# At this point we want to reraise the exception. If the error is
# still the same one we can reraise it with the original traceback,
# otherwise we raise it from here.
if error is exc_value:
raise exc_type, exc_value, tb
raise error
def preprocess_request(self): def preprocess_request(self):
"""Called before the actual request dispatching and will """Called before the actual request dispatching and will

5
flask/helpers.py

@ -11,7 +11,6 @@
from __future__ import with_statement from __future__ import with_statement
import imp
import os import os
import sys import sys
import pkgutil import pkgutil
@ -303,10 +302,12 @@ def url_for(endpoint, **values):
rv = url_adapter.build(endpoint, values, method=method, rv = url_adapter.build(endpoint, values, method=method,
force_external=external) force_external=external)
except BuildError, error: except BuildError, error:
# We need to inject the values again so that the app callback can
# deal with that sort of stuff.
values['_external'] = external values['_external'] = external
values['_anchor'] = anchor values['_anchor'] = anchor
values['_method'] = method values['_method'] = method
return appctx.app.handle_build_error(error, endpoint, **values) return appctx.app.handle_url_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)

6
flask/testsuite/basic.py

@ -712,13 +712,13 @@ class BasicFunctionalityTestCase(FlaskTestCase):
try: try:
raise RuntimeError('Test case where BuildError is not current.') raise RuntimeError('Test case where BuildError is not current.')
except RuntimeError: except RuntimeError:
self.assertRaises(BuildError, app.handle_build_error, error, 'spam') self.assertRaises(BuildError, app.handle_url_build_error, error, 'spam', {})
# Test a custom handler. # Test a custom handler.
def handler(error, endpoint, **values): def handler(error, endpoint, values):
# Just a test. # Just a test.
return '/test_handler/' return '/test_handler/'
app.build_error_handler = handler app.url_build_error_handlers.append(handler)
with app.test_request_context(): with app.test_request_context():
self.assert_equal(flask.url_for('spam'), '/test_handler/') self.assert_equal(flask.url_for('spam'), '/test_handler/')

Loading…
Cancel
Save