summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2017-02-25 17:23:50 -0600
committerMichael Merickel <michael@merickel.org>2017-02-25 17:47:59 -0600
commit0bee841b9e9537a912b14017601de63e7efeabf1 (patch)
tree4501c046c1d1233fb7f9e13e75568d4de4c9674a
parentd3cb4b5796f341da609fe3397e31be67c68b32c1 (diff)
downloadpyramid-0bee841b9e9537a912b14017601de63e7efeabf1.tar.gz
pyramid-0bee841b9e9537a912b14017601de63e7efeabf1.tar.bz2
pyramid-0bee841b9e9537a912b14017601de63e7efeabf1.zip
add an IExecutionPolicy that can wrap the router
-rw-r--r--docs/api/config.rst1
-rw-r--r--docs/api/interfaces.rst3
-rw-r--r--docs/glossary.rst4
-rw-r--r--pyramid/config/factories.py25
-rw-r--r--pyramid/interfaces.py43
-rw-r--r--pyramid/router.py46
-rw-r--r--pyramid/tests/pkgs/subrequestapp/__init__.py4
-rw-r--r--pyramid/tests/test_config/test_factories.py19
-rw-r--r--pyramid/tests/test_integration.py2
-rw-r--r--pyramid/tests/test_router.py13
10 files changed, 145 insertions, 15 deletions
diff --git a/docs/api/config.rst b/docs/api/config.rst
index 62f138b76..c76d3d5ff 100644
--- a/docs/api/config.rst
+++ b/docs/api/config.rst
@@ -70,6 +70,7 @@
.. automethod:: add_subscriber_predicate
.. automethod:: add_view_predicate
.. automethod:: add_view_deriver
+ .. automethod:: set_execution_policy
.. automethod:: set_request_factory
.. automethod:: set_root_factory
.. automethod:: set_session_factory
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
index 521d65d2b..a212ba7a9 100644
--- a/docs/api/interfaces.rst
+++ b/docs/api/interfaces.rst
@@ -65,6 +65,9 @@ Other Interfaces
.. autointerface:: IResponseFactory
:members:
+ .. autointerface:: IRouter
+ :members:
+
.. autointerface:: IViewMapperFactory
:members:
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 0f299c169..3a55a9f8a 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -1154,3 +1154,7 @@ Glossary
coverage
A measurement of code coverage, usually expressed as a percentage of which lines of code have been executed over which lines are executable, typically run during test execution.
+ execution policy
+ A policy which wraps the :term:`router` by creating the request object
+ and sending it through the request pipeline.
+ See :class:`pyramid.config.Configurator.set_execution_policy`.
diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py
index f0b6252ae..c8633cc47 100644
--- a/pyramid/config/factories.py
+++ b/pyramid/config/factories.py
@@ -3,6 +3,7 @@ from zope.interface import implementer
from pyramid.interfaces import (
IDefaultRootFactory,
+ IExecutionPolicy,
IRequestFactory,
IResponseFactory,
IRequestExtensions,
@@ -10,6 +11,7 @@ from pyramid.interfaces import (
ISessionFactory,
)
+from pyramid.router import default_execution_policy
from pyramid.traversal import DefaultRootFactory
from pyramid.util import (
@@ -231,6 +233,29 @@ class FactoriesConfiguratorMixin(object):
'set_request_propery() is deprecated as of Pyramid 1.5; use '
'add_request_method() with the property=True argument instead')
+ @action_method
+ def set_execution_policy(self, policy):
+ """
+ Override the :app:`Pyramid` :term:`execution policy` in the
+ current configuration. The ``policy`` argument must be an instance
+ of an :class:`pyramid.interfaces.IExecutionPolicy` or a
+ :term:`dotted Python name` that points at an instance of an
+ execution policy.
+
+ """
+ policy = self.maybe_dotted(policy)
+ if policy is None:
+ policy = default_execution_policy
+
+ def register():
+ self.registry.registerUtility(policy, IExecutionPolicy)
+
+ intr = self.introspectable('execution policy', None,
+ self.object_description(policy),
+ 'execution policy')
+ intr['policy'] = policy
+ self.action(IExecutionPolicy, register, introspectables=(intr,))
+
@implementer(IRequestExtensions)
class _RequestExtensions(object):
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index 450cd9c24..bbb4754e4 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -682,7 +682,48 @@ class IRouter(Interface):
registry = Attribute(
"""Component architecture registry local to this application.""")
-class ISettings(Interface):
+ def make_request(environ):
+ """
+ Create a new request object.
+
+ This method initializes a new :class:`pyramid.interfaces.IRequest`
+ object using the application's
+ :class:`pyramid.interfaces.IRequestFactory`.
+ """
+
+ def invoke_request(request):
+ """
+ Invoke the :app:`Pyramid` request pipeline.
+
+ See :ref:`router_chapter` for information on the request pipeline.
+ """
+
+class IExecutionPolicy(Interface):
+ def __call__(environ, router):
+ """
+ This callable triggers the router to process a raw WSGI environ dict
+ into a response and controls the :app:`Pyramid` request pipeline.
+
+ The ``environ`` is the raw WSGI environ.
+
+ The ``router`` is an :class:`pyramid.interfaces.IRouter` object which
+ should be used to create a request object and send it into the
+ processing pipeline.
+
+ The return value should be a :class:`pyramid.interfaces.IResponse`
+ object or an exception that will be handled by WSGI middleware.
+
+ The default execution policy simple creates a request and sends it
+ through the pipeline:
+
+ .. code-block:: python
+
+ def simple_execution_policy(environ, router):
+ request = router.make_request(environ)
+ return router.invoke_request(request)
+ """
+
+class ISettings(IDict):
""" Runtime settings utility for pyramid; represents the
deployment settings for the application. Implements a mapping
interface."""
diff --git a/pyramid/router.py b/pyramid/router.py
index fd11925e9..8b7b7b6bc 100644
--- a/pyramid/router.py
+++ b/pyramid/router.py
@@ -5,6 +5,7 @@ from zope.interface import (
from pyramid.interfaces import (
IDebugLogger,
+ IExecutionPolicy,
IRequest,
IRequestExtensions,
IRootFactory,
@@ -49,6 +50,8 @@ class Router(object):
self.routes_mapper = q(IRoutesMapper)
self.request_factory = q(IRequestFactory, default=Request)
self.request_extensions = q(IRequestExtensions)
+ self.execution_policy = q(
+ IExecutionPolicy, default=default_execution_policy)
self.orig_handle_request = self.handle_request
tweens = q(ITweens)
if tweens is not None:
@@ -182,19 +185,36 @@ class Router(object):
:term:`tween` in the tween stack closest to the request ingress. If
``use_tweens`` is ``False``, the request will be sent to the main
router handler, and no tweens will be invoked.
-
+
See the API for pyramid.request for complete documentation.
"""
+ request.registry = self.registry
+ request.invoke_subrequest = self.invoke_subrequest
+ return self.invoke_request(
+ request,
+ _use_tweens=use_tweens,
+ _apply_extensions=True,
+ )
+
+ def make_request(self, environ):
+ request = self.request_factory(environ)
+ request.registry = self.registry
+ request.invoke_subrequest = self.invoke_subrequest
+ extensions = self.request_extensions
+ if extensions is not None:
+ apply_request_extensions(request, extensions=extensions)
+ return request
+
+ def invoke_request(self, request,
+ _use_tweens=True, _apply_extensions=False):
registry = self.registry
has_listeners = self.registry.has_listeners
notify = self.registry.notify
- threadlocals = {'registry':registry, 'request':request}
+ threadlocals = {'registry': registry, 'request': request}
manager = self.threadlocal_manager
manager.push(threadlocals)
- request.registry = registry
- request.invoke_subrequest = self.invoke_subrequest
-
- if use_tweens:
+
+ if _use_tweens:
handle_request = self.handle_request
else:
handle_request = self.orig_handle_request
@@ -203,7 +223,7 @@ class Router(object):
try:
extensions = self.request_extensions
- if extensions is not None:
+ if _apply_extensions and extensions is not None:
apply_request_extensions(request, extensions=extensions)
response = handle_request(request)
@@ -211,7 +231,7 @@ class Router(object):
request._process_response_callbacks(response)
has_listeners and notify(NewResponse(request, response))
-
+
return response
finally:
@@ -229,6 +249,10 @@ class Router(object):
within the application registry; call ``start_response`` and
return an iterable.
"""
- request = self.request_factory(environ)
- response = self.invoke_subrequest(request, use_tweens=True)
- return response(request.environ, start_response)
+ response = self.execution_policy(environ, self)
+ return response(environ, start_response)
+
+
+def default_execution_policy(environ, router):
+ request = router.make_request(environ)
+ return router.invoke_request(request)
diff --git a/pyramid/tests/pkgs/subrequestapp/__init__.py b/pyramid/tests/pkgs/subrequestapp/__init__.py
index b8f44cd7f..e4b1d386a 100644
--- a/pyramid/tests/pkgs/subrequestapp/__init__.py
+++ b/pyramid/tests/pkgs/subrequestapp/__init__.py
@@ -7,7 +7,8 @@ def view_one(request):
return response
def view_two(request):
- return 'This came from view_two'
+ # check that request.foo is valid for a subrequest
+ return 'This came from view_two, foo=%s' % (request.foo,)
def view_three(request):
subreq = Request.blank('/view_four')
@@ -46,5 +47,6 @@ def main():
config.add_view(view_three, route_name='three')
config.add_view(view_four, route_name='four')
config.add_view(view_five, route_name='five')
+ config.add_request_method(lambda r: 'bar', 'foo', property=True)
return config
diff --git a/pyramid/tests/test_config/test_factories.py b/pyramid/tests/test_config/test_factories.py
index 452d762f8..eb1f3534c 100644
--- a/pyramid/tests/test_config/test_factories.py
+++ b/pyramid/tests/test_config/test_factories.py
@@ -144,6 +144,24 @@ class TestFactoriesMixin(unittest.TestCase):
self.assertRaises(ConfigurationError, get_bad_name)
+ def test_set_execution_policy(self):
+ from pyramid.interfaces import IExecutionPolicy
+ config = self._makeOne(autocommit=True)
+ def dummy_policy(environ, router): pass
+ config.set_execution_policy(dummy_policy)
+ registry = config.registry
+ result = registry.queryUtility(IExecutionPolicy)
+ self.assertEqual(result, dummy_policy)
+
+ def test_set_execution_policy_to_None(self):
+ from pyramid.interfaces import IExecutionPolicy
+ from pyramid.router import default_execution_policy
+ config = self._makeOne(autocommit=True)
+ config.set_execution_policy(None)
+ registry = config.registry
+ result = registry.queryUtility(IExecutionPolicy)
+ self.assertEqual(result, default_execution_policy)
+
class TestDeprecatedFactoriesMixinMethods(unittest.TestCase):
def setUp(self):
from zope.deprecation import __show__
@@ -203,4 +221,3 @@ class TestDeprecatedFactoriesMixinMethods(unittest.TestCase):
config.set_request_property(bar, name='bar')
self.assertRaises(ConfigurationConflictError, config.commit)
-
diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py
index c2786c391..85c4466a4 100644
--- a/pyramid/tests/test_integration.py
+++ b/pyramid/tests/test_integration.py
@@ -610,7 +610,7 @@ class SubrequestAppTest(unittest.TestCase):
def test_one(self):
res = self.testapp.get('/view_one', status=200)
- self.assertTrue(b'This came from view_two' in res.body)
+ self.assertTrue(b'This came from view_two, foo=bar' in res.body)
def test_three(self):
res = self.testapp.get('/view_three', status=500)
diff --git a/pyramid/tests/test_router.py b/pyramid/tests/test_router.py
index 7aa42804c..a5da5c627 100644
--- a/pyramid/tests/test_router.py
+++ b/pyramid/tests/test_router.py
@@ -1271,6 +1271,19 @@ class TestRouter(unittest.TestCase):
start_response = DummyStartResponse()
self.assertRaises(PredicateMismatch, router, environ, start_response)
+ def test_custom_execution_policy(self):
+ from pyramid.interfaces import IExecutionPolicy
+ from pyramid.request import Request
+ from pyramid.response import Response
+ registry = self.config.registry
+ def dummy_policy(environ, router):
+ return Response(status=200, body=b'foo')
+ registry.registerUtility(dummy_policy, IExecutionPolicy)
+ router = self._makeOne()
+ resp = Request.blank('/').get_response(router)
+ self.assertEqual(resp.status_code, 200)
+ self.assertEqual(resp.body, b'foo')
+
class DummyPredicate(object):
def __call__(self, info, request):
return True