From 499b78aea5bc94626a48022afcf8cf92afb55cf8 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Sun, 23 Aug 2015 11:59:51 -0400 Subject: Add a RouteFound event which will fire after a route is found --- pyramid/events.py | 19 +++++++++++++++++++ pyramid/interfaces.py | 8 ++++++++ pyramid/router.py | 3 +++ 3 files changed, 30 insertions(+) diff --git a/pyramid/events.py b/pyramid/events.py index 97375638e..78ebf4d70 100644 --- a/pyramid/events.py +++ b/pyramid/events.py @@ -11,6 +11,7 @@ from pyramid.interfaces import ( INewResponse, IApplicationCreated, IBeforeRender, + IRouteFound, ) class subscriber(object): @@ -129,6 +130,24 @@ class NewResponse(object): self.request = request self.response = response +@implementer(IRouteFound) +class RouteFound(object): + """ + An instance of this class is emitted as an :term:`event` after the + :app:`Pyramid` :term:`router` finds a :term:`route` object but before any + traversal or view code is executed. The instance has an attribute, + ``request``, which is the request object generated by :app:`Pyramid`. + + Notably, the request object will have an attributed named + ``matched_route``, which is the matched route that was found. + + This class implements the :class:`pyramid.interfaces.IRouteFound` + interface. + """ + + def __init__(self, request): + self.request = request + @implementer(IContextFound) class ContextFound(object): """ An instance of this class is emitted as an :term:`event` after diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index 90534593c..baf36610a 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -25,6 +25,14 @@ class IContextFound(Interface): IAfterTraversal = IContextFound +class IRouteFound(Interface): + """ + An event type that is emitted whenever :app:`Pyramid` has found a route + but before it calls any traversal or view code. See the documentation + attached to :class:`pyramid.events.Routefound` for more information. + """ + request = Attribute('The request object') + class INewRequest(Interface): """ An event type that is emitted whenever :app:`Pyramid` begins to process a new request. See the documentation attached diff --git a/pyramid/router.py b/pyramid/router.py index 4054ef52e..c4b86f89d 100644 --- a/pyramid/router.py +++ b/pyramid/router.py @@ -20,6 +20,7 @@ from pyramid.events import ( ContextFound, NewRequest, NewResponse, + RouteFound, ) from pyramid.httpexceptions import HTTPNotFound @@ -112,6 +113,8 @@ class Router(object): name=route.name, default=IRequest) + has_listeners and notify(RouteFound(request)) + root_factory = route.factory or self.root_factory root = root_factory(request) -- cgit v1.2.3 From d4f5a87e4859cfc3959afa4d85a516f8c307ef70 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sun, 10 Apr 2016 21:51:38 -0600 Subject: Rename RouteFound to BeforeTraversal --- pyramid/events.py | 8 ++++---- pyramid/interfaces.py | 2 +- pyramid/router.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pyramid/events.py b/pyramid/events.py index 78ebf4d70..e467e4012 100644 --- a/pyramid/events.py +++ b/pyramid/events.py @@ -11,7 +11,7 @@ from pyramid.interfaces import ( INewResponse, IApplicationCreated, IBeforeRender, - IRouteFound, + IBeforeTraversal, ) class subscriber(object): @@ -130,8 +130,8 @@ class NewResponse(object): self.request = request self.response = response -@implementer(IRouteFound) -class RouteFound(object): +@implementer(IBeforeTraversal) +class BeforeTraversal(object): """ An instance of this class is emitted as an :term:`event` after the :app:`Pyramid` :term:`router` finds a :term:`route` object but before any @@ -141,7 +141,7 @@ class RouteFound(object): Notably, the request object will have an attributed named ``matched_route``, which is the matched route that was found. - This class implements the :class:`pyramid.interfaces.IRouteFound` + This class implements the :class:`pyramid.interfaces.IBeforeTraversal` interface. """ diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index cf559ef06..298eaf303 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -25,7 +25,7 @@ class IContextFound(Interface): IAfterTraversal = IContextFound -class IRouteFound(Interface): +class IBeforeTraversal(Interface): """ An event type that is emitted whenever :app:`Pyramid` has found a route but before it calls any traversal or view code. See the documentation diff --git a/pyramid/router.py b/pyramid/router.py index c4b86f89d..99ea6ffa5 100644 --- a/pyramid/router.py +++ b/pyramid/router.py @@ -20,7 +20,7 @@ from pyramid.events import ( ContextFound, NewRequest, NewResponse, - RouteFound, + BeforeTraversal, ) from pyramid.httpexceptions import HTTPNotFound @@ -113,7 +113,7 @@ class Router(object): name=route.name, default=IRequest) - has_listeners and notify(RouteFound(request)) + has_listeners and notify(BeforeTraversal(request)) root_factory = route.factory or self.root_factory -- cgit v1.2.3 From 732d80b476ccc883fc7b6209a4256ef97946e1eb Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sun, 10 Apr 2016 21:55:52 -0600 Subject: Add API docs for BeforeTraversal --- docs/api/events.rst | 2 ++ docs/api/interfaces.rst | 3 +++ 2 files changed, 5 insertions(+) diff --git a/docs/api/events.rst b/docs/api/events.rst index 31a0e22c1..0a8463740 100644 --- a/docs/api/events.rst +++ b/docs/api/events.rst @@ -21,6 +21,8 @@ Event Types .. autoclass:: ContextFound +.. autoclass:: BeforeTraversal + .. autoclass:: NewResponse .. autoclass:: BeforeRender diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst index 635d3c5b6..272820a91 100644 --- a/docs/api/interfaces.rst +++ b/docs/api/interfaces.rst @@ -17,6 +17,9 @@ Event-Related Interfaces .. autointerface:: IContextFound :members: + .. autointerface:: IBeforeTraversal + :members: + .. autointerface:: INewResponse :members: -- cgit v1.2.3 From 20bc06ed03142bd184cb2f7322bc229e8e4dc9fa Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sun, 10 Apr 2016 22:15:38 -0600 Subject: Move event to the appropriate location This way the notification is not sent only after finding a route, but anytime generically before attempting traversal. --- pyramid/router.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pyramid/router.py b/pyramid/router.py index 99ea6ffa5..19773cf62 100644 --- a/pyramid/router.py +++ b/pyramid/router.py @@ -113,14 +113,21 @@ class Router(object): name=route.name, default=IRequest) - has_listeners and notify(BeforeTraversal(request)) - root_factory = route.factory or self.root_factory + # Notify anyone listening that we are about to start traversal + # + # Notify before creating root_factory in case we want to do something + # special on a route we may have matched. See + # https://github.com/Pylons/pyramid/pull/1876 for ideas of what is + # possible. + has_listeners and notify(BeforeTraversal(request)) + + # Create the root factory root = root_factory(request) attrs['root'] = root - # find a context + # We are about to traverse and find a context traverser = adapters.queryAdapter(root, ITraverser) if traverser is None: traverser = ResourceTreeTraverser(root) @@ -136,6 +143,9 @@ class Router(object): ) attrs.update(tdict) + + # Notify anyone listening that we have a context and traversal is + # complete has_listeners and notify(ContextFound(request)) # find a view callable -- cgit v1.2.3 From 4112d67d7dd987b9b34fed9b01165c01308790b6 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sun, 10 Apr 2016 22:16:32 -0600 Subject: Update doc in interfaces.py for BeforeTraversal --- pyramid/interfaces.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index 298eaf303..2b00752cf 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -27,8 +27,8 @@ IAfterTraversal = IContextFound class IBeforeTraversal(Interface): """ - An event type that is emitted whenever :app:`Pyramid` has found a route - but before it calls any traversal or view code. See the documentation + An event type that is emitted after :app:`Pyramid` attempted to find a + route but before it calls any traversal or view code. See the documentation attached to :class:`pyramid.events.Routefound` for more information. """ request = Attribute('The request object') -- cgit v1.2.3 From 3f34aa05a48e5d9ae7b43f717b5ad9061effb08c Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sun, 10 Apr 2016 22:17:19 -0600 Subject: Update documentation in event.py for BeforeTraversal --- pyramid/events.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pyramid/events.py b/pyramid/events.py index e467e4012..35da2fa6f 100644 --- a/pyramid/events.py +++ b/pyramid/events.py @@ -134,12 +134,14 @@ class NewResponse(object): class BeforeTraversal(object): """ An instance of this class is emitted as an :term:`event` after the - :app:`Pyramid` :term:`router` finds a :term:`route` object but before any - traversal or view code is executed. The instance has an attribute, - ``request``, which is the request object generated by :app:`Pyramid`. + :app:`Pyramid` :term:`router` has attempted to find a :term:`route` object + but before any traversal or view code is executed. The instance has an + attribute, ``request``, which is the request object generated by + :app:`Pyramid`. - Notably, the request object will have an attributed named - ``matched_route``, which is the matched route that was found. + Notably, the request object **may** have an attribute named + ``matched_route``, which is the matched route if found. If no route + matched, this attribute is not available. This class implements the :class:`pyramid.interfaces.IBeforeTraversal` interface. @@ -175,7 +177,7 @@ class ContextFound(object): AfterTraversal = ContextFound # b/c as of 1.0 @implementer(IApplicationCreated) -class ApplicationCreated(object): +class ApplicationCreated(object): """ An instance of this class is emitted as an :term:`event` when the :meth:`pyramid.config.Configurator.make_wsgi_app` is called. The instance has an attribute, ``app``, which is an @@ -262,4 +264,3 @@ class BeforeRender(dict): dict.__init__(self, system) self.rendering_val = rendering_val - -- cgit v1.2.3 From 5b918737fb361584d820893fc82c6b898ae1fc69 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sun, 10 Apr 2016 22:18:21 -0600 Subject: Update router documentation --- docs/narr/router.rst | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/narr/router.rst b/docs/narr/router.rst index e02142e6e..d4c0cc885 100644 --- a/docs/narr/router.rst +++ b/docs/narr/router.rst @@ -46,16 +46,22 @@ request enters a :app:`Pyramid` application through to the point that object. The former contains a dictionary representing the matched dynamic elements of the request's ``PATH_INFO`` value, and the latter contains the :class:`~pyramid.interfaces.IRoute` object representing the route which - matched. The root object associated with the route found is also generated: + matched. + + A :class:`~pyramid.events.BeforeTraversal` :term:`event` is sent to any + subscribers. + + The root object associated with the route found is also generated: if the :term:`route configuration` which matched has an associated ``factory`` argument, this factory is used to generate the root object, otherwise a default :term:`root factory` is used. #. If a route match was *not* found, and a ``root_factory`` argument was passed to the :term:`Configurator` constructor, that callable is used to generate - the root object. If the ``root_factory`` argument passed to the - Configurator constructor was ``None``, a default root factory is used to - generate a root object. + the root object after a :class:`~pyramid.events.BeforeTraversal` + :term:`event` is sent to any subscribers. If the ``root_factory`` argument + passed to the Configurator constructor was ``None``, a default root factory + is used to generate a root object. #. The :app:`Pyramid` router calls a "traverser" function with the root object and the request. The traverser function attempts to traverse the root -- cgit v1.2.3 From 59428d50df0e9c4221fff3b46f205b9d573d1056 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sun, 10 Apr 2016 22:32:29 -0600 Subject: Add explicit tests for IBeforeTraversal/BeforeTraversal Although the new code was already being covered by other tests, this adds some explicit testing to make sure it all works. --- pyramid/tests/test_events.py | 21 +++++++++++++++++++++ pyramid/tests/test_router.py | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/pyramid/tests/test_events.py b/pyramid/tests/test_events.py index 2c72c07e8..eec395012 100644 --- a/pyramid/tests/test_events.py +++ b/pyramid/tests/test_events.py @@ -124,6 +124,27 @@ class AfterTraversalEventTests(ContextFoundEventTests): from pyramid.interfaces import IAfterTraversal verifyObject(IAfterTraversal, self._makeOne()) +class BeforeTraversalEventTests(unittest.TestCase): + def _getTargetClass(self): + from pyramid.events import BeforeTraversal + return BeforeTraversal + + def _makeOne(self, request=None): + if request is None: + request = DummyRequest() + return self._getTargetClass()(request) + + def test_class_conforms_to_IBeforeTraversal(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IBeforeTraversal + verifyClass(IBeforeTraversal, self._getTargetClass()) + + def test_instance_conforms_to_IBeforeTraversal(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IBeforeTraversal + verifyObject(IBeforeTraversal, self._makeOne()) + + class TestSubscriber(unittest.TestCase): def setUp(self): self.config = testing.setUp() diff --git a/pyramid/tests/test_router.py b/pyramid/tests/test_router.py index 1cdc4abaa..7aa42804c 100644 --- a/pyramid/tests/test_router.py +++ b/pyramid/tests/test_router.py @@ -591,6 +591,7 @@ class TestRouter(unittest.TestCase): def test_call_eventsends(self): from pyramid.interfaces import INewRequest from pyramid.interfaces import INewResponse + from pyramid.interfaces import IBeforeTraversal from pyramid.interfaces import IContextFound from pyramid.interfaces import IViewClassifier context = DummyContext() @@ -601,6 +602,7 @@ class TestRouter(unittest.TestCase): environ = self._makeEnviron() self._registerView(view, '', IViewClassifier, None, None) request_events = self._registerEventListener(INewRequest) + beforetraversal_events = self._registerEventListener(IBeforeTraversal) context_found_events = self._registerEventListener(IContextFound) response_events = self._registerEventListener(INewResponse) router = self._makeOne() @@ -608,6 +610,8 @@ class TestRouter(unittest.TestCase): result = router(environ, start_response) self.assertEqual(len(request_events), 1) self.assertEqual(request_events[0].request.environ, environ) + self.assertEqual(len(beforetraversal_events), 1) + self.assertEqual(beforetraversal_events[0].request.environ, environ) self.assertEqual(len(context_found_events), 1) self.assertEqual(context_found_events[0].request.environ, environ) self.assertEqual(context_found_events[0].request.context, context) -- cgit v1.2.3 From e224ffabfc70beadd583629d325e598ffd286361 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sun, 10 Apr 2016 22:33:17 -0600 Subject: Fix whitespace --- pyramid/tests/test_events.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pyramid/tests/test_events.py b/pyramid/tests/test_events.py index eec395012..52e53c34e 100644 --- a/pyramid/tests/test_events.py +++ b/pyramid/tests/test_events.py @@ -14,7 +14,7 @@ class NewRequestEventTests(unittest.TestCase): from zope.interface.verify import verifyClass klass = self._getTargetClass() verifyClass(INewRequest, klass) - + def test_instance_conforms_to_INewRequest(self): from pyramid.interfaces import INewRequest from zope.interface.verify import verifyObject @@ -40,7 +40,7 @@ class NewResponseEventTests(unittest.TestCase): from zope.interface.verify import verifyClass klass = self._getTargetClass() verifyClass(INewResponse, klass) - + def test_instance_conforms_to_INewResponse(self): from pyramid.interfaces import INewResponse from zope.interface.verify import verifyObject @@ -103,7 +103,7 @@ class ContextFoundEventTests(unittest.TestCase): from zope.interface.verify import verifyClass from pyramid.interfaces import IContextFound verifyClass(IContextFound, self._getTargetClass()) - + def test_instance_conforms_to_IContextFound(self): from zope.interface.verify import verifyObject from pyramid.interfaces import IContextFound @@ -118,7 +118,7 @@ class AfterTraversalEventTests(ContextFoundEventTests): from zope.interface.verify import verifyClass from pyramid.interfaces import IAfterTraversal verifyClass(IAfterTraversal, self._getTargetClass()) - + def test_instance_conforms_to_IAfterTraversal(self): from zope.interface.verify import verifyObject from pyramid.interfaces import IAfterTraversal @@ -242,7 +242,7 @@ class TestBeforeRender(unittest.TestCase): result = event.setdefault('a', 1) self.assertEqual(result, 1) self.assertEqual(event, {'a':1}) - + def test_setdefault_success(self): event = self._makeOne({}) event['a'] = 1 @@ -303,7 +303,7 @@ class DummyConfigurator(object): class DummyRegistry(object): pass - + class DummyVenusian(object): def __init__(self): self.attached = [] @@ -313,7 +313,7 @@ class DummyVenusian(object): class Dummy: pass - + class DummyRequest: pass -- cgit v1.2.3 From 0da7b0de590b835d6d7df361394b8bf70797f566 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 11 Apr 2016 13:29:50 -0700 Subject: - upgrade `BeforeTraversal` event in router.rst --- docs/narr/router.rst | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/docs/narr/router.rst b/docs/narr/router.rst index d4c0cc885..e45e6f4a8 100644 --- a/docs/narr/router.rst +++ b/docs/narr/router.rst @@ -41,27 +41,26 @@ request enters a :app:`Pyramid` application through to the point that user-defined :term:`route` matches the current WSGI environment. The :term:`router` passes the request as an argument to the mapper. -#. If any route matches, the route mapper adds attributes to the request: - ``matchdict`` and ``matched_route`` attributes are added to the request - object. The former contains a dictionary representing the matched dynamic - elements of the request's ``PATH_INFO`` value, and the latter contains the +#. If any route matches, the route mapper adds the attributes ``matchdict`` + and ``matched_route`` to the request object. The former contains a + dictionary representing the matched dynamic elements of the request's + ``PATH_INFO`` value, and the latter contains the :class:`~pyramid.interfaces.IRoute` object representing the route which matched. - - A :class:`~pyramid.events.BeforeTraversal` :term:`event` is sent to any + +#. A :class:`~pyramid.events.BeforeTraversal` :term:`event` is sent to any subscribers. - The root object associated with the route found is also generated: - if the :term:`route configuration` which matched has an associated - ``factory`` argument, this factory is used to generate the root object, - otherwise a default :term:`root factory` is used. +#. Continuing, if any route matches, the root object associated with the found + route is generated. If the :term:`route configuration` which matched has an + associated ``factory`` argument, then this factory is used to generate the + root object; otherwise a default :term:`root factory` is used. -#. If a route match was *not* found, and a ``root_factory`` argument was passed + However, if no route matches, and if a ``root_factory`` argument was passed to the :term:`Configurator` constructor, that callable is used to generate - the root object after a :class:`~pyramid.events.BeforeTraversal` - :term:`event` is sent to any subscribers. If the ``root_factory`` argument - passed to the Configurator constructor was ``None``, a default root factory - is used to generate a root object. + the root object. If the ``root_factory`` argument passed to the + Configurator constructor was ``None``, a default root factory is used to + generate a root object. #. The :app:`Pyramid` router calls a "traverser" function with the root object and the request. The traverser function attempts to traverse the root -- cgit v1.2.3 From 17905a39040b8a2f4b57341909eef9d0fac218f5 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Tue, 12 Apr 2016 18:05:30 -0600 Subject: Add CHANGES for BeforeTraversal --- CHANGES.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index fd8c636a0..488c38c7b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,11 @@ unreleased ========== +- A new event and interface (BeforeTraversal) has been introduced that will + notify listeners before traversal starts in the router. See + https://github.com/Pylons/pyramid/pull/2469 and + https://github.com/Pylons/pyramid/pull/1876 + - Python 2.6 is no longer supported by Pyramid. See https://github.com/Pylons/pyramid/issues/2368 -- cgit v1.2.3