summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2008-07-31 06:17:31 +0000
committerChris McDonough <chrism@agendaless.com>2008-07-31 06:17:31 +0000
commita0423aedb4abaf12b4008c0b229ec0ecad4ddfd8 (patch)
tree6193a7bce3bdb05c6c9e81921d592b769cd071b1
parent54302499c5ff609b4c615a7ae1d341e7d652dae3 (diff)
downloadpyramid-a0423aedb4abaf12b4008c0b229ec0ecad4ddfd8.tar.gz
pyramid-a0423aedb4abaf12b4008c0b229ec0ecad4ddfd8.tar.bz2
pyramid-a0423aedb4abaf12b4008c0b229ec0ecad4ddfd8.zip
- Add event sends for INewRequest and INewResponse. See the
events.rst chapter in the documentation's ``api`` directory.
-rw-r--r--CHANGES.txt5
-rw-r--r--docs/api/events.rst49
-rw-r--r--docs/conf.py4
-rw-r--r--docs/index.rst1
-rw-r--r--repoze/bfg/events.py23
-rw-r--r--repoze/bfg/interfaces.py9
-rw-r--r--repoze/bfg/router.py6
-rw-r--r--repoze/bfg/sampleapp/configure.zcml12
-rw-r--r--repoze/bfg/sampleapp/listeners.py6
-rw-r--r--repoze/bfg/tests/test_events.py60
-rw-r--r--repoze/bfg/tests/test_router.py33
-rw-r--r--setup.py2
12 files changed, 208 insertions, 2 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 2ae332b0c..111be6294 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,8 @@
+0.2.6
+
+ - Add event sends for INewRequest and INewResponse. See the
+ events.rst chapter in the documentation's ``api`` directory.
+
0.2.5
- Add ``model_url`` API.
diff --git a/docs/api/events.rst b/docs/api/events.rst
new file mode 100644
index 000000000..25bb9841b
--- /dev/null
+++ b/docs/api/events.rst
@@ -0,0 +1,49 @@
+.. _events_module:
+
+:mod:`repoze.bfg.events`
+--------------------------
+
+.. automodule:: repoze.bfg.events
+
+ .. autoclass:: NewRequest
+
+ .. autoclass:: NewResponse
+
+You can write *listeners* for these event types and subsequently
+register the listeners to be called when the events occur. For
+example, if you create event listener functions in a ``listeners.py``
+file in your application like so:
+
+.. code-block:: python
+ :linenos:
+
+ def handle_new_request(event):
+ print 'request', event.request
+
+ def handle_new_response(event):
+ print 'response', event.response
+
+You may configure these functions to be called at the appropriate
+times by adding the following to your application's ``configure.zcml``
+file:
+
+.. code-block:: xml
+ :linenos:
+
+ <subscriber
+ for="repoze.bfg.interfaces.INewRequest"
+ handler=".listeners.handle_new_request"
+ />
+
+ <subscriber
+ for="repoze.bfg.interfaces.INewResponse"
+ handler=".listeners.handle_new_response"
+ />
+
+This causes the functions as to be registered as event listeners
+within the :term:`application registry` . Under this configuration,
+when the application is run, every new request and every response will
+be printed to the console.
+
+The return value of a listener function is ignored.
+
diff --git a/docs/conf.py b/docs/conf.py
index 8d19cf4db..2a0d53381 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -51,9 +51,9 @@ copyright = '2008, Agendaless Consulting'
# other places throughout the built documents.
#
# The short X.Y version.
-version = '0.2.5'
+version = '0.2.6'
# The full version, including alpha/beta/rc tags.
-release = '0.2.5'
+release = '0.2.6'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
diff --git a/docs/index.rst b/docs/index.rst
index 5825a6129..63f8b96e4 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -50,6 +50,7 @@ Per-module :mod:`repoze.bfg` API documentation.
.. toctree::
:maxdepth: 2
+ api/events
api/push
api/router
api/security
diff --git a/repoze/bfg/events.py b/repoze/bfg/events.py
new file mode 100644
index 000000000..3253f1380
--- /dev/null
+++ b/repoze/bfg/events.py
@@ -0,0 +1,23 @@
+from zope.interface import implements
+
+from repoze.bfg.interfaces import INewRequest
+from repoze.bfg.interfaces import INewResponse
+
+class NewRequest(object):
+ """ An instance of this class is emitted as an event whenever
+ repoze.bfg begins to process a new request. The instance has an
+ attribute, ``request``, which is the request object. This class
+ implements the ``repoze.bfg.interfaces.INewRequest`` interface."""
+ implements(INewRequest)
+ def __init__(self, request):
+ self.request = request
+
+class NewResponse(object):
+ """ An instance of this class is emitted as an event whenever any
+ repoze.bfg view returns a response.. The instance has an
+ attribute, ``response``, which is the response object returned by
+ the view. This class implements the
+ ``repoze.bfg.interfaces.INewResponse`` interface."""
+ implements(INewResponse)
+ def __init__(self, response):
+ self.response = response
diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py
index 489fe0559..4b2b626e6 100644
--- a/repoze/bfg/interfaces.py
+++ b/repoze/bfg/interfaces.py
@@ -69,3 +69,12 @@ class IViewPermissionFactory(Interface):
class IURLDispatchModel(Interface):
""" A model that is created as a result of URL dispatching """
+class INewRequest(Interface):
+ """ An event type that is emitted whenever repoze.bfg begins to
+ process a new request """
+ request = Attribute('The request object')
+
+class INewResponse(Interface):
+ """ An event type that is emitted whenever any repoze.bfg view
+ returns a response."""
+ response = Attribute('The response object')
diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py
index d889c15a0..0333c8a99 100644
--- a/repoze/bfg/router.py
+++ b/repoze/bfg/router.py
@@ -1,12 +1,15 @@
from zope.component import getMultiAdapter
from zope.component import queryMultiAdapter
from zope.component import queryUtility
+from zope.component.event import dispatch
from zope.interface import directlyProvides
from webob import Request
from webob.exc import HTTPNotFound
from webob.exc import HTTPUnauthorized
+from repoze.bfg.events import NewRequest
+from repoze.bfg.events import NewResponse
from repoze.bfg.interfaces import ITraverserFactory
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IViewPermission
@@ -28,6 +31,7 @@ class Router:
registry_manager.set(self.registry)
request = Request(environ)
directlyProvides(request, IRequest)
+ dispatch(NewRequest(request))
root = self.root_policy(environ)
traverser = getMultiAdapter((root, request), ITraverserFactory)
context, name, subpath = traverser(environ)
@@ -55,6 +59,8 @@ class Router:
if not isResponse(response):
raise ValueError('response was not IResponse: %s' % response)
+ dispatch(NewResponse(response))
+
start_response(response.status, response.headerlist)
return response.app_iter
diff --git a/repoze/bfg/sampleapp/configure.zcml b/repoze/bfg/sampleapp/configure.zcml
index 121566520..a5f27595e 100644
--- a/repoze/bfg/sampleapp/configure.zcml
+++ b/repoze/bfg/sampleapp/configure.zcml
@@ -39,4 +39,16 @@
permission="manage"
/>
+ <!-- event listener for a new request -->
+ <subscriber
+ for="repoze.bfg.interfaces.INewRequest"
+ handler=".listeners.handle_new_request"
+ />
+
+ <!-- event listener for a new response -->
+ <subscriber
+ for="repoze.bfg.interfaces.INewResponse"
+ handler=".listeners.handle_new_response"
+ />
+
</configure>
diff --git a/repoze/bfg/sampleapp/listeners.py b/repoze/bfg/sampleapp/listeners.py
new file mode 100644
index 000000000..7c80d314f
--- /dev/null
+++ b/repoze/bfg/sampleapp/listeners.py
@@ -0,0 +1,6 @@
+def handle_new_request(event):
+ assert(hasattr(event, 'request'))
+
+def handle_new_response(event):
+ assert(hasattr(event, 'response'))
+
diff --git a/repoze/bfg/tests/test_events.py b/repoze/bfg/tests/test_events.py
new file mode 100644
index 000000000..770f1afeb
--- /dev/null
+++ b/repoze/bfg/tests/test_events.py
@@ -0,0 +1,60 @@
+import unittest
+
+class NewRequestEventTests(unittest.TestCase):
+ def _getTargetClass(self):
+ from repoze.bfg.events import NewRequest
+ return NewRequest
+
+ def _makeOne(self, request):
+ return self._getTargetClass()(request)
+
+ def test_class_implements(self):
+ from repoze.bfg.interfaces import INewRequest
+ from zope.interface.verify import verifyClass
+ klass = self._getTargetClass()
+ verifyClass(INewRequest, klass)
+
+ def test_instance_implements(self):
+ from repoze.bfg.interfaces import INewRequest
+ from zope.interface.verify import verifyObject
+ request = DummyRequest()
+ inst = self._makeOne(request)
+ verifyObject(INewRequest, inst)
+
+ def test_ctor(self):
+ request = DummyRequest()
+ inst = self._makeOne(request)
+ self.assertEqual(inst.request, request)
+
+class NewResponseEventTests(unittest.TestCase):
+ def _getTargetClass(self):
+ from repoze.bfg.events import NewResponse
+ return NewResponse
+
+ def _makeOne(self, response):
+ return self._getTargetClass()(response)
+
+ def test_class_implements(self):
+ from repoze.bfg.interfaces import INewResponse
+ from zope.interface.verify import verifyClass
+ klass = self._getTargetClass()
+ verifyClass(INewResponse, klass)
+
+ def test_instance_implements(self):
+ from repoze.bfg.interfaces import INewResponse
+ from zope.interface.verify import verifyObject
+ response = DummyResponse()
+ inst = self._makeOne(response)
+ verifyObject(INewResponse, inst)
+
+ def test_ctor(self):
+ response = DummyResponse()
+ inst = self._makeOne(response)
+ self.assertEqual(inst.response, response)
+
+class DummyRequest:
+ pass
+
+class DummyResponse:
+ pass
+
diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py
index 4b1e914b7..d2907ef8a 100644
--- a/repoze/bfg/tests/test_router.py
+++ b/repoze/bfg/tests/test_router.py
@@ -33,6 +33,11 @@ class RouterTests(unittest.TestCase, PlacelessSetup):
from repoze.bfg.interfaces import ISecurityPolicy
gsm.registerUtility(secpol, ISecurityPolicy)
+ def _registerEventListener(self, listener, iface):
+ import zope.component
+ gsm = zope.component.getGlobalSiteManager()
+ gsm.registerHandler(listener, (iface,))
+
def _getTargetClass(self):
from repoze.bfg.router import Router
return Router
@@ -232,6 +237,34 @@ class RouterTests(unittest.TestCase, PlacelessSetup):
self.failUnless('permission' in result[0])
self.assertEqual(permissionfactory.checked_with, secpol)
+ def test_call_eventsends(self):
+ rootpolicy = make_rootpolicy(None)
+ context = DummyContext()
+ traversalfactory = make_traversal_factory(context, '', [])
+ response = DummyResponse()
+ response.app_iter = ['Hello world']
+ view = make_view(response)
+ environ = self._makeEnviron()
+ self._registerTraverserFactory(traversalfactory, '', None, None)
+ self._registerView(view, '', None, None)
+ from repoze.bfg.interfaces import INewRequest
+ from repoze.bfg.interfaces import INewResponse
+ request_events = []
+ response_events = []
+ def handle_request(event):
+ request_events.append(event)
+ def handle_response(event):
+ response_events.append(event)
+ self._registerEventListener(handle_request, INewRequest)
+ self._registerEventListener(handle_response, INewResponse)
+ router = self._makeOne(rootpolicy, None)
+ 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(response_events), 1)
+ self.assertEqual(response_events[0].response, response)
+
class MakeAppTests(unittest.TestCase, PlacelessSetup):
def setUp(self):
PlacelessSetup.setUp(self)
diff --git a/setup.py b/setup.py
index d1d324d68..ed92918c1 100644
--- a/setup.py
+++ b/setup.py
@@ -52,6 +52,7 @@ setup(name='repoze.bfg',
'zope.component',
'zope.testing',
'zope.hookable',
+ 'zope.event',
'WebOb',
'Paste',
'z3c.pt',
@@ -64,6 +65,7 @@ setup(name='repoze.bfg',
'zope.component',
'zope.testing',
'zope.hookable',
+ 'zope.event',
'WebOb',
'Paste',
'z3c.pt',