diff options
| author | Chris McDonough <chrism@plope.com> | 2010-12-26 01:13:26 -0500 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2010-12-26 01:13:26 -0500 |
| commit | e1a7e0679759da628676f3c73c34875e9b2b6a43 (patch) | |
| tree | 24fde2f1d7315c457a5a02094a684e762307a1d6 /docs | |
| parent | f5bafd1da8da405936bae032d20dba545922860e (diff) | |
| download | pyramid-e1a7e0679759da628676f3c73c34875e9b2b6a43.tar.gz pyramid-e1a7e0679759da628676f3c73c34875e9b2b6a43.tar.bz2 pyramid-e1a7e0679759da628676f3c73c34875e9b2b6a43.zip | |
- Move ZCML usage in Hooks chapter to Declarative Configuration chapter.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/narr/declarative.rst | 140 | ||||
| -rw-r--r-- | docs/narr/hooks.rst | 470 |
2 files changed, 338 insertions, 272 deletions
diff --git a/docs/narr/declarative.rst b/docs/narr/declarative.rst index deccb6c48..12deb90e7 100644 --- a/docs/narr/declarative.rst +++ b/docs/narr/declarative.rst @@ -1266,9 +1266,143 @@ which we assume lives in a ``subscribers.py`` module within your application: See also :ref:`subscriber_directive` and :ref:`events_chapter`. +.. index:: + single: not found view + +.. _notfound_zcml: + +Configuring a Not Found View via ZCML +------------------------------------- + +If your application uses :term:`ZCML`, you can replace the Not Found view by +placing something like the following ZCML in your ``configure.zcml`` file. + +.. code-block:: xml + :linenos: + + <view + view="helloworld.views.notfound_view" + context="pyramid.exceptions.NotFound" + /> + +Replace ``helloworld.views.notfound_view`` with the Python dotted name to the +notfound view you want to use. + +See :ref:`changing_the_notfound_view` for more information. + +.. index:: + single: forbidden view + +.. _forbidden_zcml: + +Configuring a Forbidden View via ZCML +------------------------------------- + +If your application uses :term:`ZCML`, you can replace the Forbidden view by +placing something like the following ZCML in your ``configure.zcml`` file. + +.. code-block:: xml + :linenos: + + <view + view="helloworld.views.notfound_view" + context="pyramid.exceptions.Forbidden" + /> + +Replace ``helloworld.views.forbidden_view`` with the Python dotted name to +the forbidden view you want to use. + +See :ref:`changing_the_forbidden_view` for more information. + +.. _changing_traverser_zcml: + +Configuring an Alternate Traverser via ZCML +------------------------------------------- + +Use an ``adapter`` stanza in your application's ``configure.zcml`` to +change the default traverser: + +.. code-block:: xml + :linenos: + + <adapter + factory="myapp.traversal.Traverser" + provides="pyramid.interfaces.ITraverser" + for="*" + /> + +Or to register a traverser for a specific resource type: -.. Todo -.. ---- +.. code-block:: xml + :linenos: + + <adapter + factory="myapp.traversal.Traverser" + provides="pyramid.interfaces.ITraverser" + for="myapp.resources.MyRoot" + /> + +See :ref:`changing_the_traverser` for more information. + +.. index:: + single: url generator + +.. _changing_resource_url_zcml: + +Changing ``resource_url`` URL Generation via ZCML +------------------------------------------------- + +You can change how :func:`pyramid.url.resource_url` generates a URL for a +specific type of resource by adding an adapter statement to your +``configure.zcml``. + +.. code-block:: xml + :linenos: + + <adapter + factory="myapp.traversal.URLGenerator" + provides="pyramid.interfaces.IContextURL" + for="myapp.resources.MyRoot *" + /> + +See :ref:`changing_resource_url` for more information. + +.. _changing_request_factory_zcml: + +Changing the Request Factory via ZCML +------------------------------------- + +A ``MyRequest`` class can be registered via ZCML as a request factory through +the use of the ZCML ``utility`` directive. In the below, we assume it lives +in a package named ``mypackage.mymodule``. + +.. code-block:: xml + :linenos: + + <utility + component="mypackage.mymodule.MyRequest" + provides="pyramid.interfaces.IRequestFactory" + /> + +See :ref:`changing_request_factory` for more information. + +.. _adding_renderer_globals_zcml: + +Changing the Renderer Globals Factory via ZCML +---------------------------------------------- + +A renderer globals factory can be registered via ZCML as a through the use of +the ZCML ``utility`` directive. In the below, we assume a +``renderers_globals_factory`` function lives in a package named +``mypackage.mymodule``. + +.. code-block:: xml + :linenos: + + <utility + component="mypackage.mymodule.renderer_globals_factory" + provides="pyramid.interfaces.IRendererGlobalsFactory" + /> -.. - hooks chapter still has topics for ZCML +See :ref:`adding_renderer_globals` for more information. diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 006f5d5cb..381da0d39 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 :app:`Pyramid` -framework in various ways. +"Hooks" can be used to influence the behavior of the :app:`Pyramid` framework +in various ways. .. index:: single: not found view @@ -14,61 +14,38 @@ framework in various ways. Changing the Not Found View --------------------------- -When :app:`Pyramid` can't map a URL to view code, it invokes a -: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`. +When :app:`Pyramid` can't map a URL to view code, it invokes a :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. -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:`pyramid.exceptions.NotFound` -class as the ``context`` of the view configuration. +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:`pyramid.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:`pyramid.config.Configurator.add_view` +method to register an "exception view": - If your application uses :term:`imperative configuration`, you can - replace the Not Found view by using the - :meth:`pyramid.config.Configurator.add_view` method to - register an "exception view": - - .. code-block:: python - :linenos: - - from pyramid.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 - Found view. - -.. topic:: Using ZCML - - If your application uses :term:`ZCML`, you can replace the Not Found - view by placing something like the following ZCML in your - ``configure.zcml`` file. - - .. code-block:: xml - :linenos: +.. code-block:: python + :linenos: - <view - view="helloworld.views.notfound_view" - context="pyramid.exceptions.NotFound" - /> + from pyramid.exceptions import NotFound + from helloworld.views import notfound_view + config.add_view(notfound_view, context=NotFound) - Replace ``helloworld.views.notfound_view`` with the Python dotted name - to the notfound view you want to use. +Replace ``helloworld.views.notfound_view`` with a reference to the +:term:`view callable` you want to use to represent the Not Found view. -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:`pyramid.exceptions.NotFound` exception that -caused the view to be called. +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:`pyramid.exceptions.NotFound` exception that caused the view to be +called. -Here's some sample code that implements a minimal NotFound view -callable: +Here's some sample code that implements a minimal NotFound view callable: .. code-block:: python :linenos: @@ -93,6 +70,9 @@ callable: :exc:`pyramid.exceptions.NotFound` exception instance. If available, the resource context will still be available as ``request.context``. +For information about how to configure a not found view via :term:`ZCML`, see +:ref:`notfound_zcml`. + .. index:: single: forbidden view @@ -101,59 +81,36 @@ callable: Changing the Forbidden View --------------------------- -When :app:`Pyramid` 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 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:`pyramid.exceptions.Forbidden` -class as the ``context`` of the view configuration. +When :app:`Pyramid` 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 +the view which generates it can be overridden as necessary. -.. topic:: Using Imperative Configuration +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:`pyramid.exceptions.Forbidden` class as the +``context`` of the view configuration. - If your application uses :term:`imperative configuration`, you can - replace the Forbidden view by using the - :meth:`pyramid.config.Configurator.add_view` method to - register an "exception view": +You can replace the forbidden view by using the +:meth:`pyramid.config.Configurator.add_view` method to register an "exception +view": - .. code-block:: python - :linenos: - - from helloworld.views import forbidden_view - from pyramid.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 - Forbidden view. - -.. topic:: Using ZCML - - If your application uses :term:`ZCML`, you can replace the - Forbidden view by placing something like the following ZCML in your - ``configure.zcml`` file. - - .. code-block:: xml - :linenos: +.. code-block:: python + :linenos: - <view - view="helloworld.views.notfound_view" - context="pyramid.exceptions.Forbidden" - /> + from helloworld.views import forbidden_view + from pyramid.exceptions import Forbidden + config.add_view(forbidden_view, context=Forbidden) - Replace ``helloworld.views.forbidden_view`` with the Python - dotted name to the forbidden view you want to use. +Replace ``helloworld.views.forbidden_view`` with a reference to the Python +:term:`view callable` you want to use to represent the Forbidden view. -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 -request-only view argument pattern) is the context found by the router -when the view invocation was denied. The ``request`` is the current -:term:`request` representing the denied action. +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 request-only view argument +pattern) is the context found by the router when the view invocation was +denied. The ``request`` is the current :term:`request` representing the +denied action. Here's some sample code that implements a minimal forbidden view: @@ -161,10 +118,10 @@ Here's some sample code that implements a minimal forbidden view: :linenos: from pyramid.views import view_config + from pyramid.response import Response - @view_config(renderer='templates/login_form.pt') def forbidden_view(request): - return {} + return Response('forbidden') .. note:: When a forbidden view callable is invoked, it is passed a :term:`request`. The ``exception`` attribute of the request will @@ -181,6 +138,9 @@ Here's some sample code that implements a minimal forbidden view: an alternate forbidden view. For example, it would make sense to return a response with a ``403 Forbidden`` status code. +For information about how to configure a forbidden view via :term:`ZCML`, see +:ref:`forbidden_zcml`. + .. index:: single: traverser @@ -189,25 +149,22 @@ Here's some sample code that implements a minimal forbidden view: Changing the Traverser ---------------------- -The default :term:`traversal` algorithm that :app:`Pyramid` uses is -explained in :ref:`traversal_algorithm`. Though it is rarely -necessary, this default algorithm can be swapped out selectively for a -different traversal pattern via configuration. +The default :term:`traversal` algorithm that :app:`Pyramid` uses is explained +in :ref:`traversal_algorithm`. Though it is rarely necessary, this default +algorithm can be swapped out selectively for a different traversal pattern +via configuration. -Use an ``adapter`` stanza in your application's ``configure.zcml`` to -change the default traverser: - -.. code-block:: xml +.. code-block:: python :linenos: - <adapter - factory="myapp.traversal.Traverser" - provides="pyramid.interfaces.ITraverser" - for="*" - /> + from pyramid.interfaces import ITraverser + from zope.interface import Interface + from myapp.traversal import Traverser -In the example above, ``myapp.traversal.Traverser`` is assumed to be -a class that implements the following interface: + config.registry.registerAdapter(Traverser, (Interface,), ITraverser) + +In the example above, ``myapp.traversal.Traverser`` is assumed to be a class +that implements the following interface: .. code-block:: python :linenos: @@ -241,31 +198,36 @@ a class that implements the following interface: """ More than one traversal algorithm can be active at the same time. For -instance, if your :term:`root factory` returns more than one type of -object conditionally, you could claim that an alternate traverser -adapter is ``for`` only one particular class or interface. When the -root factory returned an object that implemented that class or -interface, a custom traverser would be used. Otherwise, the default -traverser would be used. For example: - -.. code-block:: xml +instance, if your :term:`root factory` returns more than one type of object +conditionally, you could claim that an alternate traverser adapter is ``for`` +only one particular class or interface. When the root factory returned an +object that implemented that class or interface, a custom traverser would be +used. Otherwise, the default traverser would be used. For example: + +.. code-block:: python :linenos: - <adapter - factory="myapp.traversal.Traverser" - provides="pyramid.interfaces.ITraverser" - for="myapp.resources.MyRoot" - /> + from pyramid.interfaces import ITraverser + from zope.interface import Interface + from myapp.traversal import Traverser + from myapp.resources import MyRoot -If the above stanza was added to a ``configure.zcml`` file, -:app:`Pyramid` would use the ``myapp.traversal.Traverser`` only + config.registry.registerAdapter(Traverser, (MyRoot,), ITraverser) + +If the above stanza was added to a Pyramid ``__init__.py`` file's ``main`` +function, :app:`Pyramid` would use the ``myapp.traversal.Traverser`` only when the application :term:`root factory` returned an instance of the ``myapp.resources.MyRoot`` object. Otherwise it would use the default :app:`Pyramid` traverser to do traversal. +For information about how to configure an alternate traverser via +:term:`ZCML`, see :ref:`changing_traverser_zcml`. + .. index:: single: url generator +.. _changing_resource_url: + Changing How :mod:`pyramid.url.resource_url` Generates a URL ------------------------------------------------------------ @@ -276,25 +238,27 @@ generates by default may be incorrect. If you've added a traverser, you can change how :func:`pyramid.url.resource_url` generates a URL for a specific type of -resource by adding an adapter stanza for -:class:`pyramid.interfaces.IContextURL` to your application's -``configure.zcml``: +resource by adding a registerAdapter call for +:class:`pyramid.interfaces.IContextURL` to your application: -.. code-block:: xml +.. code-block:: python :linenos: - <adapter - factory="myapp.traversal.URLGenerator" - provides="pyramid.interfaces.IContextURL" - for="myapp.resources.MyRoot *" - /> + from pyramid.interfaces import ITraverser + from zope.interface import Interface + from myapp.traversal import URLGenerator + from myapp.resources import MyRoot + + config.registry.registerAdapter(URLGenerator, (MyRoot, Interface), + IContextURL) -In the above example, the ``myapp.traversal.URLGenerator`` class will -be used to provide services to :func:`pyramid.url.resource_url` any -time the :term:`context` passed to ``resource_url`` is of class -``myapp.resources.MyRoot``. The asterisk following represents the type -of interface that must be possessed by the :term:`request` (in this -case, any interface, represented by asterisk). +In the above example, the ``myapp.traversal.URLGenerator`` class will be used +to provide services to :func:`pyramid.url.resource_url` any time the +:term:`context` passed to ``resource_url`` is of class +``myapp.resources.MyRoot``. The second argument in the ``(MyRoot, +Interface)`` tuple represents the type of interface that must be possessed by +the :term:`request` (in this case, any interface, represented by +``zope.interface.Interface``). The API that must be implemented by a class that provides :class:`pyramid.interfaces.IContextURL` is as follows: @@ -317,28 +281,25 @@ The API that must be implemented by a class that provides def __call__(self): """ Return a URL that points to the context """ -The default context URL generator is available for perusal as the -class :class:`pyramid.traversal.TraversalContextURL` in the -`traversal module -<http://github.com/Pylons/pyramid/blob/master/pyramid/traversal.py>`_ of -the :term:`Pylons` GitHub Pyramid repository. +The default context URL generator is available for perusal as the class +:class:`pyramid.traversal.TraversalContextURL` in the `traversal module +<http://github.com/Pylons/pyramid/blob/master/pyramid/traversal.py>`_ of the +:term:`Pylons` GitHub Pyramid repository. .. _changing_the_request_factory: Changing the Request Factory ---------------------------- -Whenever :app:`Pyramid` handles a :term:`WSGI` request, it creates -a :term:`request` object based on the WSGI environment it has been -passed. By default, an instance of the -:class:`pyramid.request.Request` class is created to represent the -request object. +Whenever :app:`Pyramid` handles a :term:`WSGI` request, it creates a +:term:`request` object based on the WSGI environment it has been passed. By +default, an instance of the :class:`pyramid.request.Request` class is created +to represent the request object. -The class (aka "factory") that :app:`Pyramid` uses to create a -request object instance can be changed by passing a -``request_factory`` argument to the constructor of the -:term:`configurator`. This argument can be either a callable or a -:term:`dotted Python name` representing a callable. +The class (aka "factory") that :app:`Pyramid` uses to create a request object +instance can be changed by passing a ``request_factory`` argument to the +constructor of the :term:`configurator`. This argument can be either a +callable or a :term:`dotted Python name` representing a callable. .. code-block:: python :linenos: @@ -350,24 +311,9 @@ request object instance can be changed by passing a config = Configurator(request_factory=MyRequest) -The same ``MyRequest`` class can alternately be registered via ZCML as -a request factory through the use of the ZCML ``utility`` directive. -In the below, we assume it lives in a package named -``mypackage.mymodule``. - -.. code-block:: xml - :linenos: - - <utility - component="mypackage.mymodule.MyRequest" - provides="pyramid.interfaces.IRequestFactory" - /> - -Lastly, if you're doing imperative configuration, and you'd rather do -it after you've already constructed a :term:`configurator` it can also -be registered via the -:meth:`pyramid.config.Configurator.set_request_factory` -method: +If you're doing imperative configuration, and you'd rather do it after you've +already constructed a :term:`configurator` it can also be registered via the +:meth:`pyramid.config.Configurator.set_request_factory` method: .. code-block:: python :linenos: @@ -381,26 +327,26 @@ method: config = Configurator() config.set_request_factory(MyRequest) +To use ZCML for the same purpose, see :ref:`changing_request_factory_zcml`. + .. _adding_renderer_globals: Adding Renderer Globals ----------------------- -Whenever :app:`Pyramid` handles a request to perform a rendering -(after a view with a ``renderer=`` configuration attribute is invoked, -or when the any of the methods beginning with ``render`` within the -:mod:`pyramid.renderers` module are called), *renderer globals* can -be injected into the *system* values sent to the renderer. By -default, no renderer globals are injected, and the "bare" system -values (such as ``request``, ``context``, and ``renderer_name``) are -the only values present in the system dictionary passed to every -renderer. - -A callback that :app:`Pyramid` will call every time a renderer is -invoked can be added by passing a ``renderer_globals_factory`` -argument to the constructor of the :term:`configurator`. This -callback can either be a callable object or a :term:`dotted Python -name` representing such a callable. +Whenever :app:`Pyramid` handles a request to perform a rendering (after a +view with a ``renderer=`` configuration attribute is invoked, or when the any +of the methods beginning with ``render`` within the :mod:`pyramid.renderers` +module are called), *renderer globals* can be injected into the *system* +values sent to the renderer. By default, no renderer globals are injected, +and the "bare" system values (such as ``request``, ``context``, and +``renderer_name``) are the only values present in the system dictionary +passed to every renderer. + +A callback that :app:`Pyramid` will call every time a renderer is invoked can +be added by passing a ``renderer_globals_factory`` argument to the +constructor of the :term:`configurator`. This callback can either be a +callable object or a :term:`dotted Python name` representing such a callable. .. code-block:: python :linenos: @@ -411,30 +357,15 @@ name` representing such a callable. config = Configurator( renderer_globals_factory=renderer_globals_factory) -Such a callback must accept a single positional argument (notionally -named ``system``) which will contain the original system values. It -must return a dictionary of values that will be merged into the system -dictionary. See :ref:`renderer_system_values` for discription of the -values present in the system dictionary. +Such a callback must accept a single positional argument (notionally named +``system``) which will contain the original system values. It must return a +dictionary of values that will be merged into the system dictionary. See +:ref:`renderer_system_values` for discription of the values present in the +system dictionary. -A renderer globals factory can alternately be registered via ZCML as a -through the use of the ZCML ``utility`` directive. In the below, we -assume a ``renderers_globals_factory`` function lives in a package -named ``mypackage.mymodule``. - -.. code-block:: xml - :linenos: - - <utility - component="mypackage.mymodule.renderer_globals_factory" - provides="pyramid.interfaces.IRendererGlobalsFactory" - /> - -Lastly, if you're doing imperative configuration, and you'd rather do -it after you've already constructed a :term:`configurator` it can also -be registered via the -:meth:`pyramid.config.Configurator.set_renderer_globals_factory` -method: +If you're doing imperative configuration, and you'd rather do it after you've +already constructed a :term:`configurator` it can also be registered via the +:meth:`pyramid.config.Configurator.set_renderer_globals_factory` method: .. code-block:: python :linenos: @@ -450,6 +381,9 @@ method: Another mechanism which allows event subscribers to add renderer global values exists in :ref:`beforerender_event`. +If you'd rather ZCML to register a renderer globals factory, see +:ref:`adding_renderer_globals_zcml`. + .. _beforerender_event: Using The Before Render Event @@ -472,8 +406,8 @@ that can be used for this purpose. For example: An object of this type is sent as an event just before a :term:`renderer` is invoked (but *after* the application-level renderer globals factory added via -:class:`pyramid.config.Configurator.set_renderer_globals_factory`, if -any, has injected its own keys into the renderer globals dictionary). +:class:`pyramid.config.Configurator.set_renderer_globals_factory`, if any, +has injected its own keys into the renderer globals dictionary). If a subscriber attempts to add a key that already exist in the renderer globals dictionary, a :exc:`KeyError` is raised. This limitation is enforced @@ -493,16 +427,16 @@ renderer global values exists in :ref:`adding_renderer_globals`. Using Response Callbacks ------------------------ -Unlike many other web frameworks, :app:`Pyramid` does not eagerly -create a global response object. Adding a :term:`response callback` -allows an application to register an action to be performed against a -response object once it is created, usually in order to mutate it. +Unlike many other web frameworks, :app:`Pyramid` does not eagerly create a +global response object. Adding a :term:`response callback` allows an +application to register an action to be performed against a response object +once it is created, usually in order to mutate it. -The :meth:`pyramid.request.Request.add_response_callback` method is -used to register a response callback. +The :meth:`pyramid.request.Request.add_response_callback` method is used to +register a response callback. -A response callback is a callable which accepts two positional -parameters: ``request`` and ``response``. For example: +A response callback is a callable which accepts two positional parameters: +``request`` and ``response``. For example: .. code-block:: python :linenos: @@ -515,11 +449,11 @@ parameters: ``request`` and ``response``. For example: No response callback is called if an unhandled exception happens in application code, or if the response object returned by a :term:`view -callable` is invalid. Response callbacks *are*, however, invoked when -a :term:`exception view` is rendered successfully: in such a case, the -:attr:`request.exception` attribute of the request when it enters a -response callback will be an exception object instead of its default -value of ``None``. +callable` is invalid. Response callbacks *are*, however, invoked when a +:term:`exception view` is rendered successfully: in such a case, the +:attr:`request.exception` attribute of the request when it enters a response +callback will be an exception object instead of its default value of +``None``. Response callbacks are called in the order they're added (first-to-most-recently-added). All response callbacks are called *after* @@ -537,13 +471,13 @@ of a :class:`pyramid.events.NewRequest` event). Using Finished Callbacks ------------------------ -A :term:`finished callback` is a function that will be called -unconditionally by the :app:`Pyramid` :term:`router` at the very -end of request processing. A finished callback can be used to perform -an action at the end of a request unconditionally. +A :term:`finished callback` is a function that will be called unconditionally +by the :app:`Pyramid` :term:`router` at the very end of request processing. +A finished callback can be used to perform an action at the end of a request +unconditionally. -The :meth:`pyramid.request.Request.add_finished_callback` method is -used to register a finished callback. +The :meth:`pyramid.request.Request.add_finished_callback` method is used to +register a finished callback. A finished callback is a callable which accepts a single positional parameter: ``request``. For example: @@ -563,25 +497,24 @@ parameter: ``request``. For example: Finished callbacks are called in the order they're added ( first- to most-recently- added). Finished callbacks (unlike a :term:`response -callback`) are *always* called, even if an exception happens in -application code that prevents a response from being generated. - -The set of finished callbacks associated with a request are called -*very late* in the processing of that request; they are essentially -the very last thing called by the :term:`router` before a request -"ends". They are called after response processing has already occurred -in a top-level ``finally:`` block within the router request processing -code. As a result, mutations performed to the ``request`` provided to -a finished callback will have no meaningful effect, because response -processing will have already occurred, and the request's scope will -expire almost immediately after all finished callbacks have been -processed. +callback`) are *always* called, even if an exception happens in application +code that prevents a response from being generated. + +The set of finished callbacks associated with a request are called *very +late* in the processing of that request; they are essentially the very last +thing called by the :term:`router` before a request "ends". They are called +after response processing has already occurred in a top-level ``finally:`` +block within the router request processing code. As a result, mutations +performed to the ``request`` provided to a finished callback will have no +meaningful effect, because response processing will have already occurred, +and the request's scope will expire almost immediately after all finished +callbacks have been processed. It is often necessary to tell whether an exception occurred within -:term:`view callable` code from within a finished callback: in such a -case, the :attr:`request.exception` attribute of the request when it -enters a response callback will be an exception object instead of its -default value of ``None``. +:term:`view callable` code from within a finished callback: in such a case, +the :attr:`request.exception` attribute of the request when it enters a +response callback will be an exception object instead of its default value of +``None``. Errors raised by finished callbacks are not handled specially. They will be propagated to the caller of the :app:`Pyramid` router @@ -598,22 +531,21 @@ Registering Configuration Decorators ------------------------------------ Decorators such as :class:`pyramid.view.view_config` don't change the -behavior of the functions or classes they're decorating. Instead, -when a :term:`scan` is performed, a modified version of the function -or class is registered with :app:`Pyramid`. - -You may wish to have your own decorators that offer such -behaviour. This is possible by using the :term:`Venusian` package in -the same way that it is used by :app:`Pyramid`. - -By way of example, let's suppose you want to write a decorator that -registers the function it wraps with a :term:`Zope Component -Architecture` "utility" within the :term:`application registry` -provided by :app:`Pyramid`. The application registry and the -utility inside the registry is likely only to be available once your -application's configuration is at least partially completed. A normal -decorator would fail as it would be executed before the configuration -had even begun. +behavior of the functions or classes they're decorating. Instead, when a +:term:`scan` is performed, a modified version of the function or class is +registered with :app:`Pyramid`. + +You may wish to have your own decorators that offer such behaviour. This is +possible by using the :term:`Venusian` package in the same way that it is +used by :app:`Pyramid`. + +By way of example, let's suppose you want to write a decorator that registers +the function it wraps with a :term:`Zope Component Architecture` "utility" +within the :term:`application registry` provided by :app:`Pyramid`. The +application registry and the utility inside the registry is likely only to be +available once your application's configuration is at least partially +completed. A normal decorator would fail as it would be executed before the +configuration had even begun. However, using :term:`Venusian`, the decorator could be written as follows: |
