Blinker provides fast & simple object-to-object and broadcast signaling for Python objects.
The core of Blinker is quite small but provides powerful features:
- a global registry of named signals
- anonymous signals
- custom name registries
- permanently or temporarily connected receivers
- automatically disconnected receivers via weak referencing
- sending arbitrary data payloads
- collecting return values from signal receivers
- thread safety
Blinker was written by Jason Kirtand and is provided under the MIT License. The library supports Python 2.4 or later; Python 3.0 or later; or Jython 2.5 or later; or PyPy 1.6 or later.
Named signals are created with signal():
>>> from blinker import signal
>>> initialized = signal('initialized')
>>> initialized is signal('initialized')
True
Every call to signal('name') returns the same signal object, allowing unconnected parts of code (different modules, plugins, anything) to all use the same signal without requiring any code sharing or special imports.
Signal.connect() registers a function to be invoked each time the signal is emitted. Connected functions are always passed the object that caused the signal to be emitted.
>>> def subscriber(sender):
... print("Got a signal sent by %r" % sender)
...
>>> ready = signal('ready')
>>> ready.connect(subscriber)
<function subscriber at 0x...>
Code producing events of interest can Signal.send() notifications to all connected receivers.
Below, a simple Processor class emits a ready signal when it’s about to process something, and complete when it is done. It passes self to the send() method, signifying that that particular instance was responsible for emitting the signal.
>>> class Processor:
... def __init__(self, name):
... self.name = name
...
... def go(self):
... ready = signal('ready')
... ready.send(self)
... print("Processing.")
... complete = signal('complete')
... complete.send(self)
...
... def __repr__(self):
... return '<Processor %s>' % self.name
...
>>> processor_a = Processor('a')
>>> processor_a.go()
Got a signal sent by <Processor a>
Processing.
Notice the complete signal in go()? No receivers have connected to complete yet, and that’s a-ok. Calling send() on a signal with no receivers will result in no notifications being sent, and these no-op sends are optimized to be as inexpensive as possible.
The default connection to a signal invokes the receiver function when any sender emits it. The Signal.connect() function accepts an optional argument to restrict the subscription to one specific sending object:
>>> def b_subscriber(sender):
... print("Caught signal from processor_b.")
... assert sender.name == 'b'
...
>>> processor_b = Processor('b')
>>> ready.connect(b_subscriber, sender=processor_b)
<function b_subscriber at 0x...>
This function has been subscribed to ready but only when sent by processor_b:
>>> processor_a.go()
Got a signal sent by <Processor a>
Processing.
>>> processor_b.go()
Got a signal sent by <Processor b>
Caught signal from processor_b.
Processing.
Additional keyword arguments can be passed to send(). These will in turn be passed to the connected functions:
>>> send_data = signal('send-data')
>>> @send_data.connect
... def receive_data(sender, **kw):
... print("Caught signal from %r, data %r" % (sender, kw))
... return 'received!'
...
>>> result = send_data.send('anonymous', abc=123)
Caught signal from 'anonymous', data {'abc': 123}
The return value of send() collects the return values of each connected function as a list of (receiver function, return value) pairs:
>>> result
[(<function receive_data at 0x...>, 'received!')]
Signals need not be named. The Signal constructor creates a unique signal each time it is invoked. For example, an alternative implementation of the Processor from above might provide the processing signals as class attributes:
>>> from blinker import Signal
>>> class AltProcessor:
... on_ready = Signal()
... on_complete = Signal()
...
... def __init__(self, name):
... self.name = name
...
... def go(self):
... self.on_ready.send(self)
... print("Alternate processing.")
... self.on_complete.send(self)
...
... def __repr__(self):
... return '<AltProcessor %s>' % self.name
...
You may have noticed the return value of connect() in the console output in the sections above. This allows connect to be used as a decorator on functions:
>>> apc = AltProcessor('c')
>>> @apc.on_complete.connect
... def completed(sender):
... print "AltProcessor %s completed!" % sender.name
...
>>> apc.go()
Alternate processing.
AltProcessor c completed!
While convenient, this form unfortunately does not allow the sender or weak arguments to be customized for the connected function. For this, connect_via() can be used:
>>> dice_roll = signal('dice_roll')
>>> @dice_roll.connect_via(1)
... @dice_roll.connect_via(3)
... @dice_roll.connect_via(5)
... def odd_subscriber(sender):
... print("Observed dice roll %r." % sender)
...
>>> result = dice_roll.send(3)
Observed dice roll 3.
Signals are optimized to send very quickly, whether receivers are connected or not. If the keyword data to be sent with a signal is expensive to compute, it can be more efficient to check to see if any receivers are connected first by testing the receivers property:
>>> bool(signal('ready').receivers)
True
>>> bool(signal('complete').receivers)
False
>>> bool(AltProcessor.on_complete.receivers)
True
Checking for a receiver listening for a particular sender is also possible:
>>> signal('ready').has_receivers_for(processor_a)
True
Both named and anonymous signals can be passed a doc argument at construction to set the pydoc help text for the signal. This documentation will be picked up by most documentation generators (such as sphinx) and is nice for documenting any additional data parameters that will be sent down with the signal.
See the documentation of the receiver_connected built-in signal for an example.
All public API members can (and should) be imported from blinker:
from blinker import ANY, signal
Token for “any sender”.
Sent by a Signal after a receiver connects.
Argument : | the Signal that was connected to |
---|---|
Parameters: |
|
Deprecated since version 1.2.
As of 1.2, individual signals have their own private receiver_connected and receiver_disconnected signals with a slightly simplified call signature. This global signal is planned to be removed in 1.6.
A notification emitter.
Parameters: | doc – optional. If provided, will be assigned to the signal’s __doc__ attribute. |
---|
Emitted after each connect().
The signal sender is the signal instance, and the connect() arguments are passed through: receiver, sender, and weak.
New in version 1.2.
Emitted after disconnect().
The sender is the signal instance, and the disconnect() arguments are passed through: receiver and sender.
Note, this signal is emitted only when disconnect() is called explicitly.
The disconnect signal can not be emitted by an automatic disconnect (due to a weakly referenced receiver or sender going out of scope), as the receiver and/or sender instances are no longer available for use at the time this signal would be emitted.
An alternative approach is available by subscribing to receiver_connected and setting up a custom weakref cleanup callback on weak receivers and senders.
New in version 1.2.
A mapping of connected receivers.
The values of this mapping are not meaningful outside of the internal Signal implementation, however the boolean value of the mapping is useful as an extremely efficient check to see if any receivers are connected to the signal.
Connect receiver to signal events sent by sender.
Parameters: |
|
---|
Connect the decorated function as a receiver for sender.
Parameters: |
|
---|
New in version 1.1.
Execute a block with the signal temporarily connected to receiver.
Parameters: |
|
---|
This is a context manager for use in the with statement. It can be useful in unit tests. receiver is connected to the signal for the duration of the with block, and will be disconnected automatically when exiting the block:
with on_ready.connected_to(receiver):
# do stuff
on_ready.send(123)
New in version 1.1.
Disconnect receiver from this signal’s events.
Parameters: |
---|
True if there is probably a receiver for sender.
Performs an optimistic check only. Does not guarantee that all weakly referenced receivers are still alive. See receivers_for() for a stronger search.
Iterate all live receivers listening for sender.
Emit this signal on behalf of sender, passing on **kwargs.
Returns a list of 2-tuples, pairing receivers with their return value. The ordering of receiver notification is undefined.
Parameters: |
|
---|
An alias for connected_to().
Parameters: |
|
---|
New in version 0.9.
Changed in version 1.1: Renamed to connected_to(). temporarily_connected_to was deprecated in 1.2 and will be removed in a subsequent version.
Return the NamedSignal name, creating it if required.
Repeated calls to this function will return the same signal object. Signals are created in a global Namespace.
Bases: blinker.base.Signal
A named generic notification emitter.
The name of this signal.
Bases: dict
A mapping of signal names to signals.
Return the NamedSignal name, creating it if required.
Repeated calls to this function will return the same signal object.
Bases: weakref.WeakValueDictionary
A weak mapping of signal names to signals.
Automatically cleans up unused Signals when the last reference goes out of scope. This namespace implementation exists for a measure of legacy compatibility with Blinker <= 1.2, and may be dropped in the future.
New in version 1.3.
Return the NamedSignal name, creating it if required.
Repeated calls to this function will return the same signal object.
Released July 23, 2015
Released July 3, 2013
Released October 26, 2011
Released July 21, 2010
Released February 26, 2010
Released February 14, 2010