From a7e625785f65c41e5a6dc017b31bd0d74821474e Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Tue, 31 May 2011 14:40:05 -0400 Subject: the canonical import location for HTTP exceptions/responses is now pyramid.response --- docs/narr/hooks.rst | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'docs/narr/hooks.rst') diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 7e3fe0a5c..d620b5672 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -21,7 +21,7 @@ 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 +only of naming the :exc:`pyramid.response.HTTPNotFound` class as the ``context`` of the view configuration. If your application uses :term:`imperative configuration`, you can replace @@ -31,9 +31,9 @@ method to register an "exception view": .. code-block:: python :linenos: - from pyramid.exceptions import NotFound + from pyramid.response import HTTPNotFound from helloworld.views import notfound_view - config.add_view(notfound_view, context=NotFound) + config.add_view(notfound_view, context=HTTPNotFound) Replace ``helloworld.views.notfound_view`` with a reference to the :term:`view callable` you want to use to represent the Not Found view. @@ -42,7 +42,7 @@ 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 +:exc:`~pyramid.response.HTTPNotFound` exception that caused the view to be called. Here's some sample code that implements a minimal NotFound view callable: @@ -50,25 +50,25 @@ Here's some sample code that implements a minimal NotFound view callable: .. code-block:: python :linenos: - from pyramid.httpexceptions import HTTPNotFound + from pyramid.response import HTTPNotFound def notfound_view(request): return HTTPNotFound() .. 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:`~pyramid.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. + :term:`request`. The ``exception`` attribute of the request will be an + instance of the :exc:`~pyramid.response.HTTPNotFound` 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:`~pyramid.exceptions.NotFound` exception instance. If available, the - resource context will still be available as ``request.context``. + :exc:`~pyramid.response.HTTPNotFound` exception instance. If available, + the resource context will still be available as ``request.context``. .. index:: single: forbidden view @@ -85,7 +85,7 @@ the view which generates it can be overridden as necessary. 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 +only of naming the :exc:`pyramid.response.HTTPForbidden` class as the ``context`` of the view configuration. You can replace the forbidden view by using the @@ -96,8 +96,8 @@ view": :linenos: from helloworld.views import forbidden_view - from pyramid.exceptions import Forbidden - config.add_view(forbidden_view, context=Forbidden) + from pyramid.response import HTTPForbidden + config.add_view(forbidden_view, context=HTTPForbidden) Replace ``helloworld.views.forbidden_view`` with a reference to the Python :term:`view callable` you want to use to represent the Forbidden view. @@ -121,13 +121,13 @@ Here's some sample code that implements a minimal forbidden view: return Response('forbidden') .. 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:`~pyramid.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. + :term:`request`. The ``exception`` attribute of the request will be an + instance of the :exc:`~pyramid.response.HTTPForbidden` 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. .. index:: single: request factory -- cgit v1.2.3 From df15ed98612e7962e3122da52d8d5f5b9d8882b2 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 4 Jun 2011 18:43:25 -0400 Subject: - It is now possible to control how the Pyramid router calls the WSGI ``start_response`` callable and obtains the WSGI ``app_iter`` based on adapting the response object to the new ``pyramid.interfaces.IResponder`` interface. The default ``IResponder`` uses Pyramid 1.0's logic to do this. To override the responder:: from pyramid.interfaces import IResponder from pyramid.response import Response from myapp import MyResponder config.registry.registerAdapter(MyResponder, (Response,), IResponder, name='') This makes it possible to reuse response object implementations which have, for example, their own ``__call__`` expected to be used as a WSGI application (like ``pyramid.response.Response``), e.g.: class MyResponder(object): def __init__(self, response): """ Obtain a reference to the response """ self.response = response def __call__(self, request, start_response): """ Call start_response and return an app_iter """ app_iter = self.response(request.environ, start_response) return app_iter --- docs/narr/hooks.rst | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'docs/narr/hooks.rst') diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index d620b5672..aa151d281 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -521,6 +521,42 @@ The default context URL generator is available for perusal as the class `_ of the :term:`Pylons` GitHub Pyramid repository. +.. index:: + single: IResponder + +.. _using_iresponder: + +Changing How Pyramid Treats Response Objects +-------------------------------------------- + +It is possible to control how the Pyramid :term:`router` calls the WSGI +``start_response`` callable and obtains the WSGI ``app_iter`` based on +adapting the response object to the :class: `pyramid.interfaces.IResponder` +interface. The default ``IResponder`` uses the three attributes ``status``, +``headerlist``, and ``app_iter`` attached to the response object, and calls +``start_response`` with the status and headerlist, returning the +``app_iter``. To override the responder:: + + from pyramid.interfaces import IResponder + from pyramid.response import Response + from myapp import MyResponder + + config.registry.registerAdapter(MyResponder, (Response,), + IResponder, name='') + +Overriding makes it possible to reuse response object implementations which +have, for example, their own ``__call__`` expected to be used as a WSGI +application (like :class:`pyramid.response.Response`), e.g.: + + class MyResponder(object): + def __init__(self, response): + """ Obtain a reference to the response """ + self.response = response + def __call__(self, request, start_response): + """ Call start_response and return an app_iter """ + app_iter = self.response(request.environ, start_response) + return app_iter + .. index:: single: view mapper -- cgit v1.2.3 From 99edc51a3b05309c7f5d98ff96289ec51b1d7660 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 11 Jun 2011 05:35:27 -0400 Subject: - Pyramid now expects Response objects to have a __call__ method which implements the WSGI application interface instead of the three webob attrs status, headerlist and app_iter. Backwards compatibility exists for code which returns response objects that do not have a __call__. - pyramid.response.Response is no longer an exception (and therefore cannot be raised in order to generate a response). - Changed my mind about moving stuff from pyramid.httpexceptions to pyramid.response. The stuff I moved over has been moved back to pyramid.httpexceptions. --- docs/narr/hooks.rst | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'docs/narr/hooks.rst') diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index aa151d281..b6a781417 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -21,7 +21,7 @@ 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.response.HTTPNotFound` class as the +only of naming the :exc:`pyramid.httpexceptions.HTTPNotFound` class as the ``context`` of the view configuration. If your application uses :term:`imperative configuration`, you can replace @@ -31,7 +31,7 @@ method to register an "exception view": .. code-block:: python :linenos: - from pyramid.response import HTTPNotFound + from pyramid.httpexceptions import HTTPNotFound from helloworld.views import notfound_view config.add_view(notfound_view, context=HTTPNotFound) @@ -42,22 +42,22 @@ 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.response.HTTPNotFound` exception that caused the view to be -called. +:exc:`~pyramid.httpexceptions.HTTPNotFound` exception that caused the view to +be called. Here's some sample code that implements a minimal NotFound view callable: .. code-block:: python :linenos: - from pyramid.response import HTTPNotFound + from pyramid.httpexceptions import HTTPNotFound def notfound_view(request): return HTTPNotFound() .. 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:`~pyramid.response.HTTPNotFound` exception that + instance of the :exc:`~pyramid.httpexceptions.HTTPNotFound` 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 @@ -67,8 +67,9 @@ Here's some sample code that implements a minimal NotFound view callable: .. 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:`~pyramid.response.HTTPNotFound` exception instance. If available, - the resource context will still be available as ``request.context``. + :exc:`~pyramid.httpexceptions.HTTPNotFound` exception instance. If + available, the resource context will still be available as + ``request.context``. .. index:: single: forbidden view @@ -85,7 +86,7 @@ the view which generates it can be overridden as necessary. 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.response.HTTPForbidden` class as the +only of naming the :exc:`pyramid.httpexceptions.HTTPForbidden` class as the ``context`` of the view configuration. You can replace the forbidden view by using the @@ -96,7 +97,7 @@ view": :linenos: from helloworld.views import forbidden_view - from pyramid.response import HTTPForbidden + from pyramid.httpexceptions import HTTPForbidden config.add_view(forbidden_view, context=HTTPForbidden) Replace ``helloworld.views.forbidden_view`` with a reference to the Python @@ -122,8 +123,8 @@ Here's some sample code that implements a minimal forbidden view: .. 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:`~pyramid.response.HTTPForbidden` exception that - caused the forbidden view to be called. The value of + instance of the :exc:`~pyramid.httpexceptions.HTTPForbidden` 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 @@ -532,10 +533,10 @@ Changing How Pyramid Treats Response Objects It is possible to control how the Pyramid :term:`router` calls the WSGI ``start_response`` callable and obtains the WSGI ``app_iter`` based on adapting the response object to the :class: `pyramid.interfaces.IResponder` -interface. The default ``IResponder`` uses the three attributes ``status``, -``headerlist``, and ``app_iter`` attached to the response object, and calls -``start_response`` with the status and headerlist, returning the -``app_iter``. To override the responder:: +interface. The default responder uses the ``__call__`` method of a response +object, passing it the WSGI environ and the WSGI ``start_response`` callable +(the response is assumed to be a WSGI application). To override the +responder:: from pyramid.interfaces import IResponder from pyramid.response import Response @@ -545,8 +546,9 @@ interface. The default ``IResponder`` uses the three attributes ``status``, IResponder, name='') Overriding makes it possible to reuse response object implementations which -have, for example, their own ``__call__`` expected to be used as a WSGI -application (like :class:`pyramid.response.Response`), e.g.: +have, for example, the ``app_iter``, ``headerlist`` and ``status`` attributes +of an object returned as a response instead of trying to use the object's +``__call__`` method:: class MyResponder(object): def __init__(self, response): @@ -554,8 +556,8 @@ application (like :class:`pyramid.response.Response`), e.g.: self.response = response def __call__(self, request, start_response): """ Call start_response and return an app_iter """ - app_iter = self.response(request.environ, start_response) - return app_iter + start_response(self.response.status, self.response.headerlist) + return self.response.app_iter .. index:: single: view mapper -- cgit v1.2.3 From d868fff7597c5a05acd1f5c024fc45dde9880413 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 13 Jun 2011 06:17:00 -0400 Subject: - Remove IResponder abstraction in favor of more general IResponse abstraction. - It is now possible to return an arbitrary object from a Pyramid view callable even if a renderer is not used, as long as a suitable adapter to ``pyramid.interfaces.IResponse`` is registered for the type of the returned object. See the section in the Hooks chapter of the documentation entitled "Changing How Pyramid Treats View Responses". - The Pyramid router now, by default, expects response objects returned from view callables to implement the ``pyramid.interfaces.IResponse`` interface. Unlike the Pyramid 1.0 version of this interface, objects which implement IResponse now must define a ``__call__`` method that accepts ``environ`` and ``start_response``, and which returns an ``app_iter`` iterable, among other things. Previously, it was possible to return any object which had the three WebOb ``app_iter``, ``headerlist``, and ``status`` attributes as a response, so this is a backwards incompatibility. It is possible to get backwards compatibility back by registering an adapter to IResponse from the type of object you're now returning from view callables. See the section in the Hooks chapter of the documentation entitled "Changing How Pyramid Treats View Responses". - The ``pyramid.interfaces.IResponse`` interface is now much more extensive. Previously it defined only ``app_iter``, ``status`` and ``headerlist``; now it is basically intended to directly mirror the ``webob.Response`` API, which has many methods and attributes. - Documentation changes to support above. --- docs/narr/hooks.rst | 134 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 98 insertions(+), 36 deletions(-) (limited to 'docs/narr/hooks.rst') diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index b6a781417..0db8ce5e0 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -523,41 +523,103 @@ The default context URL generator is available for perusal as the class :term:`Pylons` GitHub Pyramid repository. .. index:: - single: IResponder - -.. _using_iresponder: - -Changing How Pyramid Treats Response Objects --------------------------------------------- - -It is possible to control how the Pyramid :term:`router` calls the WSGI -``start_response`` callable and obtains the WSGI ``app_iter`` based on -adapting the response object to the :class: `pyramid.interfaces.IResponder` -interface. The default responder uses the ``__call__`` method of a response -object, passing it the WSGI environ and the WSGI ``start_response`` callable -(the response is assumed to be a WSGI application). To override the -responder:: - - from pyramid.interfaces import IResponder - from pyramid.response import Response - from myapp import MyResponder - - config.registry.registerAdapter(MyResponder, (Response,), - IResponder, name='') - -Overriding makes it possible to reuse response object implementations which -have, for example, the ``app_iter``, ``headerlist`` and ``status`` attributes -of an object returned as a response instead of trying to use the object's -``__call__`` method:: - - class MyResponder(object): - def __init__(self, response): - """ Obtain a reference to the response """ - self.response = response - def __call__(self, request, start_response): - """ Call start_response and return an app_iter """ - start_response(self.response.status, self.response.headerlist) - return self.response.app_iter + single: IResponse + +.. _using_iresponse: + +Changing How Pyramid Treats View Responses +------------------------------------------ + +It is possible to control how Pyramid treats the result of calling a view +callable on a per-type basis by using a hook involving +:class:`pyramid.interfaces.IResponse`. + +.. note:: This feature is new as of Pyramid 1.1. + +Pyramid, in various places, adapts the result of calling a view callable to +the :class:`~pyramid.interfaces.IResponse` interface to ensure that the +object returned by the view callable is a "true" response object. The vast +majority of time, the result of this adaptation is the result object itself, +as view callables written by "civilians" who read the narrative documentation +contained in this manual will always return something that implements the +:class:`~pyramid.interfaces.IResponse` interface. Most typically, this will +be an instance of the :class:`pyramid.response.Response` class or a subclass. +If a civilian returns a non-Response object from a view callable that isn't +configured to use a :term:`renderer`, he will typically expect the router to +raise an error. However, you can hook Pyramid in such a way that users can +return arbitrary values from a view callable by providing an adapter which +converts the arbitrary return value into something that implements +:class:`~pyramid.interfaces.IResponse`. + +For example, if you'd like to allow view callables to return bare string +objects (without requiring a a :term:`renderer` to convert a string to a +response object), you can register an adapter which converts the string to a +Response: + +.. code-block:: python + :linenos: + + from pyramid.interfaces import IResponse + from pyramid.response import Response + + def string_response_adapter(s): + response = Response(s) + return response + + # config is an instance of pyramid.config.Configurator + + config.registry.registerAdapter(string_response_adapter, (str,), + IResponse) + +Likewise, if you want to be able to return a simplified kind of response +object from view callables, you can use the IResponse hook to register an +adapter to the more complex IResponse interface: + +.. code-block:: python + :linenos: + + from pyramid.interfaces import IResponse + from pyramid.response import Response + + class SimpleResponse(object): + def __init__(self, body): + self.body = body + + def simple_response_adapter(simple_response): + response = Response(simple_response.body) + return response + + # config is an instance of pyramid.config.Configurator + + config.registry.registerAdapter(simple_response_adapter, + (SimpleResponse,), + IResponse) + +If you want to implement your own Response object instead of using the +:class:`pyramid.response.Response` object in any capacity at all, you'll have +to make sure the object implements every attribute and method outlined in +:class:`pyramid.interfaces.IResponse` *and* you'll have to ensure that it's +marked up with ``zope.interface.implements(IResponse)``: + + from pyramid.interfaces import IResponse + from zope.interface import implements + + class MyResponse(object): + implements(IResponse) + # ... an implementation of every method and attribute + # documented in IResponse should follow ... + +When an alternate response object implementation is returned by a view +callable, if that object asserts that it implements +:class:`~pyramid.interfaces.IResponse` (via +``zope.interface.implements(IResponse)``) , an adapter needn't be registered +for the object; Pyramid will use it directly. + +An IResponse adapter for ``webob.Response`` (as opposed to +:class:`pyramid.response.Response`) is registered by Pyramid by default at +startup time, as by their nature, instances of this class (and instances of +subclasses of the class) will natively provide IResponse. The adapter +registered for ``webob.Response`` simply returns the response object. .. index:: single: view mapper @@ -628,7 +690,7 @@ A user might make use of these framework components like so: # user application - from webob import Response + from pyramid.response import Response from pyramid.config import Configurator import pyramid_handlers from paste.httpserver import serve -- cgit v1.2.3 From 1a6fc7062f803b9f15b7677db9a9257a4f00bfcb Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Tue, 14 Jun 2011 02:36:07 -0400 Subject: - Added new add_response_adapter method to Configurator. - Fix Configurator docstring wrt exception responses. - Speed up registry.queryAdapterOrSelf --- docs/narr/hooks.rst | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'docs/narr/hooks.rst') diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 0db8ce5e0..8e5b93ed4 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -532,7 +532,7 @@ Changing How Pyramid Treats View Responses It is possible to control how Pyramid treats the result of calling a view callable on a per-type basis by using a hook involving -:class:`pyramid.interfaces.IResponse`. +:method:`pyramid.config.Configurator.add_response_adapter`. .. note:: This feature is new as of Pyramid 1.1. @@ -559,7 +559,6 @@ Response: .. code-block:: python :linenos: - from pyramid.interfaces import IResponse from pyramid.response import Response def string_response_adapter(s): @@ -568,8 +567,7 @@ Response: # config is an instance of pyramid.config.Configurator - config.registry.registerAdapter(string_response_adapter, (str,), - IResponse) + config.add_response_adapter(string_response_adapter, str) Likewise, if you want to be able to return a simplified kind of response object from view callables, you can use the IResponse hook to register an @@ -578,7 +576,6 @@ adapter to the more complex IResponse interface: .. code-block:: python :linenos: - from pyramid.interfaces import IResponse from pyramid.response import Response class SimpleResponse(object): @@ -591,14 +588,12 @@ adapter to the more complex IResponse interface: # config is an instance of pyramid.config.Configurator - config.registry.registerAdapter(simple_response_adapter, - (SimpleResponse,), - IResponse) + config.add_response_adapter(simple_response_adapter, SimpleResponse) If you want to implement your own Response object instead of using the :class:`pyramid.response.Response` object in any capacity at all, you'll have to make sure the object implements every attribute and method outlined in -:class:`pyramid.interfaces.IResponse` *and* you'll have to ensure that it's +:class:`pyramid.interfaces.IResponse` and you'll have to ensure that it's marked up with ``zope.interface.implements(IResponse)``: from pyramid.interfaces import IResponse -- cgit v1.2.3