diff options
| author | Chris McDonough <chrism@agendaless.com> | 2010-08-08 07:25:28 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2010-08-08 07:25:28 +0000 |
| commit | d96ff9144f98bb44254f77f56e55967c46b09774 (patch) | |
| tree | 7cabc160ce28460ebffd70a5e419f1c74178bd96 /repoze/bfg/view.py | |
| parent | 9192964c9ccc4b0c1c2f1948af1b62012a11ef7c (diff) | |
| download | pyramid-d96ff9144f98bb44254f77f56e55967c46b09774.tar.gz pyramid-d96ff9144f98bb44254f77f56e55967c46b09774.tar.bz2 pyramid-d96ff9144f98bb44254f77f56e55967c46b09774.zip | |
- New public interface: ``repoze.bfg.exceptions.IExceptionResponse``.
This interface is provided by all internal exception classes (such
as ``repoze.bfg.exceptions.NotFound`` and
``repoze.bfg.exceptions.Forbidden``), instances of which are both
exception objects and can behave as WSGI response objects. This
interface is made public so that exception classes which are also
valid WSGI response factories can be configured to implement them
or exception instances which are also or response instances can be
configured to provide them.
- New API class: ``repoze.bfg.view.AppendSlashNotFoundViewFactory`` (undoes
previous custom_notfound_view on request passsed to
append_slash_notfound_view).
- Previously, two default view functions were registered at
Configurator setup (one for ``repoze.bfg.exceptions.NotFound`` named
``default_notfound_view`` and one for
``repoze.bfg.exceptions.Forbidden`` named
``default_forbidden_view``) to render internal exception responses.
Those default view functions have been removed, replaced with a
generic default view function which is registered at Configurator
setup for the ``repoze.bfg.interfaces.IExceptionResponse`` interface
that simply returns the exception instance; the ``NotFound` and
``Forbidden`` classes are now still exception factories but they are
also response factories which generate instances that implement the
new ``repoze.bfg.interfaces.IExceptionResponse`` interface.
Diffstat (limited to 'repoze/bfg/view.py')
| -rw-r--r-- | repoze/bfg/view.py | 193 |
1 files changed, 82 insertions, 111 deletions
diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py index 30076b775..1d1839530 100644 --- a/repoze/bfg/view.py +++ b/repoze/bfg/view.py @@ -1,4 +1,3 @@ -import cgi import mimetypes import os @@ -12,7 +11,6 @@ import os if hasattr(mimetypes, 'init'): mimetypes.init() -from webob import Response from webob.exc import HTTPFound import venusian @@ -20,7 +18,6 @@ import venusian from zope.deprecation import deprecated from zope.interface import providedBy -from repoze.bfg.interfaces import IResponseFactory from repoze.bfg.interfaces import IRoutesMapper from repoze.bfg.interfaces import IView from repoze.bfg.interfaces import IViewClassifier @@ -447,129 +444,103 @@ class bfg_view(object): return wrapped -def default_view(context, request, status): +def default_exceptionresponse_view(context, request): if not isinstance(context, Exception): - # backwards compat for a default_view registered via + # backwards compat for an exception response view registered via # config.set_notfound_view or config.set_forbidden_view # instead of as a proper exception view - context = getattr(request, 'exception', None) - try: - msg = cgi.escape('%s' % context.args[0]) - except Exception: - msg = '' - html = """ - <html> - <title>%s</title> - <body> - <h1>%s</h1> - <code>%s</code> - </body> - </html> - """ % (status, status, msg) - headers = [('Content-Length', str(len(html))), - ('Content-Type', 'text/html')] - try: - registry = request.registry - except AttributeError: - registry = get_current_registry() - response_factory = registry.queryUtility(IResponseFactory, - default=Response) - return response_factory(status = status, - headerlist = headers, - app_iter = [html]) - -def default_forbidden_view(context, request): - return default_view(context, request, '401 Unauthorized') - -def default_notfound_view(context, request): - return default_view(context, request, '404 Not Found') - -def append_slash_notfound_view(context, request): - """For behavior like Django's ``APPEND_SLASH=True``, use this view - as the :term:`Not Found view` in your application. - - When this view is the Not Found view (indicating that no view was - found), and any routes have been defined in the configuration of - your application, if the value of the ``PATH_INFO`` WSGI - environment variable does not already end in a slash, and if the - value of ``PATH_INFO`` *plus* a slash matches any route's path, do - an HTTP redirect to the slash-appended PATH_INFO. Note that this - will *lose* ``POST`` data information (turning it into a GET), so - you shouldn't rely on this to redirect POST requests. - - If you use :term:`ZCML`, add the following to your application's - ``configure.zcml`` to use this view as the Not Found view:: + context = getattr(request, 'exception', context) + return context + +class AppendSlashNotFoundViewFactory(object): + """ There can only be one :term:`Not Found view` in any + :mod:`repoze.bfg application. Even if you use + :func:`repoze.bfg.view.append_slash_notfound_view` as the Not + Found view, :mod:`repoze.bfg` still must generate a ``404 Not + Found`` response when it cannot redirect to a slash-appended URL; + this not found response will be visible to site users. + + If you don't care what this 404 response looks like, and you only + need redirections to slash-appended route URLs, you may use the + :func:`repoze.bfg.view.append_slash_notfound_view` object as the + Not Found view. However, if you wish to use a *custom* notfound + view callable when a URL cannot be redirected to a slash-appended + URL, you may wish to use an instance of this class as the Not + Found view, supplying a :term:`view callable` to be used as the + custom notfound view as the first argument to its constructor. + For instance: - <view - context="repoze.bfg.exceptions.NotFound" - view="repoze.bfg.view.append_slash_notfound_view"/> + .. code-block:: python - Or use the - :meth:`repoze.bfg.configuration.Configurator.add_view` - method if you don't use ZCML:: + from repoze.bfg.exceptions import NotFound + from repoze.bfg.view import AppendSlashNotFoundViewFactory - from repoze.bfg.exceptions import NotFound - from repoze.bfg.view import append_slash_notfound_view - config.add_view(append_slash_notfound_view, context=NotFound) + def notfound_view(context, request): + return HTTPNotFound('It aint there, stop trying!') - See also :ref:`changing_the_notfound_view`. + custom_append_slash = AppendSlashNotFoundViewFactory(notfound_view) + config.add_view(custom_append_slash, context=NotFound) - .. note:: This function is new as of :mod:`repoze.bfg` version 1.1. + The ``notfound_view`` supplied must adhere to the two-argument + view callable calling convention of ``(context, request)`` + (``context`` will be the exception object). - There can only be one Not Found view in any :mod:`repoze.bfg - application. If you use ``append_slash_notfound_view`` as the Not - Found view, it still must generate a NotFound response when it - cannot redirect to a slash-appended URL; this not found response - will be visible to site users. + .. note:: This class is new as of :mod:`repoze.bfg` version 1.3. - If you wish to use a custom notfound view callable when - ``append_slash_notfound_view`` does not redirect to a - slash-appended URL, use a wrapper function as the - :exc:`repoze.bfg.exceptions.NotFound` view; have this wrapper - attach a :term:`view callable` which returns a response to the - request object named ``custom_notfound_view`` before calling - ``append_slash_notfound_view``. For example: + """ + def __init__(self, notfound_view=None): + if notfound_view is None: + notfound_view = default_exceptionresponse_view + self.notfound_view = notfound_view + + def __call__(self, context, request): + if not isinstance(context, Exception): + # backwards compat for an append_notslash_view registered via + # config.set_notfound_view instead of as a proper exception view + context = getattr(request, 'exception', None) + path = request.environ.get('PATH_INFO', '/') + registry = request.registry + mapper = registry.queryUtility(IRoutesMapper) + if mapper is not None and not path.endswith('/'): + slashpath = path + '/' + for route in mapper.get_routes(): + if route.match(slashpath) is not None: + return HTTPFound(location=slashpath) + return self.notfound_view(context, request) + +append_slash_notfound_view = AppendSlashNotFoundViewFactory() +append_slash_notfound_view.__doc__ = """\ +For behavior like Django's ``APPEND_SLASH=True``, use this view as the +:term:`Not Found view` in your application. + +When this view is the Not Found view (indicating that no view was +found), and any routes have been defined in the configuration of your +application, if the value of the ``PATH_INFO`` WSGI environment +variable does not already end in a slash, and if the value of +``PATH_INFO`` *plus* a slash matches any route's path, do an HTTP +redirect to the slash-appended PATH_INFO. Note that this will *lose* +``POST`` data information (turning it into a GET), so you shouldn't +rely on this to redirect POST requests. + +If you use :term:`ZCML`, add the following to your application's +``configure.zcml`` to use this view as the Not Found view:: - .. code-block:: python + <view + context="repoze.bfg.exceptions.NotFound" + view="repoze.bfg.view.append_slash_notfound_view"/> - from webob.exc import HTTPNotFound - from repoze.bfg.exceptions import NotFound - from repoze.bfg.view import append_slash_notfound_view +Or use the +:meth:`repoze.bfg.configuration.Configurator.add_view` +method if you don't use ZCML:: - def notfound_view(exc, request): - def fallback_notfound_view(exc, request): - return HTTPNotFound('It aint there, stop trying!') - request.fallback_notfound_view = fallback_notfound_view - return append_slash_notfound_view(exc, request) + from repoze.bfg.exceptions import NotFound + from repoze.bfg.view import append_slash_notfound_view + config.add_view(append_slash_notfound_view, context=NotFound) - config.add_view(notfound_view, context=NotFound) +See also :ref:`changing_the_notfound_view`. - ``custom_notfound_view`` must adhere to the two-argument view - callable calling convention of ``(context, request)`` (``context`` - will be the exception object). +.. note:: This function is new as of :mod:`repoze.bfg` version 1.1. +""" - If ``custom_notfound_view`` is not found on the request object, a - default notfound response will be generated when the - ``append_slash_notfound_view`` doesn't redirect to a - slash-appended URL. - .. note:: The checking for ``request.custom_notfound_view`` by - ``append_slash_notfound_view`` is new as of :mod:`repoze.bfg` - version 1.3. - """ - if not isinstance(context, Exception): - # backwards compat for an append_notslash_view registered via - # config.set_notfound_view instead of as a proper exception view - context = getattr(request, 'exception', None) - path = request.environ.get('PATH_INFO', '/') - registry = request.registry - mapper = registry.queryUtility(IRoutesMapper) - if mapper is not None and not path.endswith('/'): - slashpath = path + '/' - for route in mapper.get_routes(): - if route.match(slashpath) is not None: - return HTTPFound(location=slashpath) - notfound_view = getattr(request, 'custom_notfound_view', - default_notfound_view) - return notfound_view(context, request) |
