summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjason kirtland <jek@discorporate.us>2016-08-22 14:20:42 -0700
committerjason kirtland <jek@discorporate.us>2016-08-22 14:20:42 -0700
commit7303d4bf1ab8a339a194d0d1935770bef43c3cca (patch)
tree1ecc59ac406e0f2de0989d7af174fdf031c42a6c
parent638cbae12f60965053f53b61191d2541fbf605b8 (diff)
downloadblinker-7303d4bf1ab8a339a194d0d1935770bef43c3cca.tar.gz
Added Signal.receiver_adapter, an just-in-time receiver decorator applied during :meth:`send`.
-rw-r--r--blinker/base.py25
-rw-r--r--tests/test_signals.py22
2 files changed, 44 insertions, 3 deletions
diff --git a/blinker/base.py b/blinker/base.py
index cc5880e..1b5b7dc 100644
--- a/blinker/base.py
+++ b/blinker/base.py
@@ -26,6 +26,8 @@ ANY = symbol('ANY')
ANY.__doc__ = 'Token for "any sender".'
ANY_ID = 0
+Unspecified = symbol('Unspecified')
+
class Signal(object):
"""A notification emitter."""
@@ -34,6 +36,11 @@ class Signal(object):
#: without an additional import.
ANY = ANY
+ #: If this callable is set, will be invoked with `(receiver, sender,
+ #: kwargs)` for each receiver during :meth:`send` instead of invoking
+ #: reciever functions directly.
+ receiver_adapter = None
+
@lazy_property
def receiver_connected(self):
"""Emitted after each :meth:`connect`.
@@ -70,14 +77,21 @@ class Signal(object):
"""
return Signal(doc="Emitted after a receiver disconnects.")
- def __init__(self, doc=None):
+ def __init__(self, doc=None, receiver_adapter=Unspecified):
"""
:param doc: optional. If provided, will be assigned to the signal's
__doc__ attribute.
+ :param receiver_adapter: optional callable. If provided, will be
+ invoked with `receiver, sender, kwargs` for each receiver during
+ :meth:`send` instead of invoking receivers functions directly.
+
+ ... versionadded:: 1.5
"""
if doc:
self.__doc__ = doc
+ if receiver_adapter is not Unspecified:
+ self.receiver_adapter = receiver_adapter
#: A mapping of connected receivers.
#:
#: The values of this mapping are not meaningful outside of the
@@ -263,8 +277,13 @@ class Signal(object):
if not self.receivers:
return []
else:
- return [(receiver, receiver(sender, **kwargs))
- for receiver in self.receivers_for(sender)]
+ if self.receiver_adapter is not None:
+ adapt = self.receiver_adapter
+ return [(receiver, adapt(receiver, sender, kwargs))
+ for receiver in self.receivers_for(sender)]
+ else:
+ return [(receiver, receiver(sender, **kwargs))
+ for receiver in self.receivers_for(sender)]
def has_receivers_for(self, sender):
"""True if there is probably a receiver for *sender*.
diff --git a/tests/test_signals.py b/tests/test_signals.py
index a1172ed..2340bac 100644
--- a/tests/test_signals.py
+++ b/tests/test_signals.py
@@ -417,6 +417,28 @@ def test_decorated_receiver():
assert sig.receivers
+def test_adapted_receiver():
+ sentinel = []
+
+ def receiver(sender, **kw):
+ raise AssertionError
+
+ def adapter(fn, sender, kw):
+ assert fn is receiver
+ sentinel.append(kw)
+
+ sig_constructor = blinker.Signal(None, adapter)
+ sig_adhoc = blinker.Signal()
+ sig_adhoc.receiver_adapter = adapter
+
+ for sig in (sig_constructor, sig_adhoc):
+ sig.connect(receiver)
+ del sentinel[:]
+ sig.send()
+ assert sentinel == [{}]
+ sig.disconnect(receiver)
+
+
def test_no_double_send():
sentinel = []
def received(sender):