Blinker Documentation

_images/blinker-named.png

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 3.8 or later; or PyPy3.9 or later.

Decoupling With Named Signals

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.

Subscribing to Signals

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(f"Got a signal sent by {sender!r}")
...
>>> ready = signal('ready')
>>> ready.connect(subscriber)
<function subscriber at 0x...>

Emitting Signals

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 f'<Processor {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.

Subscribing to Specific Senders

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.

Sending and Receiving Data Through Signals

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(f"Caught signal from {sender!r}, data {kw!r}")
...     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!')]

Muting signals

To mute a signal, as may be required when testing, the muted() can be used as a context decorator:

sig = signal('send-data')
with sig.muted():
     ...

Anonymous Signals

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 f'<AltProcessor {self.name}>'
...

connect as a Decorator

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 f"AltProcessor {sender.name} completed!"
...
>>> 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(f"Observed dice roll {sender!r}.")
...
>>> result = dice_roll.send(3)
Observed dice roll 3.

Optimizing Signal Sending

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

Documenting Signals

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.

Async receivers

Receivers can be coroutine functions which can be called and awaited via the send_async() method:

sig = blinker.Signal()

async def receiver():
    ...

sig.connect(receiver)
await sig.send_async()

This however requires that all receivers are awaitable which then precludes the usage of send(). To mix and match the send_async() method takes a _sync_wrapper argument such as:

sig = blinker.Signal()

def receiver():
    ...

sig.connect(receiver)

def wrapper(func):

    async def inner(*args, **kwargs):
        func(*args, **kwargs)

    return inner

await sig.send_async(_sync_wrapper=wrapper)

The equivalent usage for send() is via the _async_wrapper argument. This usage is will depend on your event loop, and in the simple case whereby you aren’t running within an event loop the following example can be used:

sig = blinker.Signal()

async def receiver():
    ...

sig.connect(receiver)

def wrapper(func):

    def inner(*args, **kwargs):
        asyncio.run(func(*args, **kwargs))

    return inner

await sig.send(_async_wrapper=wrapper)

Call receivers in order of registration

It can be advantageous to call a signal’s receivers in the order they were registered. To achieve this the storage class for receivers should be changed from an (unordered) set to an ordered set,

from blinker import Signal
from ordered_set import OrderedSet

Signal.set_class = OrderedSet

Please note that ordered_set is a PyPI package and is not installed with blinker.

API Documentation

All public API members can (and should) be imported from blinker:

from blinker import ANY, signal

Basic Signals

blinker.ANY

Symbol for “any sender”.

class blinker.Signal(doc=None)

A notification emitter.

Parameters:

doc (str | None) – The docstring for the signal.

set_class

alias of set

connect(receiver, sender=ANY, weak=True)

Connect receiver to be called when the signal is sent by sender.

Parameters:
  • receiver (F) – The callable to call when send() is called with the given sender, passing sender as a positional argument along with any extra keyword arguments.

  • sender (t.Any) – Any object or ANY. receiver will only be called when send() is called with this sender. If ANY, the receiver will be called for any sender. A receiver may be connected to multiple senders by calling connect() multiple times.

  • weak (bool) – Track the receiver with a weakref. The receiver will be automatically disconnected when it is garbage collected. When connecting a receiver defined within a function, set to False, otherwise it will be disconnected when the function scope ends.

Return type:

F

connect_via(sender, weak=False)

Connect the decorated function to be called when the signal is sent by sender.

The decorated function will be called when send() is called with the given sender, passing sender as a positional argument along with any extra keyword arguments.

Parameters:
  • sender (t.Any) – Any object or ANY. receiver will only be called when send() is called with this sender. If ANY, the receiver will be called for any sender. A receiver may be connected to multiple senders by calling connect() multiple times.

  • weak (bool) – Track the receiver with a weakref. The receiver will be automatically disconnected when it is garbage collected. When connecting a receiver defined within a function, set to False, otherwise it will be disconnected when the function scope ends.=

Return type:

c.Callable[[F], F]

Changelog

Added in version 1.1.

connected_to(receiver, sender=ANY)

A context manager that temporarily connects receiver to the signal while a with block executes. When the block exits, the receiver is disconnected. Useful for tests.

Parameters:
  • receiver (Callable[[...], Any]) – The callable to call when send() is called with the given sender, passing sender as a positional argument along with any extra keyword arguments.

  • sender (Any) – Any object or ANY. receiver will only be called when send() is called with this sender. If ANY, the receiver will be called for any sender.

Return type:

Generator[None, None, None]

Changelog

Added in version 1.1.

disconnect(receiver, sender=ANY)

Disconnect receiver from being called when the signal is sent by sender.

