summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-11-02 12:23:26 +0000
committerChris McDonough <chrism@agendaless.com>2009-11-02 12:23:26 +0000
commit49304cecdf2c51888ee4ff42ec6496207186ad9b (patch)
tree82172ae270dfd0d5d972f65d4669c93219c9f469
parent32d95886503540052d9b7075c9ce3326b042bb08 (diff)
downloadpyramid-49304cecdf2c51888ee4ff42ec6496207186ad9b.tar.gz
pyramid-49304cecdf2c51888ee4ff42ec6496207186ad9b.tar.bz2
pyramid-49304cecdf2c51888ee4ff42ec6496207186ad9b.zip
- Add a new event type: ``repoze.bfg.events.AfterTraversal``. Events
of this type will be sent after traversal is completed, but before any view code is invoked. Like ``repoze.bfg.events.NewRequest``, This event will have a single attribute: ``request`` representing the current request. Unlike the request attribute of ``repoze.bfg.events.NewRequest`` however, during an AfterTraversal event, the request object will possess attributes set by the traverser, most notably ``context``, which will be the context used when a view is found and invoked. The interface ``repoze.bfg.events.IAfterTraversal`` can be used to subscribe to the event. For example:: <subscriber for="repoze.bfg.interfaces.IAfterTraversal" handler="my.app.handle_after_traverse"/> Like any framework event, a subscriber function should expect one parameter: ``event``.
-rw-r--r--CHANGES.txt21
-rw-r--r--docs/whatsnew-1.1.rst17
-rw-r--r--repoze/bfg/events.py19
-rw-r--r--repoze/bfg/interfaces.py5
-rw-r--r--repoze/bfg/router.py7
-rw-r--r--repoze/bfg/tests/test_events.py25
-rw-r--r--repoze/bfg/tests/test_router.py8
7 files changed, 95 insertions, 7 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index edd3170ff..b1d84c4d1 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -12,6 +12,27 @@ Documentation
- "What's New in ``repoze.bfg`` 1.1" document added to narrative
documentation.
+Features
+--------
+
+- Add a new event type: ``repoze.bfg.events.AfterTraversal``. Events
+ of this type will be sent after traversal is completed, but before
+ any view code is invoked. Like ``repoze.bfg.events.NewRequest``,
+ This event will have a single attribute: ``request`` representing
+ the current request. Unlike the request attribute of
+ ``repoze.bfg.events.NewRequest`` however, during an AfterTraversal
+ event, the request object will possess attributes set by the
+ traverser, most notably ``context``, which will be the context used
+ when a view is found and invoked. The interface
+ ``repoze.bfg.events.IAfterTraversal`` can be used to subscribe to
+ the event. For example::
+
+ <subscriber for="repoze.bfg.interfaces.IAfterTraversal"
+ handler="my.app.handle_after_traverse"/>
+
+ Like any framework event, a subscriber function should expect one
+ parameter: ``event``.
+
Dependencies
------------
diff --git a/docs/whatsnew-1.1.rst b/docs/whatsnew-1.1.rst
index e84d38a13..655e73882 100644
--- a/docs/whatsnew-1.1.rst
+++ b/docs/whatsnew-1.1.rst
@@ -595,6 +595,23 @@ Minor Miscellaneous Feature Additions
registered forbidden view. When the router catches a ``NotFound``,
it returns the registered notfound view.
+- Add a new event type: ``repoze.bfg.events.AfterTraversal``. Events
+ of this type will be sent after traversal is completed, but before
+ any view code is invoked. Like ``repoze.bfg.events.NewRequest``,
+ This event will have a single attribute: ``request`` representing
+ the current request. Unlike the request attribute of
+ ``repoze.bfg.events.NewRequest`` however, during an AfterTraversal
+ event, the request object will possess attributes set by the
+ traverser, most notably ``context``, which will be the context used
+ when a view is found and invoked. The interface
+ ``repoze.bfg.events.IAfterTraversal`` can be used to subscribe to
+ the event. For example::
+
+ <subscriber for="repoze.bfg.interfaces.IAfterTraversal"
+ handler="my.app.handle_after_traverse"/>
+
+ Like any framework event, a subscriber function should expect one
+ parameter: ``event``.
Backwards Incompatibilities
---------------------------
diff --git a/repoze/bfg/events.py b/repoze/bfg/events.py
index 54f60c294..14e47ca3f 100644
--- a/repoze/bfg/events.py
+++ b/repoze/bfg/events.py
@@ -1,5 +1,6 @@
from zope.interface import implements
+from repoze.bfg.interfaces import IAfterTraversal
from repoze.bfg.interfaces import INewRequest
from repoze.bfg.interfaces import INewResponse
from repoze.bfg.interfaces import IWSGIApplicationCreatedEvent
@@ -23,6 +24,20 @@ class NewResponse(object):
def __init__(self, response):
self.response = response
+class AfterTraversal(object):
+ implements(IAfterTraversal)
+ """ An instance of this class is emitted as an event after the
+ repoze.bfg router performs traversal (but before any view code is
+ executed). The instance has an attribute, ``request``, which is
+ the request object returned by the view. Notably, the request
+ object will have an attribute named ``context``, which is the
+ context that will be provided to the view which will eventually be
+ called, as well as other attributes defined by the traverser.
+ This class implements the
+ ``repoze.bfg.interfaces.IAfterTraversal`` interface."""
+ def __init__(self, request):
+ self.request = request
+
class WSGIApplicationCreatedEvent(object):
""" An instance of this class is emitted as an event whenever a
``repoze.bfg`` application starts. The instance has an attribute,
@@ -32,7 +47,5 @@ class WSGIApplicationCreatedEvent(object):
implements(IWSGIApplicationCreatedEvent)
def __init__(self, app):
self.app = app
+ self.object = app
- @property
- def object(self):
- return self.app
diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py
index 53b01bfd6..582e8984d 100644
--- a/repoze/bfg/interfaces.py
+++ b/repoze/bfg/interfaces.py
@@ -120,6 +120,11 @@ class IRouter(Interface):
registry = Attribute(
"""Component architecture registry local to this application.""")
+class IAfterTraversal(Interface):
+ """ An event type that is emitted after repoze.bfg completes
+ traversal but before it calls any view code."""
+ request = Attribute('The request object')
+
class INewRequest(Interface):
""" An event type that is emitted whenever repoze.bfg begins to
process a new request """
diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py
index c3580a8da..ca0f8e449 100644
--- a/repoze/bfg/router.py
+++ b/repoze/bfg/router.py
@@ -14,6 +14,7 @@ from repoze.bfg.interfaces import IView
from repoze.bfg.configuration import make_registry
from repoze.bfg.configuration import DefaultRootFactory
+from repoze.bfg.events import AfterTraversal
from repoze.bfg.events import NewRequest
from repoze.bfg.events import NewResponse
from repoze.bfg.events import WSGIApplicationCreatedEvent
@@ -54,6 +55,7 @@ class Router(object):
iterable.
"""
registry = self.registry
+ has_listeners = registry.has_listeners
logger = self.logger
manager = self.threadlocal_manager
threadlocals = {'registry':registry, 'request':None}
@@ -65,7 +67,7 @@ class Router(object):
threadlocals['request'] = request
attrs = request.__dict__
attrs['registry'] = registry
- registry.has_listeners and registry.notify(NewRequest(request))
+ has_listeners and registry.notify(NewRequest(request))
# view lookup
root = self.root_factory(request)
@@ -79,6 +81,7 @@ class Router(object):
tdict['traversed'], tdict['virtual_root'],
tdict['virtual_root_path'])
attrs.update(tdict)
+ has_listeners and registry.notify(AfterTraversal(request))
provides = map(providedBy, (context, request))
view_callable = registry.adapters.lookup(
provides, IView, name=view_name, default=None)
@@ -111,7 +114,7 @@ class Router(object):
response = self.notfound_view(context, request)
# response handling
- registry.has_listeners and registry.notify(NewResponse(response))
+ has_listeners and registry.notify(NewResponse(response))
try:
headers = response.headerlist
diff --git a/repoze/bfg/tests/test_events.py b/repoze/bfg/tests/test_events.py
index d7ab772f5..f1cbfff5b 100644
--- a/repoze/bfg/tests/test_events.py
+++ b/repoze/bfg/tests/test_events.py
@@ -66,6 +66,31 @@ class WSGIAppEventTests(unittest.TestCase):
from zope.interface.verify import verifyClass
verifyClass(IWSGIApplicationCreatedEvent, WSGIApplicationCreatedEvent)
+class AfterTraversalEventTests(unittest.TestCase):
+ def _getTargetClass(self):
+ from repoze.bfg.events import AfterTraversal
+ return AfterTraversal
+
+ def _makeOne(self, request):
+ return self._getTargetClass()(request)
+
+ def test_class_implements(self):
+ from repoze.bfg.interfaces import IAfterTraversal
+ from zope.interface.verify import verifyClass
+ klass = self._getTargetClass()
+ verifyClass(IAfterTraversal, klass)
+
+ def test_instance_implements(self):
+ from repoze.bfg.interfaces import IAfterTraversal
+ from zope.interface.verify import verifyObject
+ request = DummyRequest()
+ inst = self._makeOne(request)
+ verifyObject(IAfterTraversal, inst)
+
+ def test_ctor(self):
+ request = DummyRequest()
+ inst = self._makeOne(request)
+ self.assertEqual(inst.request, request)
class DummyRequest:
pass
diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py
index 1e5382dde..d5bc8cd66 100644
--- a/repoze/bfg/tests/test_router.py
+++ b/repoze/bfg/tests/test_router.py
@@ -382,6 +382,9 @@ class TestRouter(unittest.TestCase):
self.assertEqual(start_response.headers, [('a', 1), ('b', 2)])
def test_call_eventsends(self):
+ from repoze.bfg.interfaces import INewRequest
+ from repoze.bfg.interfaces import INewResponse
+ from repoze.bfg.interfaces import IAfterTraversal
context = DummyContext()
self._registerTraverserFactory(context)
response = DummyResponse()
@@ -389,15 +392,16 @@ class TestRouter(unittest.TestCase):
view = DummyView(response)
environ = self._makeEnviron()
self._registerView(view, '', None, None)
- from repoze.bfg.interfaces import INewRequest
- from repoze.bfg.interfaces import INewResponse
request_events = self._registerEventListener(INewRequest)
+ aftertraversal_events = self._registerEventListener(IAfterTraversal)
response_events = self._registerEventListener(INewResponse)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
self.assertEqual(len(request_events), 1)
self.assertEqual(request_events[0].request.environ, environ)
+ self.assertEqual(len(aftertraversal_events), 1)
+ self.assertEqual(aftertraversal_events[0].request.environ, environ)
self.assertEqual(len(response_events), 1)
self.assertEqual(response_events[0].response, response)