diff options
Diffstat (limited to 'docs/index.rst')
-rw-r--r-- | docs/index.rst | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..82a41d5 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,309 @@ +.. rst-class:: hide-header + +Blinker Documentation +===================== + +.. image:: _static/blinker-named.png + :align: center + +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.7 and Python 3.5 or later; +or Jython 2.7 or later; or PyPy 2.7 or later. + + +Decoupling With Named Signals +----------------------------- + +Named signals are created with :func:`signal`: + +.. code-block:: python + + >>> 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 +---------------------- + +:meth:`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. + +.. code-block:: python + + >>> def subscriber(sender): + ... print("Got a signal sent by %r" % sender) + ... + >>> ready = signal('ready') + >>> ready.connect(subscriber) + <function subscriber at 0x...> + + +Emitting Signals +---------------- + +Code producing events of interest can :meth:`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 :meth:`~Signal.send` method, signifying that +that particular instance was responsible for emitting the signal. + +.. code-block:: python + + >>> 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 +:meth:`~Signal.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 :meth:`Signal.connect` function accepts an +optional argument to restrict the subscription to one specific sending +object: + +.. code-block:: python + + >>> 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``: + +.. code-block:: python + + >>> 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 :meth:`~Signal.send`. +These will in turn be passed to the connected functions: + +.. code-block:: python + + >>> 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 :meth:`~Signal.send` collects the return values of +each connected function as a list of (``receiver function``, ``return +value``) pairs: + +.. code-block:: python + + >>> result + [(<function receive_data at 0x...>, 'received!')] + + +Anonymous Signals +----------------- + +Signals need not be named. The :class:`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: + +.. code-block:: python + + >>> 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 + ... + +``connect`` as a Decorator +-------------------------- + +You may have noticed the return value of :meth:`~Signal.connect` in +the console output in the sections above. This allows ``connect`` to +be used as a decorator on functions: + +.. code-block:: python + + >>> 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, :meth:`~Signal.connect_via` can be used: + +.. code-block:: python + + >>> 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. + + +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 :attr:`~Signal.receivers` +property: + +.. code-block:: python + + >>> 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: + +.. code-block:: python + + >>> 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. + +See the documentation of the :obj:`receiver_connected` built-in signal +for an example. + + +API Documentation +----------------- + +All public API members can (and should) be imported from ``blinker``:: + + from blinker import ANY, signal + +.. currentmodule:: blinker.base + +Basic Signals ++++++++++++++ + +.. autodata:: blinker.base.ANY + +.. autodata:: blinker.base.receiver_connected + +.. autoclass:: Signal + :members: + :undoc-members: + +Named Signals ++++++++++++++ + +.. function:: signal(name, doc=None) + + Return the :class:`NamedSignal` *name*, creating it if required. + + Repeated calls to this function will return the same signal object. + Signals are created in a global :class:`Namespace`. + +.. autoclass:: NamedSignal + :show-inheritance: + :members: + +.. autoclass:: Namespace + :show-inheritance: + :members: signal + +.. autoclass:: WeakNamespace + :show-inheritance: + :members: signal + + +Changes +======= + +.. include:: ../CHANGES.rst |