diff options
| author | Chris McDonough <chrism@agendaless.com> | 2010-04-14 02:49:19 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2010-04-14 02:49:19 +0000 |
| commit | ff1213e8f2aed987108ba57aed517c033491b1aa (patch) | |
| tree | f531544c3373ae7d5b51746987cb373326277a9c /docs | |
| parent | 2b6bc8adfa294f7133680f64df411251afb67dfc (diff) | |
| download | pyramid-ff1213e8f2aed987108ba57aed517c033491b1aa.tar.gz pyramid-ff1213e8f2aed987108ba57aed517c033491b1aa.tar.bz2 pyramid-ff1213e8f2aed987108ba57aed517c033491b1aa.zip | |
Add "exception views" work contributed primarily by Andrey Popp by merging the "phash" branch.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/narr/configuration.rst | 5 | ||||
| -rw-r--r-- | docs/narr/hooks.rst | 107 | ||||
| -rw-r--r-- | docs/narr/urldispatch.rst | 3 | ||||
| -rw-r--r-- | docs/narr/views.rst | 91 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki/authorization.rst | 2 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki/src/authorization/tutorial/configure.zcml | 5 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki2/authorization.rst | 6 | ||||
| -rw-r--r-- | docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml | 5 | ||||
| -rw-r--r-- | docs/zcml/forbidden.rst | 16 | ||||
| -rw-r--r-- | docs/zcml/notfound.rst | 16 |
10 files changed, 197 insertions, 59 deletions
diff --git a/docs/narr/configuration.rst b/docs/narr/configuration.rst index 66ecd486c..a3336e735 100644 --- a/docs/narr/configuration.rst +++ b/docs/narr/configuration.rst @@ -198,9 +198,8 @@ effectively a "macro" which calls the behalf. The ``<view>`` tag is an example of a :mod:`repoze.bfg` declaration -tag. Other such tags include ``<route>``, ``<scan>``, ``<notfound>``, -``<forbidden>``, and others. Each of these tags is effectively a -"macro" which calls methods of a +tag. Other such tags include ``<route>`` and ``<scan>``. Each of +these tags is effectively a "macro" which calls methods of a :class:`repoze.bfg.configuration.Configurator` object on your behalf. Essentially, using a :term:`ZCML` file and loading it from the diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 9410f3b79..678b9dbc3 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -3,8 +3,8 @@ Using Hooks =========== -"Hooks" can be used to influence the behavior of the -:mod:`repoze.bfg` framework in various ways. +"Hooks" can be used to influence the behavior of the :mod:`repoze.bfg` +framework in various ways. .. index:: single: not found view @@ -15,23 +15,29 @@ Changing the Not Found View --------------------------- When :mod:`repoze.bfg` can't map a URL to view code, it invokes a -:term:`not found view`, which is a :term:`view callable`. The view it -invokes can be customized through application configuration. This -view can be configured via :term:`imperative configuration` or -:term:`ZCML`. +:term:`not found view`, which is a :term:`view callable`. A default +notfound view exists. The default not found view can be overridden +through application configuration. This override can be done via +:term:`imperative configuration` or :term:`ZCML`. + +The :term:`not found view` callable is a view callable like any other. +The :term:`view configuration` which causes it to be a "not found" +view consists only of naming the :exc:`repoze.bfg.exceptions.NotFound` +class as the ``context`` of the view configuration. .. topic:: Using Imperative Configuration If your application uses :term:`imperative configuration`, you can replace the Not Found view by using the - :meth:`repoze.bfg.configuration.Configurator.set_notfound_view` - method: + :meth:`repoze.bfg.configuration.Configurator.add_view` method to + register an "exception view": .. code-block:: python :linenos: - import helloworld.views - config.set_notfound_view(helloworld.views.notfound_view) + from repoze.bfg.exceptions import NotFound + from helloworld.views import notfound_view + config.add_view(notfound_view, context=NotFound) Replace ``helloworld.views.notfound_view`` with a reference to the Python :term:`view callable` you want to use to represent the Not @@ -46,16 +52,22 @@ view can be configured via :term:`imperative configuration` or .. code-block:: xml :linenos: - <notfound - view="helloworld.views.notfound_view"/> + <view + view="helloworld.views.notfound_view" + context="repoze.bfg.exceptions.NotFound"/> Replace ``helloworld.views.notfound_view`` with the Python dotted name to the notfound view you want to use. - Other attributes of the ``notfound`` directive are documented at - :ref:`notfound_directive`. +Like any other view, the notfound view must accept at least a +``request`` parameter, or both ``context`` and ``request``. The +``request`` is the current :term:`request` representing the denied +action. The ``context`` (if used in the call signature) will be the +instance of the :exc:`repoze.bfg.exceptions.NotFound` exception that +caused the view to be called. -Here's some sample code that implements a minimal NotFound view: +Here's some sample code that implements a minimal NotFound view +callable: .. code-block:: python :linenos: @@ -65,13 +77,21 @@ Here's some sample code that implements a minimal NotFound view: def notfound_view(request): return HTTPNotFound() -.. note:: When a NotFound view is invoked, it is passed a - :term:`request`. The ``environ`` attribute of the request is the - WSGI environment. Within the WSGI environ will be a key named - ``repoze.bfg.message`` that has a value explaining why the not - found error was raised. This error will be different when the - ``debug_notfound`` environment setting is true than it is when it - is false. +.. note:: When a NotFound view callable is invoked, it is passed a + :term:`request`. The ``exception`` attribute of the request will + be an instance of the :exc:`repoze.bfg.exceptions.NotFound` + exception that caused the not found view to be called. The value + of ``request.exception.args[0]`` will be a value explaining why the + not found error was raised. This message will be different when + the ``debug_notfound`` environment setting is true than it is when + it is false. + +.. warning:: When a NotFound view callable accepts an argument list as + described in :ref:`request_and_context_view_definitions`, the + ``context`` passed as the first argument to the view callable will + be the :exc:`repoze.bfg.exceptions.NotFound` exception instance. + If available, the *model* context will still be available as + ``request.context``. .. index:: single: forbidden view @@ -84,21 +104,28 @@ Changing the Forbidden View When :mod:`repoze.bfg` can't authorize execution of a view based on the :term:`authorization policy` in use, it invokes a :term:`forbidden view`. The default forbidden response has a 401 status code and is -very plain, but it can be overridden as necessary using either -:term:`imperative configuration` or :term:`ZCML`: +very plain, but the view which generates it can be overridden as +necessary using either :term:`imperative configuration` or +:term:`ZCML`: + +The :term:`forbidden view` callable is a view callable like any other. +The :term:`view configuration` which causes it to be a "not found" +view consists only of naming the :exc:`repoze.bfg.exceptions.Forbidden` +class as the ``context`` of the view configuration. .. topic:: Using Imperative Configuration If your application uses :term:`imperative configuration`, you can replace the Forbidden view by using the - :meth:`repoze.bfg.configuration.Configurator.set_forbidden_view` - method: + :meth:`repoze.bfg.configuration.Configurator.add_view` method to + register an "exception view": .. code-block:: python :linenos: - import helloworld.views - config.set_forbiddden_view(helloworld.views.forbidden_view) + from helloworld.views import forbidden_view + from repoze.bfg.exceptions import Forbidden + config.add_view(forbidden_view, context=Forbidden) Replace ``helloworld.views.forbidden_view`` with a reference to the Python :term:`view callable` you want to use to represent the @@ -113,16 +140,13 @@ very plain, but it can be overridden as necessary using either .. code-block:: xml :linenos: - <forbidden - view="helloworld.views.forbidden_view"/> - + <view + view="helloworld.views.notfound_view" + context="repoze.bfg.exceptions.Forbidden"/> Replace ``helloworld.views.forbidden_view`` with the Python dotted name to the forbidden view you want to use. - Other attributes of the ``forbidden`` directive are documented at - :ref:`forbidden_directive`. - Like any other view, the forbidden view must accept at least a ``request`` parameter, or both ``context`` and ``request``. The ``context`` (available as ``request.context`` if you're using the @@ -140,13 +164,14 @@ Here's some sample code that implements a minimal forbidden view: def forbidden_view(request): return render_template_to_response('templates/login_form.pt') -.. note:: When a forbidden view is invoked, it is passed the - :term:`request` as the second argument. An attribute of the - request is ``environ``, which is the WSGI environment. Within the - WSGI environ will be a key named ``repoze.bfg.message`` that has a - value explaining why the current view invocation was forbidden. - This error will be different when the ``debug_authorization`` - environment setting is true than it is when it is false. +.. note:: When a forbidden view callable is invoked, it is passed a + :term:`request`. The ``exception`` attribute of the request will + be an instance of the :exc:`repoze.bfg.exceptions.Forbidden` + exception that caused the forbidden view to be called. The value + of ``request.exception.args[0]`` will be a value explaining why the + forbidden was raised. This message will be different when the + ``debug_authorization`` environment setting is true than it is when + it is false. .. warning:: the default forbidden view sends a response with a ``401 Unauthorized`` status code for backwards compatibility reasons. diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 0c6b6cfe6..9e478ef2d 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -936,7 +936,8 @@ stanza: .. code-block:: xml :linenos: - <notfound + <view + context="repoze.bfg.exceptions.NotFound" view="repoze.bfg.views.append_slash_notfound_view" /> diff --git a/docs/narr/views.rst b/docs/narr/views.rst index 030d19052..a24e4b7b5 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -133,7 +133,7 @@ represent the method expected to return a response, you can use an .. _request_and_context_view_definitions: -Request-And-Context View Callable Definitions +Context-And-Request View Callable Definitions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Usually, view callables are defined to accept only a single argument: @@ -813,6 +813,8 @@ See also :ref:`renderer_directive` and .. index:: single: view exceptions +.. _special_exceptions_in_callables: + Using Special Exceptions In View Callables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -836,7 +838,92 @@ agent which performed the request. In all cases, the message provided to the exception constructor is made available to the view which :mod:`repoze.bfg` invokes as -``request.environ['repoze.bfg.message']``. +``request.exception.args[0]``. + +Exception Views +~~~~~~~~~~~~~~~~ + +The machinery which allows the special +:exc:`repoze.bfg.exceptions.NotFound` and +:exc:`repoze.bfg.exceptions.Forbidden` exceptions to be caught by +specialized views as described in +:ref:`special_exceptions_in_callables` can also be used by application +developers to convert arbitrary exceptions to responses. + +To register a view that should be called whenever a particular +exception is raised from with :mod:`repoze.bfg` view code, use the +exception class or one of its superclasses as the ``context`` of a +view configuration which points at a view callable you'd like to +generate a response. + +For example, given the following exception class in a module named +``helloworld.exceptions``: + +.. code-block:: python + :linenos: + + class ValidationFailure(Exception): + def __init__(self, msg): + self.msg = msg + + +You can wire a view callable to be called whenever any of your *other* +code raises a ``hellworld.exceptions.ValidationFailure`` exception: + +.. code-block:: python + :linenos: + + from helloworld.exceptions import ValidationFailure + + @bfg_view(context=ValidationFailure) + def failed_validation(exc, request): + response = Response('Failed validation: %s' % exc.msg) + response.status_int = 500 + return response + +Assuming that a :term:`scan` was run to pick up this view +registration, this view callable will be invoked whenever a +``helloworld.exceptions.ValidationError`` is raised by your +application's view code. The same exception raised by a custom root +factory or a custom traverser is also caught and hooked. + +Other normal view predicates can also be used in combination with an +exception view registration: + +.. code-block:: python + :linenos: + + from repoze.bfg.view import bfg_view + from repoze.bfg.exceptions import NotFound + from webob.exc import HTTPNotFound + + @bfg_view(context=NotFound, route_name='home') + def notfound_view(request): + return HTTPNotFound() + +The above exception view names the ``route_name`` of ``home``, meaning +that it will only be called when the route matched has a name of +``home``. You can therefore have more than one exception view for any +given exception in the system: the "most specific" one will be called +when the set of request circumstances which match the view +registration. + +The only view predicate that cannot be not be used successfully when +creating an exception view configuration is ``name``. The name used +to look up an exception view is always the empty string. Views +registered as exception views which have a name will be ignored. + +.. note:: + + Normal (non-exception) views registered against a context which + inherits from :exc:`Exception` will work normally. When an + exception view configuraton is processed, *two* exceptions are + registered. One as a "normal" view, the other as an "exception" + view. This means that you can use an exception as ``context`` for a + normal view. + +The feature can be used with any view registration mechanism +(``@bfg_view`` decorator, ZCML, or imperative ``add_view`` styles). .. index:: single: unicode, views, and forms diff --git a/docs/tutorials/bfgwiki/authorization.rst b/docs/tutorials/bfgwiki/authorization.rst index 1b83d3651..8c2ab1df9 100644 --- a/docs/tutorials/bfgwiki/authorization.rst +++ b/docs/tutorials/bfgwiki/authorization.rst @@ -27,7 +27,7 @@ Changing ``configure.zcml`` We'll change our ``configure.zcml`` file to enable an ``AuthTktAuthenticationPolicy`` and an ``ACLAuthorizationPolicy`` to -enable declarative security checking. We'll also add a ``forbidden`` +enable declarative security checking. We'll also add a new view stanza, which species a :term:`forbidden view`. This configures our login view to show up when :mod:`repoze.bfg` detects that a view invocation can not be authorized. When you're done, your diff --git a/docs/tutorials/bfgwiki/src/authorization/tutorial/configure.zcml b/docs/tutorials/bfgwiki/src/authorization/tutorial/configure.zcml index 837c04089..5297b9ee3 100644 --- a/docs/tutorials/bfgwiki/src/authorization/tutorial/configure.zcml +++ b/docs/tutorials/bfgwiki/src/authorization/tutorial/configure.zcml @@ -5,9 +5,10 @@ <scan package="."/> - <forbidden + <view view=".login.login" - renderer="templates/login.pt"/> + renderer="templates/login.pt" + context="repoze.bfg.exceptions.Forbidden"/> <authtktauthenticationpolicy secret="sosecret" diff --git a/docs/tutorials/bfgwiki2/authorization.rst b/docs/tutorials/bfgwiki2/authorization.rst index 9a37760f1..2f1a9e082 100644 --- a/docs/tutorials/bfgwiki2/authorization.rst +++ b/docs/tutorials/bfgwiki2/authorization.rst @@ -84,9 +84,9 @@ Changing ``configure.zcml`` We'll change our ``configure.zcml`` file to enable an ``AuthTktAuthenticationPolicy`` and an ``ACLAuthorizationPolicy`` to enable declarative security checking. We'll also change -``configure.zcml`` to add a ``forbidden`` stanza which points at our -``login`` :term:`view callable`, also known as a :term:`forbidden -view`. This configures our newly created login view to show up when +``configure.zcml`` to add a view stanza which points at our ``login`` +:term:`view callable`, also known as a :term:`forbidden view`. This +configures our newly created login view to show up when :mod:`repoze.bfg` detects that a view invocation can not be authorized. Also, we'll add ``view_permission`` attributes with the value ``edit`` to the ``edit_page`` and ``add_page`` route diff --git a/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml b/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml index 564cb7443..018892cd1 100644 --- a/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml +++ b/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml @@ -53,9 +53,10 @@ view_permission="edit" /> - <forbidden + <view view=".login.login" - renderer="templates/login.pt"/> + renderer="templates/login.pt" + for="repoze.bfg.exceptions.Forbidden"/> <authtktauthenticationpolicy secret="sosecret" diff --git a/docs/zcml/forbidden.rst b/docs/zcml/forbidden.rst index bd2235ccf..5a52a05ab 100644 --- a/docs/zcml/forbidden.rst +++ b/docs/zcml/forbidden.rst @@ -9,6 +9,14 @@ view`. The default forbidden response has a 401 status code and is very plain, but it can be overridden as necessary using the ``forbidden`` ZCML directive. +.. warning:: + + The ``forbidden`` ZCML directive is deprecated in :mod:`repoze.bfg` + version 1.3. Instead, you should use the :ref:`view_directive` + directive with a ``context`` that names the + :exc:`repoze.bfg.exceptions.Forbidden` class. See + :ref:`changing_the_forbidden_view` form more information. + Attributes ~~~~~~~~~~ @@ -63,8 +71,12 @@ Example Alternatives ~~~~~~~~~~~~ -The :meth:`repoze.bfg.configuration.Configurator.set_forbidden_view` -method performs the same job as the ``forbidden`` ZCML directive. +Use the :ref:`view_directive` directive with a ``context`` that names +the :exc:`repoze.bfg.exceptions.Forbidden` class. + +Use the :meth:`repoze.bfg.configuration.Configurator.add_view` method, +passing it a ``context`` which is the +:exc:`repoze.bfg.exceptions.Forbidden` class. See Also ~~~~~~~~ diff --git a/docs/zcml/notfound.rst b/docs/zcml/notfound.rst index 141cac6f9..3fe9900d4 100644 --- a/docs/zcml/notfound.rst +++ b/docs/zcml/notfound.rst @@ -3,6 +3,14 @@ ``notfound`` ------------ +.. warning:: + + The ``notfound`` ZCML directive is deprecated in :mod:`repoze.bfg` + version 1.3. Instead, you should use the :ref:`view_directive` + directive with a ``context`` that names the + :exc:`repoze.bfg.exceptions.NotFound` class. See + :ref:`changing_the_notfound_view` form more information. + When :mod:`repoze.bfg` can't map a URL to view code, it invokes a :term:`not found view`. The default not found view is very plain, but the view callable used can be configured via the ``notfound`` ZCML @@ -62,8 +70,12 @@ Example Alternatives ~~~~~~~~~~~~ -The :meth:`repoze.bfg.configuration.Configurator.set_notfound_view` -method performs the same job as the ``notfound`` ZCML directive. +Use the :ref:`view_directive` directive with a ``context`` that names +the :exc:`repoze.bfg.exceptions.NotFound` class. + +Use the :meth:`repoze.bfg.configuration.Configurator.add_view` method, +passing it a ``context`` which is the +:exc:`repoze.bfg.exceptions.NotFound` class. See Also ~~~~~~~~ |