Parameters:
  • receiver (Callable[[...], Any]) – A connected receiver callable.

  • sender (Any) – Disconnect from only this sender. By default, disconnect from all senders.

Return type:

None

has_receivers_for(sender)

Check if there is at least one receiver that will be called with the given sender. A receiver connected to ANY will always be called, regardless of sender. Does not check if weakly referenced receivers are still live. See receivers_for() for a stronger search.

Parameters:

sender (Any) – Check for receivers connected to this sender, in addition to those connected to ANY.

Return type:

bool

muted()

A context manager that temporarily disables the signal. No receivers will be called if the signal is sent, until the with block exits. Useful for tests.

Return type:

Generator[None, None, None]

receivers_for(sender)

Yield each receiver to be called for sender, in addition to those to be called for ANY. Weakly referenced receivers that are not live will be disconnected and skipped.

Parameters:

sender (Any) – Yield receivers connected to this sender, in addition to those connected to ANY.

Return type:

Generator[Callable[[…], Any], None, None]

send(sender=None, /, *, _async_wrapper=None, **kwargs)

Call all receivers that are connected to the given sender or ANY. Each receiver is called with sender as a positional argument along with any extra keyword arguments. Return a list of (receiver, return value) tuples.

The order receivers are called is undefined, but can be influenced by setting set_class.

If a receiver raises an exception, that exception will propagate up. This makes debugging straightforward, with an assumption that correctly implemented receivers will not raise.

Parameters:
  • sender (t.Any | None) – Call receivers connected to this sender, in addition to those connected to ANY.

  • _async_wrapper (PAsyncWrapper | None) – Will be called on any receivers that are async coroutines to turn them into sync callables. For example, could run the receiver with an event loop.

  • kwargs (t.Any) – Extra keyword arguments to pass to each receiver.

Return type:

list[tuple[c.Callable[…, t.Any], t.Any]]

Changelog

Changed in version 1.7: Added the _async_wrapper argument.

async send_async(sender=None, /, *, _sync_wrapper=None, **kwargs)

Await all receivers that are connected to the given sender or ANY. Each receiver is called with sender as a positional argument along with any extra keyword arguments. Return a list of (receiver, return value) tuples.

The order receivers are called is undefined, but can be influenced by setting set_class.

If a receiver raises an exception, that exception will propagate up. This makes debugging straightforward, with an assumption that correctly implemented receivers will not raise.

Parameters:
  • sender (t.Any | None) – Call receivers connected to this sender, in addition to those connected to ANY.

  • _sync_wrapper (PSyncWrapper | None) – Will be called on any receivers that are sync callables to turn them into async coroutines. For example, could call the receiver in a thread.

  • kwargs (t.Any) – Extra keyword arguments to pass to each receiver.

Return type:

list[tuple[c.Callable[…, t.Any], t.Any]]

Changelog

Added in version 1.7.

temporarily_connected_to(receiver, sender=ANY)

Deprecated alias for connected_to().

Deprecated since version 1.1: Renamed to connected_to. Will be removed in Blinker 1.9.

Changelog

Added in version 0.9.

Parameters:
  • receiver (Callable[[...], Any])

  • sender (Any)

Return type:

AbstractContextManager[None]

ANY = ANY

An alias for the ANY sender symbol.

property receiver_connected: Signal

Emitted at the end of each connect() call.

The signal sender is the signal instance, and the connect() arguments are passed through: receiver, sender, and weak.

Changelog

Added in version 1.2.

property receiver_disconnected: Signal

Emitted at the end of each disconnect() call.

The sender is the signal instance, and the disconnect() arguments are passed through: receiver and sender.

This signal is emitted only when disconnect() is called explicitly. This signal cannot be emitted by an automatic disconnect when a weakly referenced receiver or sender goes out of scope, as the instance is no longer be available to be used as the sender for this signal.

An alternative approach is available by subscribing to receiver_connected and setting up a custom weakref cleanup callback on weak receivers and senders.

Changelog

Added in version 1.2.

receivers: dict[Any, ReferenceType[Callable[[...], Any]] | Callable[[...], Any]]

The map of connected receivers. Useful to quickly check if any receivers are connected to the signal: if s.receivers:. The structure and data is not part of the public API, but checking its boolean value is.

Named Signals

blinker.signal(name, doc=None)

Return a NamedSignal in default_namespace for the given name, creating it if required. Repeated calls with the same name return the same signal.

Parameters:
  • name (str) – The name of the signal.

  • doc (str | None) – The docstring of the signal.

Return type:

NamedSignal

blinker.default_namespace

A default Namespace for creating named signals. signal() creates a NamedSignal in this namespace.

