summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Anderson <sontek@gmail.com>2015-01-01 22:18:45 -0800
committerJohn Anderson <sontek@gmail.com>2015-01-01 22:18:45 -0800
commit35d64fd7ff6e1caf7cf43e94039c2748fd86dfbe (patch)
treeb19a2833212cd2d877250e606c0b726fca706c21
parent22c836ecbc6f10c4851d88017243f91e469016aa (diff)
parent0414b7c7b2e3ea45d4dcbdf273a9e332238a0f50 (diff)
downloadpyramid-35d64fd7ff6e1caf7cf43e94039c2748fd86dfbe.tar.gz
pyramid-35d64fd7ff6e1caf7cf43e94039c2748fd86dfbe.tar.bz2
pyramid-35d64fd7ff6e1caf7cf43e94039c2748fd86dfbe.zip
Merge branch 'master' of https://github.com/Pylons/pyramid into support_more_features_in_routes
-rw-r--r--CHANGES.txt5
-rw-r--r--docs/_static/pyramid_router.pngbin0 -> 120643 bytes
m---------docs/_themes0
-rw-r--r--docs/glossary.rst4
-rw-r--r--docs/narr/hooks.rst47
-rw-r--r--docs/narr/router.rst4
-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/scripts/prequest.py8
-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_scripts/test_prequest.py13
-rw-r--r--pyramid/tests/test_testing.py4
-rw-r--r--pyramid/tests/test_util.py4
-rw-r--r--pyramid/util.py6
-rw-r--r--rtd.txt2
-rw-r--r--setup.py2
23 files changed, 203 insertions, 35 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/_static/pyramid_router.png b/docs/_static/pyramid_router.png
new file mode 100644
index 000000000..3c9f81158
--- /dev/null
+++ b/docs/_static/pyramid_router.png
Binary files differ
diff --git a/docs/_themes b/docs/_themes
-Subproject 3bec9280a6cedb15e97e5899021aa8d723c2538
+Subproject b14bf8c2a0d95ae8e3d38d07ad3721370ae6f3f
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/docs/narr/router.rst b/docs/narr/router.rst
index 693217a6b..6f90c70cc 100644
--- a/docs/narr/router.rst
+++ b/docs/narr/router.rst
@@ -9,7 +9,7 @@
Request Processing
==================
-.. image:: ../_static/pyramid_request_processing.svg
+.. image:: ../_static/pyramid_request_processing.*
:alt: Request Processing
Once a :app:`Pyramid` application is up and running, it is ready to accept
@@ -119,7 +119,7 @@ request enters a :app:`Pyramid` application through to the point that
#. The :term:`thread local` stack is popped.
-.. image:: ../_static/pyramid_router.svg
+.. image:: ../_static/pyramid_router.*
:alt: Pyramid Router
This is a very high-level overview that leaves out various details. For more
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/scripts/prequest.py b/pyramid/scripts/prequest.py
index 2ab3b8bb9..34eeadf32 100644
--- a/pyramid/scripts/prequest.py
+++ b/pyramid/scripts/prequest.py
@@ -5,7 +5,7 @@ import textwrap
from pyramid.compat import url_unquote
from pyramid.request import Request
-from pyramid.paster import get_app
+from pyramid.paster import get_app, setup_logging
from pyramid.scripts.common import parse_vars
def main(argv=sys.argv, quiet=False):
@@ -97,12 +97,18 @@ class PRequestCommand(object):
if not self.quiet:
print(msg)
+ def configure_logging(self, app_spec):
+ setup_logging(app_spec)
+
def run(self):
if not len(self.args) >= 2:
self.out('You must provide at least two arguments')
return 2
app_spec = self.args[0]
path = self.args[1]
+
+ self.configure_logging(app_spec)
+
if not path.startswith('/'):
path = '/' + path
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_scripts/test_prequest.py b/pyramid/tests/test_scripts/test_prequest.py
index 37f1d3c0f..95cec0518 100644
--- a/pyramid/tests/test_scripts/test_prequest.py
+++ b/pyramid/tests/test_scripts/test_prequest.py
@@ -210,8 +210,21 @@ class TestPRequestCommand(unittest.TestCase):
self.assertEqual(self._path_info, '/')
self.assertEqual(self._spec, 'development.ini')
self.assertEqual(self._app_name, None)
+
self.assertEqual(self._out, [b'abc'])
+ def test_command_method_configures_logging(self):
+ command = self._makeOne(['', 'development.ini', '/'])
+ called_args = []
+
+ def configure_logging(app_spec):
+ called_args.append(app_spec)
+
+ command.configure_logging = configure_logging
+ command.run()
+ self.assertEqual(called_args, ['development.ini'])
+
+
class Test_main(unittest.TestCase):
def _callFUT(self, argv):
from pyramid.scripts.prequest import main
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
-
diff --git a/rtd.txt b/rtd.txt
index b449ac73c..4aecd9933 100644
--- a/rtd.txt
+++ b/rtd.txt
@@ -1,4 +1,4 @@
+Sphinx >= 1.2.3
repoze.sphinx.autointerface
repoze.lru
pylons_sphinx_latesturl
-
diff --git a/setup.py b/setup.py
index d736dc38d..629b77f3d 100644
--- a/setup.py
+++ b/setup.py
@@ -56,7 +56,7 @@ if not PY3:
tests_require.append('zope.component>=3.11.0')
docs_extras = [
- 'Sphinx',
+ 'Sphinx >= 1.2.3',
'docutils',
'repoze.sphinx.autointerface',
]