Chris McDonough
2011-06-13 d868fff7597c5a05acd1f5c024fc45dde9880413
- 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.
27 files modified
734 ■■■■ changed files
CHANGES.txt 42 ●●●● patch | view | raw | blame | history
TODO.txt 19 ●●●● patch | view | raw | blame | history
docs/api/interfaces.rst 2 ●●● patch | view | raw | blame | history
docs/api/request.rst 33 ●●●● patch | view | raw | blame | history
docs/designdefense.rst 4 ●●●● patch | view | raw | blame | history
docs/glossary.rst 12 ●●●● patch | view | raw | blame | history
docs/narr/assets.rst 2 ●●● patch | view | raw | blame | history
docs/narr/hooks.rst 120 ●●●● patch | view | raw | blame | history
docs/narr/renderers.rst 2 ●●● patch | view | raw | blame | history
docs/narr/router.rst 6 ●●●● patch | view | raw | blame | history
docs/narr/views.rst 34 ●●●●● patch | view | raw | blame | history
docs/narr/webob.rst 66 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/definingviews.rst 10 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/definingviews.rst 3 ●●●● patch | view | raw | blame | history
pyramid/config.py 39 ●●●● patch | view | raw | blame | history
pyramid/interfaces.py 8 ●●●●● patch | view | raw | blame | history
pyramid/registry.py 19 ●●●●● patch | view | raw | blame | history
pyramid/renderers.py 4 ●●● patch | view | raw | blame | history
pyramid/request.py 2 ●●●●● patch | view | raw | blame | history
pyramid/response.py 2 ●●● patch | view | raw | blame | history
pyramid/router.py 47 ●●●● patch | view | raw | blame | history
pyramid/session.py 14 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_config.py 78 ●●●● patch | view | raw | blame | history
pyramid/tests/test_router.py 122 ●●●● patch | view | raw | blame | history
pyramid/tests/test_session.py 15 ●●●● patch | view | raw | blame | history
pyramid/tests/test_view.py 23 ●●●●● patch | view | raw | blame | history
pyramid/view.py 6 ●●●●● patch | view | raw | blame | history
CHANGES.txt
@@ -127,11 +127,11 @@
- The ``pyramid.request.Response`` class now has a ``RequestClass`` interface
  which points at ``pyramid.response.Request``.
- 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.  See the section in the Hooks chapter of the documentation
  entitled "Changing How Pyramid Treats Response Objects".
- 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 will now, by default, call the ``__call__`` method of
  WebOb response objects when returning a WSGI response.  This means that,
@@ -306,22 +306,26 @@
  ``webob.response.Response`` (in order to directly implement the
  ``pyramid.interfaces.IResponse`` interface).
- The ``pyramid.interfaces.IResponse`` interface now includes a ``__call__``
  method which has the WSGI application call signature (and which expects an
  iterable as a result).
Backwards Incompatibilities
---------------------------
- The Pyramid router now, by default, expects response objects returned from
  views to implement the WSGI application interface (a ``__call__`` method
  that accepts ``environ`` and ``start_response``, and which returns an
  ``app_iter`` iterable).  If such a method exists, Pyramid will now call it
  in order to satisfy the WSGI request.  Backwards compatibility code in the
  default responder exists which will fall back to the older behavior, but
  Pyramid will raise a deprecation warning if it is reached.  See the section
  in the Hooks chapter of the documentation entitled "Changing How Pyramid
  Treats Response Objects" to default back to the older behavior, where the
  ``app_iter``, ``headerlist``, and ``status`` attributes of the object were
  consulted directly (without any indirection through ``__call__``) to
  silence the deprecation warnings.
  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.
Dependencies
------------
TODO.txt
@@ -6,14 +6,27 @@
- To subclass or not subclass http exceptions.
- Depend on only __call__ interface or only 3-attr interface in builtin code
  that deals with response objects.
- Flesh out IResponse interface.  Attributes Used internally: unicode_body /
  body / content_type / charset / cache_expires / headers/
  default_content_type / set_cookie / headerlist / app_iter / status /
  __call__.
- Figure out what to do with ``is_response``.
- Deprecate view.is_response?
- Move is_response to response.py?
- Make sure registering IResponse adapter for webob.Response doesn't make it
  impossible to register an IResponse adapter for an interface that a
  webob.Response happens to implement.
- Run whatsitdoing tests.
- Docs mention ``exception.args[0]`` as a way to get messages; check that
  this works.
- Deprecate response_foo attrs on request at attribute set time rather than
  lookup time.
Should-Have
-----------
docs/api/interfaces.rst
@@ -57,6 +57,6 @@
  .. autointerface:: IMultiDict
     :members:
  .. autointerface:: IResponder
  .. autointerface:: IResponse
     :members:
docs/api/request.rst
@@ -107,7 +107,9 @@
         return {'text':'Value that will be used by the renderer'}
     Mutations to this response object will be preserved in the response sent
     to the client after rendering.
     to the client after rendering.  For more information about using
     ``request.response`` in conjunction with a renderer, see
     :ref:`request_response_attr`.
     Non-renderer code can also make use of request.response instead of
     creating a response "by hand".  For example, in view code::