class blinker.NamedSignal(name, doc=None)

Bases: Signal

A named generic notification emitter. The name is not used by the signal itself, but matches the key in the Namespace that it belongs to.

Parameters:
  • name (str) – The name of the signal within the namespace.

  • doc (str | None) – The docstring for the signal.

class blinker.Namespace

Bases: dict

A dict mapping names to signals.

signal(name, doc=None)

Return the NamedSignal for the given name, creating it if required. Repeated calls with the same name return the same signal.

Parameters:
  • name (str) – The name of the signal.

  • doc (str | None) – The docstring of the signal.

Return type:

NamedSignal

Changes

Version 1.8.1

Released 2024-04-28

  • Restore identity handling for str and int senders. #148

  • Fix deprecated blinker.base.WeakNamespace import. #149

  • Fix deprecated blinker.base.receiver_connected import. #153

  • Use types from collections.abc instead of typing. #150

  • Fully specify exported types as reported by pyright. #152

Version 1.8.0

Released 2024-04-27

  • Deprecate the __version__ attribute. Use feature detection, or importlib.metadata.version("blinker"), instead. #128

  • Specify that the deprecated temporarily_connected_to will be removed in the next version.

  • Show a deprecation warning for the deprecated global receiver_connected signal and specify that it will be removed in the next version.

  • Show a deprecation warning for the deprecated WeakNamespace and specify that it will be removed in the next version.

  • Greatly simplify how the library uses weakrefs. This is a significant change internally but should not affect any public API. #144

  • Expose the namespace used by signal() as default_namespace. #145

Version 1.7.0

Released 2023-11-01

  • Fixed messages printed to standard error about unraisable exceptions during signal cleanup, typically during interpreter shutdown. #123

  • Allow the Signal set_class to be customised, to allow calling of receivers in registration order. #116.

  • Drop Python 3.7 and support Python 3.12. #126

Version 1.6.3

Released 2023-09-23

  • Fix SyncWrapperType and AsyncWrapperType #108

  • Fixed issue where connected_to would not disconnect the receiver if an instance of BaseException was raised. #114

Version 1.6.2

Released 2023-04-12

  • Type annotations are not evaluated at runtime. typing-extensions is not a runtime dependency. #94

Version 1.6.1

Released 2023-04-09

  • Ensure that py.typed is present in the distributions (to enable other projects to use Blinker’s typing).

  • Require typing-extensions > 4.2 to ensure it includes ParamSpec. #90

Version 1.6

Released 2023-04-02

  • Add a muted context manager to temporarily turn off a signal. #84

  • int instances with the same value will be treated as the same sender, the same as str instances. #83

  • Add a send_async method to allow signals to send to coroutine receivers. #76

  • Update and modernise the project structure to match that used by the Pallets projects. #77

  • Add an initial set of type hints for the project.

Version 1.5

Released 2022-07-17

  • Support Python >= 3.7 and PyPy. Python 2, Python < 3.7, and Jython may continue to work, but the next release will make incompatible changes.

Version 1.4

Released 2015-07-23

  • Verified Python 3.4 support, no changes needed.

  • Additional bookkeeping cleanup for non-ANY connections at disconnect time.

  • Added Signal._cleanup_bookeeping() to prune stale bookkeeping on demand.

Version 1.3

Released 2013-07-03

  • The global signal stash behind signal() is now backed by a regular name-to-Signal dictionary. Previously, weak references were held in the mapping and ephermal usage in code like signal('foo').connect(...) could have surprising program behavior depending on import order of modules.

  • Namespace is now built on a regular dict. Use WeakNamespace for the older, weak-referencing behavior.

  • Signal.connect('text-sender') uses an alterate hashing strategy to avoid sharp edges in text identity.

Version 1.2

Released 2011-10-26

  • Added Signal.receiver_connected and Signal.receiver_disconnected per-Signal signals.

  • Deprecated the global receiver_connected signal.

  • Verified Python 3.2 support, no changes needed.

Version 1.1

Released 2010-07-21

  • Added @signal.connect_via(sender) decorator

  • Added signal.connected_to shorthand name for the temporarily_connected_to context manager.

Version 1.0

Released 2010-03-28

  • Python 3.0 and 3.1 compatibility.

Version 0.9

Released 2010-02-26

  • Added Signal.temporarily_connected_to context manager.

  • Docs! Sphinx docs, project web site.

Version 0.8

Released 2010-02-14

  • Initial release.

  • Extracted from flatland.util.signals.

  • Added Python 2.4 compatibility.

  • Added nearly functional Python 3.1 compatibility. Everything except connecting to instance methods seems to work.

MIT License

Copyright 2010 Jason Kirtland

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.