diff options
| author | Michael Merickel <michael@merickel.org> | 2015-01-01 23:51:48 -0600 |
|---|---|---|
| committer | Michael Merickel <michael@merickel.org> | 2015-01-01 23:51:48 -0600 |
| commit | 0414b7c7b2e3ea45d4dcbdf273a9e332238a0f50 (patch) | |
| tree | 624e02d9edf442a748e592e289130c9604552afa | |
| parent | 568b583af39880991470a5a23b2f1d5755f7c566 (diff) | |
| parent | 303abc8b585d97c75773b3cfa48b6e748c96fd64 (diff) | |
| download | pyramid-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.txt | 5 | ||||
| -rw-r--r-- | docs/glossary.rst | 4 | ||||
| -rw-r--r-- | docs/narr/hooks.rst | 47 | ||||
| -rw-r--r-- | pyramid/config/__init__.py | 16 | ||||
| -rw-r--r-- | pyramid/config/factories.py | 27 | ||||
| -rw-r--r-- | pyramid/renderers.py | 9 | ||||
| -rw-r--r-- | pyramid/request.py | 9 | ||||
| -rw-r--r-- | pyramid/response.py | 17 | ||||
| -rw-r--r-- | pyramid/testing.py | 8 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_factories.py | 15 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_init.py | 12 | ||||
| -rw-r--r-- | pyramid/tests/test_renderers.py | 9 | ||||
| -rw-r--r-- | pyramid/tests/test_response.py | 17 | ||||
| -rw-r--r-- | pyramid/tests/test_testing.py | 4 | ||||
| -rw-r--r-- | pyramid/tests/test_util.py | 4 | ||||
| -rw-r--r-- | pyramid/util.py | 6 |
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 - |