@@ -162,20 +164,21 @@
   .. attribute::  response_*
      .. warning:: As of Pyramid 1.1, assignment to ``response_*`` attrs are
         deprecated.  Assigning to one will cause a deprecation warning to be
         emitted.  Instead of assigning ``response_*`` attributes to the
         request, use API of the the :attr:`pyramid.request.Request.response`
         object (exposed to view code as ``request.response``) to influence
         response behavior.
      You can set attributes on a :class:`pyramid.request.Request` which will
      influence the behavor of *rendered* responses (views which use a
      :term:`renderer` and which don't directly return a response).  These
      attributes begin with ``response_``, such as ``response_headerlist``. If
      you need to influence response values from a view that uses a renderer
      (such as the status code, a header, the content type, etc) see,
      :ref:`response_prefixed_attrs`.
      In Pyramid 1.0, you could set attributes on a
      :class:`pyramid.request.Request` which influenced the behavor of
      *rendered* responses (views which use a :term:`renderer` and which
      don't directly return a response).  These attributes began with
      ``response_``, such as ``response_headerlist``. If you needed to
      influence response values from a view that uses a renderer (such as the
      status code, a header, the content type, etc) you would set these
      attributes.  See :ref:`response_prefixed_attrs` for further discussion.
      As of Pyramid 1.1, assignment to ``response_*`` attrs are deprecated.
      Assigning to one is still supported but will cause a deprecation
      warning to be emitted, and eventually the feature will be removed.  For
      new code, instead of assigning ``response_*`` attributes to the
      request, use API of the the :attr:`pyramid.request.Request.response`
      object (exposed to view code as ``request.response``) to influence
      rendered response behavior.
.. note::
docs/designdefense.rst
@@ -428,7 +428,7 @@
   :linenos:
   from pyramid.view import view_config
   from webob import Response
   from pyramid.response import Response
   def subpath(context, request):
       return request.subpath and request.subpath[0] == 'abc'
@@ -1497,7 +1497,7 @@
.. code-block:: python
   :linenos:
   from webob import Response                 # explicit response objects, no TL
   from pyramid.response import Response      # explicit response objects, no TL
   from paste.httpserver import serve         # explicitly WSGI
   def hello_world(request):  # accepts a request; no request thread local reqd
docs/glossary.rst
@@ -16,12 +16,12 @@
     positional argument, returns a ``WebOb`` compatible request.
   response
     An object that has three attributes: ``app_iter`` (representing an
     iterable body), ``headerlist`` (representing the http headers sent
     to the user agent), and ``status`` (representing the http status
     string sent to the user agent).  This is the interface defined for
     ``WebOb`` response objects.  See :ref:`webob_chapter` for
     information about response objects.
     An object returned by a :term:`view callable` that represents response
     data returned to the requesting user agent.  It must implements the
     :class:`pyramid.interfaces.IResponse` interface.  A response object is
     typically an instance of the :class:`pyramid.response.Response` class or
     a subclass such as :class:`pyramid.httpexceptions.HTTPFound`.  See
     :ref:`webob_chapter` for information about response objects.
   Repoze
     "Repoze" is essentially a "brand" of software developed by `Agendaless
docs/narr/assets.rst
@@ -358,7 +358,7 @@
   :linenos:
   import os
   from webob import Response
   from pyramid.response import Response
   def favicon_view(request):
       here = os.path.dirname(__file__)
docs/narr/hooks.rst
@@ -523,41 +523,103 @@
:term:`Pylons` GitHub Pyramid repository.
.. index::
   single: IResponder
   single: IResponse
.. _using_iresponder:
.. _using_iresponse:
Changing How Pyramid Treats Response Objects
--------------------------------------------
Changing How Pyramid Treats View Responses
------------------------------------------
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::
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`.
     from pyramid.interfaces import IResponder
     from pyramid.response import Response
     from myapp import MyResponder
.. note:: This feature is new as of Pyramid 1.1.
     config.registry.registerAdapter(MyResponder, (Response,),
                                     IResponder, name='')
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`.
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::
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:
     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
.. 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 @@
   # user application
   from webob import Response
   from pyramid.response import Response
   from pyramid.config import Configurator
   import pyramid_handlers
   from paste.httpserver import serve
docs/narr/renderers.rst
@@ -416,7 +416,7 @@
For more information on attributes of the request, see the API documentation
in :ref:`request_module`.  For more information on the API of
``request.response``, see :class:`pyramid.response.Response`.
``request.response``, see :attr:`pyramid.request.Request.response`.
.. _response_prefixed_attrs:
docs/narr/router.rst
@@ -115,9 +115,9 @@
   any :term:`response callback` functions attached via
   :meth:`~pyramid.request.Request.add_response_callback`.  A
   :class:`~pyramid.events.NewResponse` :term:`event` is then sent to any
   subscribers.  The response object's ``app_iter``, ``status``, and
   ``headerlist`` attributes are then used to generate a WSGI response.  The
   response is sent back to the upstream WSGI server.
   subscribers.  The response object's ``__call__`` method is then used to
   generate a WSGI response.  The response is sent back to the upstream WSGI
   server.
#. :app:`Pyramid` will attempt to execute any :term:`finished
   callback` functions attached via
docs/narr/views.rst
@@ -230,29 +230,19 @@
   def view(request):
       return Response('OK')
You don't need to use :class:`~pyramid.response.Response` to represent a
response.  A view can actually return any object that has a ``__call__``
method that implements the :term:`WSGI` application call interface.  For
example, an instance of the following class could be successfully returned by
a view callable as a response object:
:app:`Pyramid` provides a range of different "exception" classes which
inherit from :class:`pyramid.response.Response`.  For example, an instance of
the class :class:`pyramid.httpexceptions.HTTPFound` is also a valid response
object because it inherits from :class:`~pyramid.response.Response`.  For
examples, see :ref:`http_exceptions` and ref:`http_redirect`.
.. code-block:: python
   :linenos:
   class SimpleResponse(object):
       def __call__(self, environ, start_response):
           """ Call the ``start_response`` callback and return
               an iterable """
           body = 'Hello World!'
           headers = [('Content-Type', 'text/plain'),
                      ('Content-Length', str(len(body)))]
           start_response('200 OK', headers)
           return [body]
:app:`Pyramid` provides a range of different "exception" classes which can
act as response objects too.  For example, an instance of the class
:class:`pyramid.httpexceptions.HTTPFound` is also a valid response object
(see :ref:`http_exceptions` and ref:`http_redirect`).
You can also return objects from view callables that aren't instances of (or
instances of classes which are subclasses of)
:class:`pyramid.response.Response` in various circumstances.  This can be
helpful when writing tests and when attempting to share code between view
callables.  See :ref:`renderers_chapter` for the common way to allow for
this.  A much less common way to allow for view callables to return
non-Response objects is documented in :ref:`using_iresponse`.
.. index::
   single: view exceptions
docs/narr/webob.rst
@@ -10,15 +10,15 @@
.. note:: This chapter is adapted from a portion of the :term:`WebOb`
   documentation, originally written by Ian Bicking.
:app:`Pyramid` uses the :term:`WebOb` package to supply
:app:`Pyramid` uses the :term:`WebOb` package as a basis for its
:term:`request` and :term:`response` object implementations.  The
:term:`request` object that is passed to a :app:`Pyramid`
:term:`view` is an instance of the :class:`pyramid.request.Request`
class, which is a subclass of :class:`webob.Request`.  The
:term:`response` returned from a :app:`Pyramid` :term:`view`
:term:`renderer` is an instance of the :mod:`webob.Response` class.
Users can also return an instance of :mod:`webob.Response` directly
from a view as necessary.
:term:`request` object that is passed to a :app:`Pyramid` :term:`view` is an
instance of the :class:`pyramid.request.Request` class, which is a subclass
of :class:`webob.Request`.  The :term:`response` returned from a
:app:`Pyramid` :term:`view` :term:`renderer` is an instance of the
:mod:`pyramid.response.Response` class, which is a subclass of the
:class:`webob.Response` class.  Users can also return an instance of
:class:`pyramid.response.Response` directly from a view as necessary.
WebOb is a project separate from :app:`Pyramid` with a separate set of
authors and a fully separate `set of documentation
@@ -26,16 +26,15 @@
standard WebOb request, which is documented in the :ref:`request_module` API
documentation.
WebOb provides objects for HTTP requests and responses.  Specifically
it does this by wrapping the `WSGI <http://wsgi.org>`_ request
environment and response status/headers/app_iter (body).
WebOb provides objects for HTTP requests and responses.  Specifically it does
this by wrapping the `WSGI <http://wsgi.org>`_ request environment and
response status, header list, and app_iter (body) values.
WebOb request and response objects provide many conveniences for
parsing WSGI requests and forming WSGI responses.  WebOb is a nice way
to represent "raw" WSGI requests and responses; however, we won't
cover that use case in this document, as users of :app:`Pyramid`
don't typically need to use the WSGI-related features of WebOb
directly.  The `reference documentation
WebOb request and response objects provide many conveniences for parsing WSGI
requests and forming WSGI responses.  WebOb is a nice way to represent "raw"
WSGI requests and responses; however, we won't cover that use case in this
document, as users of :app:`Pyramid` don't typically need to use the
WSGI-related features of WebOb directly.  The `reference documentation
<http://pythonpaste.org/webob/reference.html>`_ shows many examples of
creating requests and using response objects in this manner, however.
@@ -170,9 +169,9 @@
Methods
+++++++
There are `several methods
<http://pythonpaste.org/webob/class-webob.Request.html#__init__>`_ but
only a few you'll use often:
There are methods of request objects documented in
:class:`pyramid.request.Request` but you'll find that you won't use very many
of them.  Here are a couple that might be useful:
``Request.blank(base_url)``:
    Creates a new request with blank information, based at the given
@@ -183,9 +182,9 @@
    subrequests).
``req.get_response(wsgi_application)``:
    This method calls the given WSGI application with this request,
    and returns a `Response`_ object.  You can also use this for
    subrequests, or testing.
    This method calls the given WSGI application with this request, and
    returns a :class:`pyramid.response.Response` object.  You can also use
    this for subrequests, or testing.
.. index::
   single: request (and unicode)
@@ -259,8 +258,10 @@
~~~~~~~~
The :app:`Pyramid` response object can be imported as
:class:`pyramid.response.Response`.  This import location is merely a facade
for its original location: ``webob.Response``.
:class:`pyramid.response.Response`.  This class is a subclass of the
``webob.Response`` class.  The subclass does not add or change any
functionality, so the WebOb Response documentation will be completely
relevant for this class as well.
A response object has three fundamental parts:
@@ -283,8 +284,8 @@
    ``response.body_file`` (a file-like object; writing to it appends
    to ``app_iter``).
Everything else in the object derives from this underlying state.
Here's the highlights:
Everything else in the object typically derives from this underlying state.
Here are some highlights:
``response.content_type``
    The content type *not* including the ``charset`` parameter.
@@ -359,11 +360,12 @@
+++++++++++++++++++
To facilitate error responses like ``404 Not Found``, the module
:mod:`webob.exc` contains classes for each kind of error response.  These
include boring, but appropriate error bodies.  The exceptions exposed by this
module, when used under :app:`Pyramid`, should be imported from the
:mod:`pyramid.httpexceptions` module.  This import location contains
subclasses and replacements that mirror those in the original ``webob.exc``.
:mod:`pyramid.httpexceptions` contains classes for each kind of error
response.  These include boring, but appropriate error bodies.  The
exceptions exposed by this module, when used under :app:`Pyramid`, should be
imported from the :mod:`pyramid.httpexceptions` module.  This import location
contains subclasses and replacements that mirror those in the ``webob.exc``
module.
Each class is named ``pyramid.httpexceptions.HTTP*``, where ``*`` is the
reason for the error.  For instance,
docs/tutorials/wiki/definingviews.rst
@@ -84,10 +84,12 @@
The ``view_wiki`` view callable always redirects to the URL of a Page
resource named "FrontPage".  To do so, it returns an instance of the
:class:`pyramid.httpexceptions.HTTPFound` class (instances of which implement
the WebOb :term:`response` interface).  The :func:`pyramid.url.resource_url`
API.  :func:`pyramid.url.resource_url` constructs a URL to the ``FrontPage``
page resource (e.g. ``http://localhost:6543/FrontPage``), and uses it as the
"location" of the HTTPFound response, forming an HTTP redirect.
the :class:`pyramid.interfaces.IResponse` interface like
:class:`pyramid.response.Response` does).  The
:func:`pyramid.url.resource_url` API.  :func:`pyramid.url.resource_url`
constructs a URL to the ``FrontPage`` page resource
(e.g. ``http://localhost:6543/FrontPage``), and uses it as the "location" of
the HTTPFound response, forming an HTTP redirect.
The ``view_page`` view function
-------------------------------
docs/tutorials/wiki2/definingviews.rst
@@ -91,7 +91,8 @@
The ``view_wiki`` function returns an instance of the
:class:`pyramid.httpexceptions.HTTPFound` class (instances of which implement
the WebOb :term:`response` interface), It will use the
the :class:`pyramid.interfaces.IResponse` interface like
:class:`pyramid.response.Response` does), It will use the
:func:`pyramid.url.route_url` API to construct a URL to the ``FrontPage``
page (e.g. ``http://localhost:6543/FrontPage``), and will use it as the
"location" of the HTTPFound response, forming an HTTP redirect.
pyramid/config.py
@@ -19,6 +19,7 @@
from zope.interface.interfaces import IInterface
from zope.interface import implements
from zope.interface import classProvides
from zope.interface import providedBy
from pyramid.interfaces import IAuthenticationPolicy
from pyramid.interfaces import IAuthorizationPolicy
@@ -36,6 +37,7 @@
from pyramid.interfaces import IRendererGlobalsFactory
from pyramid.interfaces import IRequest
from pyramid.interfaces import IRequestFactory
from pyramid.interfaces import IResponse
from pyramid.interfaces import IRootFactory
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IRoutesMapper
@@ -82,7 +84,6 @@
from pyramid.urldispatch import RoutesMapper
from pyramid.util import DottedNameResolver
from pyramid.view import render_view_to_response
from pyramid.view import is_response
DEFAULT_RENDERERS = (
    ('.mak', mako_renderer_factory),
@@ -417,7 +418,8 @@
    def _fix_registry(self):
        """ Fix up a ZCA component registry that is not a
        pyramid.registry.Registry by adding analogues of ``has_listeners``,
        and ``notify`` through monkey-patching."""
        ``notify``, ``queryAdapterOrSelf``, and ``registerSelfAdapter``
        through monkey-patching."""
        _registry = self.registry
@@ -428,6 +430,24 @@
        if not hasattr(_registry, 'has_listeners'):
            _registry.has_listeners = True
        if not hasattr(_registry, 'queryAdapterOrSelf'):
            def queryAdapterOrSelf(object, interface, name=u'', default=None):
                provides = providedBy(object)
                if not interface in provides:
                    return _registry.queryAdapter(object, interface, name=name,
                                                  default=default)
                return object
            _registry.queryAdapterOrSelf = queryAdapterOrSelf
        if not hasattr(_registry, 'registerSelfAdapter'):
            def registerSelfAdapter(required=None, provided=None,
                                    name=u'', info=u'', event=True):
                return _registry.registerAdapter(lambda x: x,
                                                 required=required,
                                                 provided=provided, name=name,
                                                 info=info, event=event)
            _registry.registerSelfAdapter = registerSelfAdapter
    def _make_context(self, autocommit=False):
        context = PyramidConfigurationMachine()
@@ -697,6 +717,9 @@
        self._fix_registry()
        self._set_settings(settings)
        self._set_root_factory(root_factory)
        # cope with WebOb response objects that aren't decorated with IResponse
        from webob import Response as WebobResponse
        registry.registerSelfAdapter((WebobResponse,), IResponse)
        debug_logger = self.maybe_dotted(debug_logger)
        if debug_logger is None:
            debug_logger = make_stream_logger('pyramid.debug', sys.stderr)
@@ -2942,22 +2965,24 @@
        def _rendered_view(context, request):
            renderer = static_renderer
            response = wrapped_view(context, request)
            if not is_response(response):
            result = wrapped_view(context, request)
            registry = self.kw['registry']
            response = registry.queryAdapterOrSelf(result, IResponse)
            if response is None:
                attrs = getattr(request, '__dict__', {})
                if 'override_renderer' in attrs:
                    # renderer overridden by newrequest event or other
                    renderer_name = attrs.pop('override_renderer')
                    renderer = RendererHelper(name=renderer_name,
                                              package=self.kw.get('package'),
                                              registry = self.kw['registry'])
                                              registry = registry)
                if '__view__' in attrs:
                    view_inst = attrs.pop('__view__')
                else:
                    view_inst = getattr(wrapped_view, '__original_view__',
                                        wrapped_view)
                return renderer.render_view(request, response, view_inst,
                                            context)
                response = renderer.render_view(request, result, view_inst,
                                                context)
            return response
        return _rendered_view
pyramid/interfaces.py
@@ -233,14 +233,6 @@
        dictionary. This is similar to the kind of dictionary often used to
        represent the variables in a web request. """
class IResponder(Interface):
    """ Adapter from IResponse to an IResponder.  See :ref:`using_iresponder`
    for usage details.  New in Pyramid 1.1.
    """
    def __call__(self, request, start_response):
        """ Call the WSGI ``start_response`` callable passed as
        ``start_response`` and return an ``app_iter``."""
# internal interfaces
class IRequest(Interface):
pyramid/registry.py
@@ -1,4 +1,5 @@
from zope.component.registry import Components
from zope.interface import providedBy
from pyramid.interfaces import ISettings
@@ -28,6 +29,24 @@
        self.has_listeners = True
        return result
    def registerSelfAdapter(self, required=None, provided=None, name=u'',
                            info=u'', event=True):
        # registerAdapter analogue which always returns the object itself
        # when required is matched
        return self.registerAdapter(lambda x: x, required=required,
                                    provided=provided, name=name,
                                    info=info, event=event)
    def queryAdapterOrSelf(self, object, interface, name=u'', default=None):
        # queryAdapter analogue which returns the object if it implements
        # the interface, otherwise it will return an adaptation to the
        # interface
        provides = providedBy(object)
        if not interface in provides:
            return self.queryAdapter(object, interface, name=name,
                                     default=default)
        return object
    def registerHandler(self, *arg, **kw):
        result = Components.registerHandler(self, *arg, **kw)
        self.has_listeners = True
pyramid/renderers.py
@@ -316,9 +316,7 @@
            'context':context,
            'request':request
            }
        return self.render_to_response(response, system,
                                       request=request)
        return self.render_to_response(response, system, request=request)
    def render(self, value, system_values, request=None):
        renderer = self.renderer
pyramid/request.py
@@ -5,12 +5,14 @@
from webob import BaseRequest
from pyramid.interfaces import IRequest
from pyramid.interfaces import IResponse
from pyramid.interfaces import ISessionFactory
from pyramid.interfaces import IResponseFactory
from pyramid.exceptions import ConfigurationError
from pyramid.decorator import reify
from pyramid.response import Response
from pyramid.threadlocal import get_current_registry
from pyramid.url import resource_url
from pyramid.url import route_url
from pyramid.url import static_url
pyramid/response.py
@@ -4,4 +4,4 @@
class Response(_Response):
    implements(IResponse)
pyramid/router.py
@@ -1,5 +1,3 @@
import warnings
from zope.interface import implements
from zope.interface import providedBy
@@ -14,7 +12,7 @@
from pyramid.interfaces import ITraverser
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IResponder
from pyramid.interfaces import IResponse
from pyramid.events import ContextFound
from pyramid.events import NewRequest
@@ -61,7 +59,6 @@
        logger = self.logger
        manager = self.threadlocal_manager
        request = None
        responder = default_responder
        threadlocals = {'registry':registry, 'request':request}
        manager.push(threadlocals)
@@ -159,7 +156,7 @@
                            msg = request.path_info
                        raise HTTPNotFound(msg)
                    else:
                        response = view_callable(context, request)
                        result = view_callable(context, request)
                # handle exceptions raised during root finding and view-exec
                except Exception, why:
@@ -181,52 +178,26 @@
                    # repoze.bfg.message docs-deprecated in Pyramid 1.0
                    environ['repoze.bfg.message'] = msg
                    response = view_callable(why, request)
                    result = view_callable(why, request)
                # process the response
                response = registry.queryAdapterOrSelf(result, IResponse)
                if response is None:
                    raise ValueError(
                        'Could not convert view return value "%s" into a '
                        'response' % (result,))
                has_listeners and registry.notify(NewResponse(request,response))
                if request.response_callbacks:
                    request._process_response_callbacks(response)
                responder = adapters.queryAdapter(response, IResponder)
                if responder is None:
                    responder = default_responder(response)
            finally:
                if request is not None and request.finished_callbacks:
                    request._process_finished_callbacks()
            return responder(request, start_response)
            return response(request.environ, start_response)
        finally:
            manager.pop()
def default_responder(response):
    def inner(request, start_response):
        # __call__ is default 1.1 response API
        call = getattr(response, '__call__', None)
        if call is not None:
            return call(request.environ, start_response)
        # start 1.0 bw compat (use headerlist, app_iter, status)
        try:
            headers = response.headerlist
            app_iter = response.app_iter
            status = response.status
        except AttributeError:
            raise ValueError(
                'Non-response object returned from view '
                '(and no renderer): %r' % (response))
        start_response(status, headers)
        warnings.warn(
            'As of Pyramid 1.1, an object used as a response object is '
            'required to have a "__call__" method if an IResponder adapter is '
            'not registered for its type.  See "Deprecations" in "What\'s New '
            'in Pyramid 1.1" within the general Pyramid documentation for '
            'further details.',
            DeprecationWarning,
            3)
        return app_iter
    return inner
pyramid/session.py
@@ -8,8 +8,6 @@
except ImportError: # pragma: no cover
    import pickle
from webob import Response
import base64
import binascii
import hmac
@@ -213,17 +211,7 @@
                    'Cookie value is too long to store (%s bytes)' %
                    len(cookieval)
                    )
            if hasattr(response, 'set_cookie'):
                # ``response`` is a "real" webob response
                set_cookie = response.set_cookie
            else:
                # ``response`` is not a "real" webob response, cope
                def set_cookie(*arg, **kw):
                    tmp_response = Response()
                    tmp_response.set_cookie(*arg, **kw)
                    response.headerlist.append(
                        tmp_response.headerlist[-1])
            set_cookie(
            response.set_cookie(
                self._cookie_name,
                value=cookieval,
                max_age = self._cookie_max_age,
pyramid/tests/test_config.py
@@ -289,27 +289,58 @@
        result = config.absolute_asset_spec('templates')
        self.assertEqual(result, 'pyramid.tests:templates')
    def test_setup_registry_fixed(self):
        class DummyRegistry(object):
            def subscribers(self, events, name):
                self.events = events
                return events
            def registerUtility(self, *arg, **kw):
                pass
    def test__fix_registry_has_listeners(self):
        reg = DummyRegistry()
        config = self._makeOne(reg)
        config._fix_registry()
        self.assertEqual(reg.has_listeners, True)
    def test__fix_registry_notify(self):
        reg = DummyRegistry()
        config = self._makeOne(reg)
        config._fix_registry()
        self.assertEqual(reg.notify(1), None)
        self.assertEqual(reg.events, (1,))
    def test__fix_registry_queryAdapterOrSelf(self):
        from zope.interface import Interface
        class IFoo(Interface):
            pass
        class Foo(object):
            implements(IFoo)
        class Bar(object):
            pass
        adaptation = ()
        foo = Foo()
        bar = Bar()
        reg = DummyRegistry(adaptation)
        config = self._makeOne(reg)
        config._fix_registry()
        self.assertTrue(reg.queryAdapterOrSelf(foo, IFoo) is foo)
        self.assertTrue(reg.queryAdapterOrSelf(bar, IFoo) is adaptation)
    def test__fix_registry_registerSelfAdapter(self):
        reg = DummyRegistry()
        config = self._makeOne(reg)
        config._fix_registry()
        reg.registerSelfAdapter('required', 'provided', name='abc')
        self.assertEqual(len(reg.adapters), 1)
        args, kw = reg.adapters[0]
        self.assertEqual(args[0]('abc'), 'abc')
        self.assertEqual(kw,
                         {'info': u'', 'provided': 'provided',
                          'required': 'required', 'name': 'abc', 'event': True})
    def test_setup_registry_calls_fix_registry(self):
        reg = DummyRegistry()
        config = self._makeOne(reg)
        config.add_view = lambda *arg, **kw: False
        config.setup_registry()
        self.assertEqual(reg.has_listeners, True)
        self.assertEqual(reg.notify(1), None)
        self.assertEqual(reg.events, (1,))
    def test_setup_registry_registers_default_exceptionresponse_view(self):
        from pyramid.interfaces import IExceptionResponse
        from pyramid.view import default_exceptionresponse_view
        class DummyRegistry(object):
            def registerUtility(self, *arg, **kw):
                pass
        reg = DummyRegistry()
        config = self._makeOne(reg)
        views = []
@@ -317,6 +348,15 @@
        config.setup_registry()
        self.assertEqual(views[0], ((default_exceptionresponse_view,),
                                    {'context':IExceptionResponse}))
    def test_setup_registry_registers_default_webob_iresponse_adapter(self):
        from webob import Response
        from pyramid.interfaces import IResponse
        config = self._makeOne()
        config.setup_registry()
        response = Response()
        self.assertTrue(
            config.registry.queryAdapter(response, IResponse) is response)
    def test_setup_registry_explicit_notfound_trumps_iexceptionresponse(self):
        from zope.interface import implementedBy
@@ -5134,3 +5174,17 @@
def dummy_extend2(config, discrim):
    config.action(discrim, None, config.registry)
    
class DummyRegistry(object):
    def __init__(self, adaptation=None):
        self.utilities = []
        self.adapters = []
        self.adaptation = adaptation
    def subscribers(self, events, name):
        self.events = events
        return events
    def registerUtility(self, *arg, **kw):
        self.utilities.append((arg, kw))
    def registerAdapter(self, *arg, **kw):
        self.adapters.append((arg, kw))
    def queryAdapter(self, *arg, **kw):
        return self.adaptation
pyramid/tests/test_router.py
@@ -2,19 +2,6 @@
from pyramid import testing
def hide_warnings(wrapped):
    import warnings
    def wrapper(*arg, **kw):
        warnings.filterwarnings('ignore')
        try:
            wrapped(*arg, **kw)
        finally:
            warnings.resetwarnings()
    wrapper.__name__ = wrapped.__name__
    wrapper.__doc__ = wrapped.__doc__
    return wrapper
class TestRouter(unittest.TestCase):
    def setUp(self):
        testing.setUp()
@@ -249,7 +236,7 @@
        self.assertTrue("view_name: ''" in message)
        self.assertTrue("subpath: []" in message)
    def test_call_view_returns_nonresponse(self):
    def test_call_view_returns_non_iresponse(self):
        from pyramid.interfaces import IViewClassifier
        context = DummyContext()
        self._registerTraverserFactory(context)
@@ -259,6 +246,24 @@
        router = self._makeOne()
        start_response = DummyStartResponse()
        self.assertRaises(ValueError, router, environ, start_response)
    def test_call_view_returns_adapted_response(self):
        from pyramid.response import Response
        from pyramid.interfaces import IViewClassifier
        from pyramid.interfaces import IResponse
        context = DummyContext()
        self._registerTraverserFactory(context)
        environ = self._makeEnviron()
        view = DummyView('abc')
        self._registerView(view, '', IViewClassifier, None, None)
        router = self._makeOne()
        start_response = DummyStartResponse()
        def make_response(s):
            return Response(s)
        router.registry.registerAdapter(make_response, (str,), IResponse)
        app_iter = router(environ, start_response)
        self.assertEqual(app_iter, ['abc'])
        self.assertEqual(start_response.status, '200 OK')
    def test_call_view_registered_nonspecific_default_path(self):
        from pyramid.interfaces import IViewClassifier
@@ -463,37 +468,6 @@
        start_response = DummyStartResponse()
        exc_raised(NotImplementedError, router, environ, start_response)
        self.assertEqual(environ['called_back'], True)
    def test_call_with_overridden_iresponder_factory(self):
        from zope.interface import Interface
        from zope.interface import directlyProvides
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IViewClassifier
        from pyramid.interfaces import IResponder
        context = DummyContext()
        class IFoo(Interface):
            pass
        directlyProvides(context, IFoo)
        self._registerTraverserFactory(context, subpath=[''])
        class DummyResponder(object):
            def __init__(self, response):
                self.response = response
            def __call__(self, request, start_response):
                self.response.responder_used = True
                return '123'
        self.registry.registerAdapter(DummyResponder, (None,),
                                      IResponder, name='')
        response = DummyResponse('200 OK')
        directlyProvides(response, IFoo)
        def view(context, request):
            return response
        environ = self._makeEnviron()
        self._registerView(view, '', IViewClassifier, IRequest, Interface)
        router = self._makeOne()
        start_response = DummyStartResponse()
        result = router(environ, start_response)
        self.assertTrue(response.responder_used)
        self.assertEqual(result, '123')
    def test_call_request_factory_raises(self):
        # making sure finally doesnt barf when a request cannot be created
@@ -875,7 +849,7 @@
        result = router(environ, start_response)
        self.assertEqual(result, ["Hello, world"])
    def test_exception_view_returns_non_response(self):
    def test_exception_view_returns_non_iresponse(self):
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IViewClassifier
        from pyramid.interfaces import IExceptionViewClassifier
@@ -1072,52 +1046,6 @@
        start_response = DummyStartResponse()
        self.assertRaises(RuntimeError, router, environ, start_response)
class Test_default_responder(unittest.TestCase):
    def _makeOne(self, response):
        from pyramid.router import default_responder
        return default_responder(response)
    def test_has_call(self):
        response = DummyResponse()
        response.app_iter = ['123']
        response.headerlist = [('a', '1')]
        responder = self._makeOne(response)
        request = DummyRequest({'a':'1'})
        start_response = DummyStartResponse()
        app_iter = responder(request, start_response)
        self.assertEqual(app_iter, response.app_iter)
        self.assertEqual(start_response.status, response.status)
        self.assertEqual(start_response.headers, response.headerlist)
        self.assertEqual(response.environ, request.environ)
    @hide_warnings
    def test_without_call_success(self):
        response = DummyResponseWithoutCall()
        response.app_iter = ['123']
        response.headerlist = [('a', '1')]
        responder = self._makeOne(response)
        request = DummyRequest({'a':'1'})
        start_response = DummyStartResponse()
        app_iter = responder(request, start_response)
        self.assertEqual(app_iter, response.app_iter)
        self.assertEqual(start_response.status, response.status)
        self.assertEqual(start_response.headers, response.headerlist)
    @hide_warnings
    def test_without_call_exception(self):
        response = DummyResponseWithoutCall()
        del response.status
        responder = self._makeOne(response)
        request = DummyRequest({'a':'1'})
        start_response = DummyStartResponse()
        self.assertRaises(ValueError, responder, request, start_response)
class DummyRequest(object):
    def __init__(self, environ=None):
        if environ is None: environ = {}
        self.environ = environ
class DummyContext:
    pass
@@ -1147,14 +1075,16 @@
        self.status = status
        self.headers = headers
class DummyResponseWithoutCall:
from pyramid.interfaces import IResponse
from zope.interface import implements
class DummyResponse(object):
    implements(IResponse)
    headerlist = ()
    app_iter = ()
    environ = None
    def __init__(self, status='200 OK'):
        self.status = status
class DummyResponse(DummyResponseWithoutCall):
    environ = None
    def __call__(self, environ, start_response):
        self.environ = environ
pyramid/tests/test_session.py
@@ -90,16 +90,8 @@
        self.assertEqual(session._set_cookie(response), True)
        self.assertEqual(response.headerlist[-1][0], 'Set-Cookie')
    def test__set_cookie_other_kind_of_response(self):
        request = testing.DummyRequest()
        request.exception = None
        session = self._makeOne(request)
        session['abc'] = 'x'
        response = DummyResponse()
        self.assertEqual(session._set_cookie(response), True)
        self.assertEqual(len(response.headerlist), 1)
    def test__set_cookie_options(self):
        from pyramid.response import Response
        request = testing.DummyRequest()
        request.exception = None
        session = self._makeOne(request,
@@ -110,10 +102,9 @@
                                cookie_httponly = True,
                                )
        session['abc'] = 'x'
        response = DummyResponse()
        response = Response()
        self.assertEqual(session._set_cookie(response), True)
        self.assertEqual(len(response.headerlist), 1)
        cookieval= response.headerlist[0][1]
        cookieval= response.headerlist[-1][1]
        val, domain, path, secure, httponly = [x.strip() for x in
                                               cookieval.split(';')]
        self.assertTrue(val.startswith('abc='))
pyramid/tests/test_view.py
@@ -120,6 +120,19 @@
                                 secure=True)
        self.assertEqual(iterable, ())
    def test_call_view_returns_iresponse_adaptable(self):
        from pyramid.response import Response
        request = self._makeRequest()
        context = self._makeContext()
        view = make_view('123')
        self._registerView(request.registry, view, 'registered')
        def str_response(s):
            return Response(s)
        request.registry.registerAdapter(str_response, (str,), IResponse)
        iterable = self._callFUT(context, request, name='registered',
                                 secure=True)
        self.assertEqual(iterable, ['123'])
    def test_call_view_registered_insecure_no_call_permissive(self):
        context = self._makeContext()
        request = self._makeRequest()
@@ -536,9 +549,15 @@
class DummyRequest:
    exception = None
class DummyResponse:
    status = '200 OK'
from pyramid.interfaces import IResponse
from zope.interface import implements
class DummyResponse(object):
    implements(IResponse)
    headerlist = ()
    app_iter = ()
    status = '200 OK'
    environ = None
    def __init__(self, body=None):
        if body is None:
            self.app_iter = ()
pyramid/view.py
@@ -4,6 +4,7 @@
from zope.interface import providedBy
from zope.deprecation import deprecated
from pyramid.interfaces import IResponse
from pyramid.interfaces import IRoutesMapper
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
@@ -100,6 +101,11 @@
    response = render_view_to_response(context, request, name, secure)
    if response is None:
        return None
    try:
        reg = request.registry
    except AttributeError:
        reg = get_current_registry()
    response = reg.queryAdapterOrSelf(response, IResponse)
    return response.app_iter
def render_view(context, request, name='', secure=True):