Chris McDonough
2011-06-14 1a6fc7062f803b9f15b7677db9a9257a4f00bfcb
- Added new add_response_adapter method to Configurator.

- Fix Configurator docstring wrt exception responses.

- Speed up registry.queryAdapterOrSelf
7 files modified
96 ■■■■ changed files
CHANGES.txt 6 ●●●●● patch | view | raw | blame | history
docs/api/config.rst 2 ●●●●● patch | view | raw | blame | history
docs/narr/hooks.rst 13 ●●●●● patch | view | raw | blame | history
pyramid/config.py 42 ●●●● patch | view | raw | blame | history
pyramid/registry.py 3 ●●●● patch | view | raw | blame | history
pyramid/router.py 2 ●●● patch | view | raw | blame | history
pyramid/tests/test_config.py 28 ●●●●● patch | view | raw | blame | history
CHANGES.txt
@@ -130,8 +130,10 @@
- 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".
  object by using the new
  ``pyramid.config.Configurator.add_response_adapter`` API.  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,
docs/api/config.rst
@@ -40,6 +40,8 @@
     .. automethod:: add_renderer(name, factory)
     .. automethod:: add_response_adapter
     .. automethod:: add_route
     .. automethod:: add_static_view(name, path, cache_max_age=3600, permission='__no_permission_required__')
docs/narr/hooks.rst
@@ -532,7 +532,7 @@
It is possible to control how Pyramid treats the result of calling a view
callable on a per-type basis by using a hook involving
:class:`pyramid.interfaces.IResponse`.
:method:`pyramid.config.Configurator.add_response_adapter`.
.. note:: This feature is new as of Pyramid 1.1.
@@ -559,7 +559,6 @@
.. code-block:: python
   :linenos:
   from pyramid.interfaces import IResponse
   from pyramid.response import Response
   def string_response_adapter(s):
@@ -568,8 +567,7 @@
   # config is an instance of pyramid.config.Configurator
   config.registry.registerAdapter(string_response_adapter, (str,),
                                   IResponse)
   config.add_response_adapter(string_response_adapter, str)
Likewise, if you want to be able to return a simplified kind of response
object from view callables, you can use the IResponse hook to register an
@@ -578,7 +576,6 @@
.. code-block:: python
   :linenos:
   from pyramid.interfaces import IResponse
   from pyramid.response import Response
   class SimpleResponse(object):
@@ -591,14 +588,12 @@
   # config is an instance of pyramid.config.Configurator
   config.registry.registerAdapter(simple_response_adapter,
                                  (SimpleResponse,),
                                   IResponse)
   config.add_response_adapter(simple_response_adapter, SimpleResponse)
If you want to implement your own Response object instead of using the
:class:`pyramid.response.Response` object in any capacity at all, you'll have
to make sure the object implements every attribute and method outlined in
:class:`pyramid.interfaces.IResponse` *and* you'll have to ensure that it's
:class:`pyramid.interfaces.IResponse` and you'll have to ensure that it's
marked up with ``zope.interface.implements(IResponse)``:
   from pyramid.interfaces import IResponse
pyramid/config.py
@@ -19,7 +19,6 @@
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
@@ -260,15 +259,11 @@
    If ``exceptionresponse_view`` is passed, it must be a :term:`view
    callable` or ``None``.  If it is a view callable, it will be used as an
    exception view callable when an :term:`exception response` is raised (any
    object that implements the :class:`pyramid.interaces.IExceptionResponse`
    interface, such as a :class:`pyramid.response.Response` object or any
    ``HTTP`` exception documented in :mod:`pyramid.httpexceptions` as well as
    exception responses raised via :func:`pyramid.exceptions.abort`,
    :func:`pyramid.exceptions.redirect`).  If ``exceptionresponse_view`` is
    ``None``, no exception response view will be registered, and all raised
    exception responses will be bubbled up to Pyramid's caller.  By
    default, the ``pyramid.exceptions.default_exceptionresponse_view``
    exception view callable when an :term:`exception response` is raised. If
    ``exceptionresponse_view`` is ``None``, no exception response view will
    be registered, and all raised exception responses will be bubbled up to
    Pyramid's caller.  By
    default, the ``pyramid.httpexceptions.default_exceptionresponse_view``
    function is used as the ``exceptionresponse_view``.  This argument is new
    in Pyramid 1.1.  """
@@ -433,8 +428,7 @@
        if not hasattr(_registry, 'queryAdapterOrSelf'):
            def queryAdapterOrSelf(object, interface, default=None):
                provides = providedBy(object)
                if not interface in provides:
                if not interface.providedBy(object):
                    return _registry.queryAdapter(object, interface,
                                                  default=default)
                return object
@@ -893,6 +887,30 @@
        self.action(None, register)
        return subscriber
    @action_method
    def add_response_adapter(self, adapter, type_or_iface):
        """ When an object of type (or interface) ``type_or_iface`` is
        returned from a view callable, Pyramid will use the adapter
        ``adapter`` to convert it into an object which implements the
        :class:`pyramid.interfaces.IResponse` interface.  If ``adapter`` is
        None, an object returned of type (or interface) ``type_or_iface``
        will itself be used as a response object.
        ``adapter`` and ``type_or_interface`` may be Python objects or
        strings representing dotted names to importable Python global
        objects.
        See :ref:`using_iresponse` for more information."""
        adapter = self.maybe_dotted(adapter)
        type_or_iface = self.maybe_dotted(type_or_iface)
        def register():
            reg = self.registry
            if adapter is None:
                reg.registerSelfAdapter((type_or_iface,), IResponse)
            else:
                reg.registerAdapter(adapter, (type_or_iface,), IResponse)
        self.action((IResponse, type_or_iface), register)
    def add_settings(self, settings=None, **kw):
        """Augment the ``settings`` argument passed in to the Configurator
        constructor with one or more 'setting' key/value pairs.  A setting is
pyramid/registry.py
@@ -41,8 +41,7 @@
        # 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:
        if not interface.providedBy(object):
            return self.queryAdapter(object, interface, default=default)
        return object
pyramid/router.py
@@ -185,7 +185,7 @@
                if response is None:
                    raise ValueError(
                        'Could not convert view return value "%s" into a '
                        'response' % (result,))
                        'response object' % (result,))
                has_listeners and registry.notify(NewResponse(request,response))
pyramid/tests/test_config.py
@@ -2628,6 +2628,34 @@
        self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'),
                         pyramid.tests)
    def test_add_response_adapter(self):
        from pyramid.interfaces import IResponse
        config = self._makeOne(autocommit=True)
        class Adapter(object):
            def __init__(self, other):
                self.other = other
        config.add_response_adapter(Adapter, str)
        result = config.registry.queryAdapter('foo', IResponse)
        self.assertTrue(result.other, 'foo')
    def test_add_response_adapter_self(self):
        from pyramid.interfaces import IResponse
        config = self._makeOne(autocommit=True)
        class Adapter(object):
            pass
        config.add_response_adapter(None, Adapter)
        adapter = Adapter()
        result = config.registry.queryAdapter(adapter, IResponse)
        self.assertTrue(result is adapter)
    def test_add_response_adapter_dottednames(self):
        from pyramid.interfaces import IResponse
        config = self._makeOne(autocommit=True)
        config.add_response_adapter('pyramid.response.Response',
                                    'types.StringType')
        result = config.registry.queryAdapter('foo', IResponse)
        self.assertTrue(result.body, 'foo')
    def test_scan_integration(self):
        import os
        from zope.interface import alsoProvides