Michael Merickel
2017-02-26 0bee841b9e9537a912b14017601de63e7efeabf1
add an IExecutionPolicy that can wrap the router
10 files modified
160 ■■■■■ changed files
docs/api/config.rst 1 ●●●● patch | view | raw | blame | history
docs/api/interfaces.rst 3 ●●●●● patch | view | raw | blame | history
docs/glossary.rst 4 ●●●● patch | view | raw | blame | history
pyramid/config/factories.py 25 ●●●●● patch | view | raw | blame | history
pyramid/interfaces.py 43 ●●●●● patch | view | raw | blame | history
pyramid/router.py 46 ●●●● patch | view | raw | blame | history
pyramid/tests/pkgs/subrequestapp/__init__.py 4 ●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_factories.py 19 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_integration.py 2 ●●● patch | view | raw | blame | history
pyramid/tests/test_router.py 13 ●●●●● patch | view | raw | blame | history
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
docs/api/interfaces.rst
@@ -65,6 +65,9 @@
  .. autointerface:: IResponseFactory
     :members:
  .. autointerface:: IRouter
     :members:
  .. autointerface:: IViewMapperFactory
     :members:
docs/glossary.rst
@@ -1154,3 +1154,7 @@
   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`.
pyramid/config/factories.py
@@ -3,6 +3,7 @@
from pyramid.interfaces import (
    IDefaultRootFactory,
    IExecutionPolicy,
    IRequestFactory,
    IResponseFactory,
    IRequestExtensions,
@@ -10,6 +11,7 @@
    ISessionFactory,
    )
from pyramid.router import default_execution_policy
from pyramid.traversal import DefaultRootFactory
from pyramid.util import (
@@ -231,6 +233,29 @@
        '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):
pyramid/interfaces.py
@@ -682,7 +682,48 @@
    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."""
pyramid/router.py
@@ -5,6 +5,7 @@
from pyramid.interfaces import (
    IDebugLogger,
    IExecutionPolicy,
    IRequest,
    IRequestExtensions,
    IRootFactory,
@@ -49,6 +50,8 @@
        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 @@
        :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 @@
            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 @@
                    request._process_response_callbacks(response)
                has_listeners and notify(NewResponse(request, response))
                return response
            finally:
@@ -229,6 +249,10 @@
        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)
pyramid/tests/pkgs/subrequestapp/__init__.py
@@ -7,7 +7,8 @@
    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 @@
    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
pyramid/tests/test_config/test_factories.py
@@ -144,6 +144,24 @@
        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 @@
        config.set_request_property(bar, name='bar')
        self.assertRaises(ConfigurationConflictError, config.commit)
pyramid/tests/test_integration.py
@@ -610,7 +610,7 @@
    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)
pyramid/tests/test_router.py
@@ -1271,6 +1271,19 @@
        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