summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt42
-rw-r--r--TODO.txt19
-rw-r--r--docs/api/interfaces.rst2
-rw-r--r--docs/api/request.rst33
-rw-r--r--docs/designdefense.rst4
-rw-r--r--docs/glossary.rst12
-rw-r--r--docs/narr/assets.rst2
-rw-r--r--docs/narr/hooks.rst134
-rw-r--r--docs/narr/renderers.rst2
-rw-r--r--docs/narr/router.rst6
-rw-r--r--docs/narr/views.rst36
-rw-r--r--docs/narr/webob.rst66
-rw-r--r--docs/tutorials/wiki/definingviews.rst10
-rw-r--r--docs/tutorials/wiki2/definingviews.rst3
-rw-r--r--pyramid/config.py39
-rw-r--r--pyramid/interfaces.py8
-rw-r--r--pyramid/registry.py19
-rw-r--r--pyramid/renderers.py4
-rw-r--r--pyramid/request.py2
-rw-r--r--pyramid/response.py2
-rw-r--r--pyramid/router.py47
-rw-r--r--pyramid/session.py14
-rw-r--r--pyramid/tests/test_config.py78
-rw-r--r--pyramid/tests/test_router.py122
-rw-r--r--pyramid/tests/test_session.py15
-rw-r--r--pyramid/tests/test_view.py23
-rw-r--r--pyramid/view.py6
27 files changed, 411 insertions, 339 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index e413f0657..5e8df1a0b 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -127,11 +127,11 @@ Features
- 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 @@ Behavior Changes
``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
------------
diff --git a/TODO.txt b/TODO.txt
index 4b82208bd..ca2433d3c 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -6,14 +6,27 @@ Must-Have
- 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
-----------
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
index 3a60fa4dc..51a1963b5 100644
--- a/docs/api/interfaces.rst
+++ b/docs/api/interfaces.rst
@@ -57,6 +57,6 @@ Other Interfaces
.. autointerface:: IMultiDict
:members:
- .. autointerface:: IResponder
+ .. autointerface:: IResponse
:members:
diff --git a/docs/api/request.rst b/docs/api/request.rst
index 8cb424658..27ce395ac 100644
--- a/docs/api/request.rst
+++ b/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::
diff --git a/docs/designdefense.rst b/docs/designdefense.rst
index 136b9c5de..de6c0af33 100644
--- a/docs/designdefense.rst
+++ b/docs/designdefense.rst
@@ -428,7 +428,7 @@ allowing people to define "custom" view predicates:
: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 @@ comments take into account what we've discussed in the
.. 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
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 079a069b4..d3ba9a545 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -16,12 +16,12 @@ Glossary
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
diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst
index 8d0e7058c..0d50b0106 100644
--- a/docs/narr/assets.rst
+++ b/docs/narr/assets.rst
@@ -358,7 +358,7 @@ do so, do things "by hand". First define the view callable.
:linenos:
import os
- from webob import Response
+ from pyramid.response import Response
def favicon_view(request):
here = os.path.dirname(__file__)
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
diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst
index 99ee14908..c4a37c23d 100644
--- a/docs/narr/renderers.rst
+++ b/docs/narr/renderers.rst
@@ -416,7 +416,7 @@ effect, you must return ``request.response``:
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:
diff --git a/docs/narr/router.rst b/docs/narr/router.rst
index 30d54767e..0812f7ec7 100644
--- a/docs/narr/router.rst
+++ b/docs/narr/router.rst
@@ -115,9 +115,9 @@ processing?
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
diff --git a/docs/narr/views.rst b/docs/narr/views.rst
index 990828f80..e3d0a37e5 100644
--- a/docs/narr/views.rst
+++ b/docs/narr/views.rst
@@ -230,29 +230,19 @@ implements the :term:`Response` interface is to return a
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:
-
-.. 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`).
+: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`.
+
+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
diff --git a/docs/narr/webob.rst b/docs/narr/webob.rst
index 70ab5eea8..0ff8e1de7 100644
--- a/docs/narr/webob.rst
+++ b/docs/narr/webob.rst
@@ -10,15 +10,15 @@ Request and Response Objects
.. 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 @@ authors and a fully separate `set of documentation
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 @@ of the request. I'll show various values for an example URL
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 @@ only a few you'll use often:
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 @@ Response
~~~~~~~~
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 @@ A response object has three fundamental parts:
``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 @@ Exception Responses
+++++++++++++++++++
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,
diff --git a/docs/tutorials/wiki/definingviews.rst b/docs/tutorials/wiki/definingviews.rst
index b6c083bbf..92a3da09c 100644
--- a/docs/tutorials/wiki/definingviews.rst
+++ b/docs/tutorials/wiki/definingviews.rst
@@ -84,10 +84,12 @@ No renderer is necessary when a view returns a response object.
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
-------------------------------
diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst
index 832f90b92..43cbc3483 100644
--- a/docs/tutorials/wiki2/definingviews.rst
+++ b/docs/tutorials/wiki2/definingviews.rst
@@ -91,7 +91,8 @@ path to our "FrontPage".
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.
diff --git a/pyramid/config.py b/pyramid/config.py
index 91ba414b3..fab75f56d 100644
--- a/pyramid/config.py
+++ b/pyramid/config.py
@@ -19,6 +19,7 @@ from zope.interface import implementedBy
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 IRendererFactory
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.traversal import traversal_path
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 @@ class Configurator(object):
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
@@ -429,6 +431,24 @@ class Configurator(object):
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()
registerCommonDirectives(context)
@@ -697,6 +717,9 @@ class Configurator(object):
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 @@ class ViewDeriver(object):
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
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index b8ff2d4c9..dea7174fb 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -233,14 +233,6 @@ class IMultiDict(Interface): # docs-only interface
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):
diff --git a/pyramid/registry.py b/pyramid/registry.py
index 37e230dc3..26f84d493 100644
--- a/pyramid/registry.py
+++ b/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 @@ class Registry(Components, dict):
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
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index a6dce9b3a..6865067dd 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -316,9 +316,7 @@ class RendererHelper(object):
'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
diff --git a/pyramid/request.py b/pyramid/request.py
index d387a0b2f..b69440ac6 100644
--- a/pyramid/request.py
+++ b/pyramid/request.py
@@ -5,12 +5,14 @@ from zope.interface.interface import InterfaceClass
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
diff --git a/pyramid/response.py b/pyramid/response.py
index 1d2ef296f..68496e386 100644
--- a/pyramid/response.py
+++ b/pyramid/response.py
@@ -4,4 +4,4 @@ from pyramid.interfaces import IResponse
class Response(_Response):
implements(IResponse)
-
+
diff --git a/pyramid/router.py b/pyramid/router.py
index 4d2750efb..48640b39d 100644
--- a/pyramid/router.py
+++ b/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 IRoutesMapper
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 @@ class Router(object):
logger = self.logger
manager = self.threadlocal_manager
request = None
- responder = default_responder
threadlocals = {'registry':registry, 'request':request}
manager.push(threadlocals)
@@ -159,7 +156,7 @@ class Router(object):
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 @@ class Router(object):
# 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
-
diff --git a/pyramid/session.py b/pyramid/session.py
index 5772c80d0..bb3226a1e 100644
--- a/pyramid/session.py
+++ b/pyramid/session.py
@@ -8,8 +8,6 @@ try:
except ImportError: # pragma: no cover
import pickle
-from webob import Response
-
import base64
import binascii
import hmac
@@ -213,17 +211,7 @@ def UnencryptedCookieSessionFactoryConfig(
'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,
diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py
index 703a2577c..49bfab396 100644
--- a/pyramid/tests/test_config.py
+++ b/pyramid/tests/test_config.py
@@ -289,27 +289,58 @@ class ConfiguratorTests(unittest.TestCase):
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.add_view = lambda *arg, **kw: False
- config.setup_registry()
+ 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)
+
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 = []
@@ -318,6 +349,15 @@ class ConfiguratorTests(unittest.TestCase):
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
from pyramid.interfaces import IRequest
@@ -5134,3 +5174,17 @@ def dummy_extend(config, discrim):
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
diff --git a/pyramid/tests/test_router.py b/pyramid/tests/test_router.py
index 765a26751..5fd2cf01e 100644
--- a/pyramid/tests/test_router.py
+++ b/pyramid/tests/test_router.py
@@ -2,19 +2,6 @@ import unittest
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 @@ class TestRouter(unittest.TestCase):
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)
@@ -260,6 +247,24 @@ class TestRouter(unittest.TestCase):
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
context = DummyContext()
@@ -464,37 +469,6 @@ class TestRouter(unittest.TestCase):
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
environ = self._makeEnviron()
@@ -875,7 +849,7 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class DummyStartResponse:
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
diff --git a/pyramid/tests/test_session.py b/pyramid/tests/test_session.py
index 17a437af6..5c6454a38 100644
--- a/pyramid/tests/test_session.py
+++ b/pyramid/tests/test_session.py
@@ -90,16 +90,8 @@ class TestUnencryptedCookieSession(unittest.TestCase):
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 @@ class TestUnencryptedCookieSession(unittest.TestCase):
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='))
diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py
index b1d48b98b..0ea9a11a0 100644
--- a/pyramid/tests/test_view.py
+++ b/pyramid/tests/test_view.py
@@ -120,6 +120,19 @@ class RenderViewToIterableTests(BaseTest, unittest.TestCase):
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 @@ def make_view(response):
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 = ()
diff --git a/pyramid/view.py b/pyramid/view.py
index 9a4be7580..a89df8859 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -4,6 +4,7 @@ import venusian
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 @@ def render_view_to_iterable(context, request, name='', secure=True):
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):