summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2015-01-01 23:51:48 -0600
committerMichael Merickel <michael@merickel.org>2015-01-01 23:51:48 -0600
commit0414b7c7b2e3ea45d4dcbdf273a9e332238a0f50 (patch)
tree624e02d9edf442a748e592e289130c9604552afa
parent568b583af39880991470a5a23b2f1d5755f7c566 (diff)
parent303abc8b585d97c75773b3cfa48b6e748c96fd64 (diff)
downloadpyramid-0414b7c7b2e3ea45d4dcbdf273a9e332238a0f50.tar.gz
pyramid-0414b7c7b2e3ea45d4dcbdf273a9e332238a0f50.tar.bz2
pyramid-0414b7c7b2e3ea45d4dcbdf273a9e332238a0f50.zip
Merge pull request #1499 from sontek/expose_response_class
Expose response class
-rw-r--r--CHANGES.txt5
-rw-r--r--docs/glossary.rst4
-rw-r--r--docs/narr/hooks.rst47
-rw-r--r--pyramid/config/__init__.py16
-rw-r--r--pyramid/config/factories.py27
-rw-r--r--pyramid/renderers.py9
-rw-r--r--pyramid/request.py9
-rw-r--r--pyramid/response.py17
-rw-r--r--pyramid/testing.py8
-rw-r--r--pyramid/tests/test_config/test_factories.py15
-rw-r--r--pyramid/tests/test_config/test_init.py12
-rw-r--r--pyramid/tests/test_renderers.py9
-rw-r--r--pyramid/tests/test_response.py17
-rw-r--r--pyramid/tests/test_testing.py4
-rw-r--r--pyramid/tests/test_util.py4
-rw-r--r--pyramid/util.py6
16 files changed, 179 insertions, 30 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 46c331268..129ce4616 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -59,6 +59,11 @@ Features
via ``request.static_url('myapp:static/foo.png')``.
See https://github.com/Pylons/pyramid/issues/1252
+- Added ``pyramid.config.Configurator.set_response_factory`` and the
+ ``response_factory`` keyword argument to the ``Configurator`` for defining
+ a factory that will return a custom ``Response`` class.
+ See https://github.com/Pylons/pyramid/pull/1499
+
Bug Fixes
---------
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 01300a0be..911c22075 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -16,6 +16,10 @@ Glossary
An object which, provided a :term:`WSGI` environment as a single
positional argument, returns a Pyramid-compatible request.
+ response factory
+ An object which, provided a :term:`request` as a single positional
+ argument, returns a Pyramid-compatible response.
+
response
An object returned by a :term:`view callable` that represents response
data returned to the requesting user agent. It must implement the
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index 4da36e730..e250c2d7e 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -354,6 +354,53 @@ We attach and cache an object named ``extra`` to the ``request`` object.
.. _beforerender_event:
+.. index::
+ single: response factory
+
+.. _changing_the_response_factory:
+
+Changing the Response Factory
+-------------------------------
+
+.. versionadded:: 1.6
+
+Whenever :app:`Pyramid` returns a response from a view it creates a
+:term:`response` object. By default, an instance of the
+:class:`pyramid.response.Response` class is created to represent the response
+object.
+
+The factory that :app:`Pyramid` uses to create a response object instance can be
+changed by passing a ``response_factory`` argument to the constructor of the
+:term:`configurator`. This argument can be either a callable or a
+:term:`dotted Python name` representing a callable.
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.response import Response
+
+ class MyResponse(Response):
+ pass
+
+ config = Configurator(response_factory=lambda r: MyResponse())
+
+If you're doing imperative configuration, and you'd rather do it after you've
+already constructed a :term:`configurator` it can also be registered via the
+:meth:`pyramid.config.Configurator.set_response_factory` method:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.config import Configurator
+ from pyramid.response import Response
+
+ class MyResponse(Response):
+ pass
+
+ config = Configurator()
+ config.set_response_factory(lambda r: MyResponse())
+
+
Using The Before Render Event
-----------------------------
diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py
index cfa35ec6c..2ab654b9a 100644
--- a/pyramid/config/__init__.py
+++ b/pyramid/config/__init__.py
@@ -179,6 +179,11 @@ class Configurator(
See :ref:`changing_the_request_factory`. By default it is ``None``,
which means use the default request factory.
+ If ``response_factory`` is passed, it should be a :term:`response
+ factory` implementation or a :term:`dotted Python name` to the same.
+ See :ref:`changing_the_response_factory`. By default it is ``None``,
+ which means use the default response factory.
+
If ``default_permission`` is passed, it should be a
:term:`permission` string to be used as the default permission for
all view configuration registrations performed against this
@@ -190,7 +195,7 @@ class Configurator(
configurations which do not explicitly declare a permission will
always be executable by entirely anonymous users (any
authorization policy in effect is ignored).
-
+
.. seealso::
See also :ref:`setting_a_default_permission`.
@@ -254,6 +259,7 @@ class Configurator(
.. versionadded:: 1.6
The ``root_package`` argument.
+ The ``response_factory`` argument.
"""
manager = manager # for testing injection
venusian = venusian # for testing injection
@@ -276,6 +282,7 @@ class Configurator(
debug_logger=None,
locale_negotiator=None,
request_factory=None,
+ response_factory=None,
default_permission=None,
session_factory=None,
default_view_mapper=None,
@@ -310,6 +317,7 @@ class Configurator(
debug_logger=debug_logger,
locale_negotiator=locale_negotiator,
request_factory=request_factory,
+ response_factory=response_factory,
default_permission=default_permission,
session_factory=session_factory,
default_view_mapper=default_view_mapper,
@@ -325,6 +333,7 @@ class Configurator(
debug_logger=None,
locale_negotiator=None,
request_factory=None,
+ response_factory=None,
default_permission=None,
session_factory=None,
default_view_mapper=None,
@@ -412,6 +421,9 @@ class Configurator(
if request_factory:
self.set_request_factory(request_factory)
+ if response_factory:
+ self.set_response_factory(response_factory)
+
if default_permission:
self.set_default_permission(default_permission)
@@ -469,7 +481,7 @@ class Configurator(
_registry.registerSelfAdapter = registerSelfAdapter
# API
-
+
def _get_introspector(self):
introspector = getattr(self.registry, 'introspector', _marker)
if introspector is _marker:
diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py
index 5ce1081c6..d7a48ba93 100644
--- a/pyramid/config/factories.py
+++ b/pyramid/config/factories.py
@@ -4,6 +4,7 @@ from zope.interface import implementer
from pyramid.interfaces import (
IDefaultRootFactory,
IRequestFactory,
+ IResponseFactory,
IRequestExtensions,
IRootFactory,
ISessionFactory,
@@ -97,6 +98,32 @@ class FactoriesConfiguratorMixin(object):
self.action(IRequestFactory, register, introspectables=(intr,))
@action_method
+ def set_response_factory(self, factory):
+ """ The object passed as ``factory`` should be an object (or a
+ :term:`dotted Python name` which refers to an object) which
+ will be used by the :app:`Pyramid` as the default response
+ objects. This factory object must have the same
+ methods and attributes as the
+ :class:`pyramid.request.Response` class.
+
+ .. note::
+
+ Using the ``response_factory`` argument to the
+ :class:`pyramid.config.Configurator` constructor
+ can be used to achieve the same purpose.
+ """
+ factory = self.maybe_dotted(factory)
+
+ def register():
+ self.registry.registerUtility(factory, IResponseFactory)
+
+ intr = self.introspectable('response factory', None,
+ self.object_description(factory),
+ 'response factory')
+ intr['factory'] = factory
+ self.action(IResponseFactory, register, introspectables=(intr,))
+
+ @action_method
def add_request_method(self,
callable=None,
name=None,
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index e647ebacf..d57671865 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -10,7 +10,6 @@ from zope.interface.registry import Components
from pyramid.interfaces import (
IJSONAdapter,
IRendererFactory,
- IResponseFactory,
IRendererInfo,
)
@@ -25,7 +24,7 @@ from pyramid.events import BeforeRender
from pyramid.path import caller_package
-from pyramid.response import Response
+from pyramid.response import Response, _get_response_factory
from pyramid.threadlocal import get_current_registry
# API
@@ -448,10 +447,8 @@ class RendererHelper(object):
if response is None:
# request is None or request is not a pyramid.response.Response
registry = self.registry
- response_factory = registry.queryUtility(IResponseFactory,
- default=Response)
-
- response = response_factory()
+ response_factory = _get_response_factory(registry)
+ response = response_factory(request)
if result is not None:
if isinstance(result, text_type):
diff --git a/pyramid/request.py b/pyramid/request.py
index bc2889310..b2e2efe05 100644
--- a/pyramid/request.py
+++ b/pyramid/request.py
@@ -10,7 +10,6 @@ from pyramid.interfaces import (
IRequest,
IResponse,
ISessionFactory,
- IResponseFactory,
)
from pyramid.compat import (
@@ -21,7 +20,7 @@ from pyramid.compat import (
from pyramid.decorator import reify
from pyramid.i18n import LocalizerRequestMixin
-from pyramid.response import Response
+from pyramid.response import Response, _get_response_factory
from pyramid.security import (
AuthenticationAPIMixin,
AuthorizationAPIMixin,
@@ -214,10 +213,8 @@ class Request(
right" attributes (e.g. by calling ``request.response.set_cookie()``)
within a view that uses a renderer. Mutations to this response object
will be preserved in the response sent to the client."""
- registry = self.registry
- response_factory = registry.queryUtility(IResponseFactory,
- default=Response)
- return response_factory()
+ response_factory = _get_response_factory(self.registry)
+ return response_factory(self)
def is_response(self, ob):
""" Return ``True`` if the object passed as ``ob`` is a valid
diff --git a/pyramid/response.py b/pyramid/response.py
index d11fd0123..892e5dfff 100644
--- a/pyramid/response.py
+++ b/pyramid/response.py
@@ -8,7 +8,8 @@ import venusian
from webob import Response as _Response
from zope.interface import implementer
-from pyramid.interfaces import IResponse
+from pyramid.interfaces import IResponse, IResponseFactory
+
def init_mimetypes(mimetypes):
# this is a function so it can be unittested
@@ -143,7 +144,7 @@ class response_adapter(object):
@response_adapter(dict, list)
def myadapter(ob):
return Response(json.dumps(ob))
-
+
This method will have no effect until a :term:`scan` is performed
agains the package or module which contains it, ala:
@@ -167,3 +168,15 @@ class response_adapter(object):
def __call__(self, wrapped):
self.venusian.attach(wrapped, self.register, category='pyramid')
return wrapped
+
+
+def _get_response_factory(registry):
+ """ Obtain a :class: `pyramid.response.Response` using the
+ `pyramid.interfaces.IResponseFactory`.
+ """
+ response_factory = registry.queryUtility(
+ IResponseFactory,
+ default=lambda r: Response()
+ )
+
+ return response_factory
diff --git a/pyramid/testing.py b/pyramid/testing.py
index f77889e72..667e6af4e 100644
--- a/pyramid/testing.py
+++ b/pyramid/testing.py
@@ -9,7 +9,6 @@ from zope.interface import (
from pyramid.interfaces import (
IRequest,
- IResponseFactory,
ISession,
)
@@ -22,7 +21,7 @@ from pyramid.compat import (
from pyramid.config import Configurator
from pyramid.decorator import reify
from pyramid.path import caller_package
-from pyramid.response import Response
+from pyramid.response import Response, _get_response_factory
from pyramid.registry import Registry
from pyramid.security import (
@@ -42,6 +41,7 @@ from pyramid.request import CallbackMethodsMixin
from pyramid.url import URLMethodsMixin
from pyramid.util import InstancePropertyMixin
+
_marker = object()
class DummyRootFactory(object):
@@ -383,8 +383,8 @@ class DummyRequest(
@reify
def response(self):
- f = self.registry.queryUtility(IResponseFactory, default=Response)
- return f()
+ f = _get_response_factory(self.registry)
+ return f(self)
have_zca = True
diff --git a/pyramid/tests/test_config/test_factories.py b/pyramid/tests/test_config/test_factories.py
index 6e679397f..0bd5336ff 100644
--- a/pyramid/tests/test_config/test_factories.py
+++ b/pyramid/tests/test_config/test_factories.py
@@ -23,6 +23,21 @@ class TestFactoriesMixin(unittest.TestCase):
self.assertEqual(config.registry.getUtility(IRequestFactory),
dummyfactory)
+ def test_set_response_factory(self):
+ from pyramid.interfaces import IResponseFactory
+ config = self._makeOne(autocommit=True)
+ factory = lambda r: object()
+ config.set_response_factory(factory)
+ self.assertEqual(config.registry.getUtility(IResponseFactory), factory)
+
+ def test_set_response_factory_dottedname(self):
+ from pyramid.interfaces import IResponseFactory
+ config = self._makeOne(autocommit=True)
+ config.set_response_factory(
+ 'pyramid.tests.test_config.dummyfactory')
+ self.assertEqual(config.registry.getUtility(IResponseFactory),
+ dummyfactory)
+
def test_set_root_factory(self):
from pyramid.interfaces import IRootFactory
config = self._makeOne()
diff --git a/pyramid/tests/test_config/test_init.py b/pyramid/tests/test_config/test_init.py
index 1e58e4d0f..aeebe3c91 100644
--- a/pyramid/tests/test_config/test_init.py
+++ b/pyramid/tests/test_config/test_init.py
@@ -546,6 +546,18 @@ class ConfiguratorTests(unittest.TestCase):
utility = reg.getUtility(IRequestFactory)
self.assertEqual(utility, factory)
+ def test_setup_registry_response_factory(self):
+ from pyramid.registry import Registry
+ from pyramid.interfaces import IResponseFactory
+ reg = Registry()
+ config = self._makeOne(reg)
+ factory = lambda r: object()
+ config.setup_registry(response_factory=factory)
+ self.assertEqual(reg.queryUtility(IResponseFactory), None)
+ config.commit()
+ utility = reg.getUtility(IResponseFactory)
+ self.assertEqual(utility, factory)
+
def test_setup_registry_request_factory_dottedname(self):
from pyramid.registry import Registry
from pyramid.interfaces import IRequestFactory
diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py
index 2bddd2318..21878b41f 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -182,7 +182,10 @@ class TestRendererHelper(unittest.TestCase):
from pyramid.interfaces import IResponseFactory
class ResponseFactory(object):
pass
- self.config.registry.registerUtility(ResponseFactory, IResponseFactory)
+
+ self.config.registry.registerUtility(
+ lambda r: ResponseFactory(), IResponseFactory
+ )
def test_render_to_response(self):
self._registerRendererFactory()
@@ -310,7 +313,9 @@ class TestRendererHelper(unittest.TestCase):
class ResponseFactory(object):
def __init__(self):
pass
- self.config.registry.registerUtility(ResponseFactory, IResponseFactory)
+ self.config.registry.registerUtility(
+ lambda r: ResponseFactory(), IResponseFactory
+ )
request = testing.DummyRequest()
helper = self._makeOne('loo.foo')
response = helper._make_response(b'abc', request)
diff --git a/pyramid/tests/test_response.py b/pyramid/tests/test_response.py
index 84ec57757..ad55882c9 100644
--- a/pyramid/tests/test_response.py
+++ b/pyramid/tests/test_response.py
@@ -8,7 +8,7 @@ class TestResponse(unittest.TestCase):
def _getTargetClass(self):
from pyramid.response import Response
return Response
-
+
def test_implements_IResponse(self):
from pyramid.interfaces import IResponse
cls = self._getTargetClass()
@@ -119,7 +119,7 @@ class Test_patch_mimetypes(unittest.TestCase):
result = self._callFUT(module)
self.assertEqual(result, True)
self.assertEqual(module.initted, True)
-
+
def test_missing_init(self):
class DummyMimetypes(object):
pass
@@ -174,6 +174,17 @@ class TestResponseAdapter(unittest.TestCase):
self.assertEqual(dummy_venusian.attached,
[(foo, dec.register, 'pyramid')])
+
+class TestGetResponseFactory(unittest.TestCase):
+ def test_get_factory(self):
+ from pyramid.registry import Registry
+ from pyramid.response import Response, _get_response_factory
+
+ registry = Registry()
+ response = _get_response_factory(registry)(None)
+ self.assertTrue(isinstance(response, Response))
+
+
class Dummy(object):
pass
@@ -190,5 +201,3 @@ class DummyVenusian(object):
def attach(self, wrapped, fn, category=None):
self.attached.append((wrapped, fn, category))
-
-
diff --git a/pyramid/tests/test_testing.py b/pyramid/tests/test_testing.py
index dfcad2a0c..113f7e5f4 100644
--- a/pyramid/tests/test_testing.py
+++ b/pyramid/tests/test_testing.py
@@ -259,7 +259,9 @@ class TestDummyRequest(unittest.TestCase):
registry = Registry('this_test')
class ResponseFactory(object):
pass
- registry.registerUtility(ResponseFactory, IResponseFactory)
+ registry.registerUtility(
+ lambda r: ResponseFactory(), IResponseFactory
+ )
request = self._makeOne()
request.registry = registry
resp = request.response
diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py
index a18fa8d16..ac5ea0683 100644
--- a/pyramid/tests/test_util.py
+++ b/pyramid/tests/test_util.py
@@ -324,7 +324,7 @@ class Test_object_description(unittest.TestCase):
self.assertEqual(
self._callFUT(inst),
"object %s" % str(inst))
-
+
def test_shortened_repr(self):
inst = ['1'] * 1000
self.assertEqual(
@@ -592,7 +592,7 @@ class TestActionInfo(unittest.TestCase):
def _getTargetClass(self):
from pyramid.util import ActionInfo
return ActionInfo
-
+
def _makeOne(self, filename, lineno, function, linerepr):
return self._getTargetClass()(filename, lineno, function, linerepr)
diff --git a/pyramid/util.py b/pyramid/util.py
index 6de53d559..4ca2937a1 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -15,6 +15,10 @@ from pyramid.exceptions import (
CyclicDependencyError,
)
+from pyramid.interfaces import (
+ IResponseFactory,
+ )
+
from pyramid.compat import (
iteritems_,
is_nonstr_iter,
@@ -25,6 +29,7 @@ from pyramid.compat import (
)
from pyramid.interfaces import IActionInfo
+from pyramid.response import Response
from pyramid.path import DottedNameResolver as _DottedNameResolver
class DottedNameResolver(_DottedNameResolver):
@@ -550,4 +555,3 @@ def action_method(wrapped):
functools.update_wrapper(wrapper, wrapped)
wrapper.__docobj__ = wrapped
return wrapper
-