summaryrefslogtreecommitdiff
path: root/repoze
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2010-07-28 03:00:58 +0000
committerChris McDonough <chrism@agendaless.com>2010-07-28 03:00:58 +0000
commitf077653f208f6f7c89c78c87c2abb0ea7031dbc0 (patch)
treeae5be691f2f19ccd98afa513738a2d424ecfe4e2 /repoze
parentbe6f3b9ae06b31920d90744b20ccb7a8b4d9a278 (diff)
downloadpyramid-f077653f208f6f7c89c78c87c2abb0ea7031dbc0.tar.gz
pyramid-f077653f208f6f7c89c78c87c2abb0ea7031dbc0.tar.bz2
pyramid-f077653f208f6f7c89c78c87c2abb0ea7031dbc0.zip
- A ``repoze.bfg.events.subscriber`` decorator was added. This
decorator decorates module-scope functions, which are then treated as event listeners after a scan() is performed. See the Events narrative documentation chapter and the ``repoze.bfg.events`` module documentation for more information.
Diffstat (limited to 'repoze')
-rw-r--r--repoze/bfg/decorator.py1
-rw-r--r--repoze/bfg/events.py64
-rw-r--r--repoze/bfg/tests/test_events.py54
3 files changed, 119 insertions, 0 deletions
diff --git a/repoze/bfg/decorator.py b/repoze/bfg/decorator.py
index 69c1e65e2..d30a06658 100644
--- a/repoze/bfg/decorator.py
+++ b/repoze/bfg/decorator.py
@@ -13,3 +13,4 @@ class reify(object):
val = self.wrapped(inst)
setattr(inst, self.wrapped.__name__, val)
return val
+
diff --git a/repoze/bfg/events.py b/repoze/bfg/events.py
index bdccfba9c..22822eeeb 100644
--- a/repoze/bfg/events.py
+++ b/repoze/bfg/events.py
@@ -1,3 +1,5 @@
+import venusian
+
from zope.interface import implements
from repoze.bfg.interfaces import IAfterTraversal
@@ -5,6 +7,68 @@ from repoze.bfg.interfaces import INewRequest
from repoze.bfg.interfaces import INewResponse
from repoze.bfg.interfaces import IWSGIApplicationCreatedEvent
+class subscriber(object):
+ """ Decorator activated via a :term:`scan` which treats the
+ function being decorated as an event subscriber for the set of
+ interfaces passed as ``*ifaces`` to the decorator constructor.
+
+ For example:
+
+ .. code-block:: python
+
+ from repoze.bfg.interfaces import INewRequest
+ from repoze.bfg.events import subscriber
+
+ @subscriber(INewRequest)
+ def mysubscriber(event):
+ event.request.foo = 1
+
+ More than one event type can be passed as a construtor argument:
+
+ .. code-block:: python
+
+ from repoze.bfg.interfaces import INewRequest
+ from repoze.bfg.events import subscriber
+
+ @subscriber(INewRequest, INewResponse)
+ def mysubscriber(event):
+ print event
+
+ When the ``subscriber`` decorator is used without passing an arguments,
+ the function it decorates is called for every event sent:
+
+ .. code-block:: python
+
+ from repoze.bfg.interfaces import INewRequest
+ from repoze.bfg.events import subscriber
+
+ @subscriber()
+ def mysubscriber(event):
+ print event
+
+ This method will have no effect until a :term:`scan` is performed
+ against the package or module which contains it, ala:
+
+ .. code-block:: python
+
+ from repoze.bfg.configuration import Configurator
+ config = Configurator()
+ config.scan('somepackage_containing_subscribers')
+
+ """
+ venusian = venusian # for unit testing
+
+ def __init__(self, *ifaces):
+ self.ifaces = ifaces
+
+ def register(self, scanner, name, wrapped):
+ config = scanner.config
+ config.add_subscriber(wrapped, self.ifaces)
+
+ def __call__(self, wrapped):
+ self.venusian.attach(wrapped, self.register, category='bfg')
+ return wrapped
+
class NewRequest(object):
""" An instance of this class is emitted as an :term:`event`
whenever :mod:`repoze.bfg` begins to process a new request. The
diff --git a/repoze/bfg/tests/test_events.py b/repoze/bfg/tests/test_events.py
index f1cbfff5b..c258c4ad2 100644
--- a/repoze/bfg/tests/test_events.py
+++ b/repoze/bfg/tests/test_events.py
@@ -92,6 +92,60 @@ class AfterTraversalEventTests(unittest.TestCase):
inst = self._makeOne(request)
self.assertEqual(inst.request, request)
+class TestSubscriber(unittest.TestCase):
+ def setUp(self):
+ registry = DummyRegistry()
+ from repoze.bfg.configuration import Configurator
+ self.config = Configurator(registry)
+ self.config.begin()
+
+ def tearDown(self):
+ self.config.end()
+
+ def _makeOne(self, *ifaces):
+ from repoze.bfg.events import subscriber
+ return subscriber(*ifaces)
+
+ def test_register(self):
+ from zope.interface import Interface
+ class IFoo(Interface): pass
+ class IBar(Interface): pass
+ dec = self._makeOne(IFoo, IBar)
+ def foo(): pass
+ config = DummyConfigurator()
+ scanner = Dummy()
+ scanner.config = config
+ dec.register(scanner, None, foo)
+ self.assertEqual(config.subscribed, [(foo, (IFoo, IBar))])
+
+ def test___call__(self):
+ dec = self._makeOne()
+ dummy_venusian = DummyVenusian()
+ dec.venusian = dummy_venusian
+ def foo(): pass
+ dec(foo)
+ self.assertEqual(dummy_venusian.attached, [(foo, dec.register, 'bfg')])
+
+class DummyConfigurator(object):
+ def __init__(self):
+ self.subscribed = []
+
+ def add_subscriber(self, wrapped, ifaces):
+ self.subscribed.append((wrapped, ifaces))
+
+class DummyRegistry(object):
+ pass
+
+class DummyVenusian(object):
+ def __init__(self):
+ self.attached = []
+
+ def attach(self, wrapped, fn, category=None):
+ self.attached.append((wrapped, fn, category))
+
+class Dummy:
+ pass
+
class DummyRequest:
pass