blinker-1.4/0000775000076500000240000000000012554156573012717 5ustar jekstaff00000000000000blinker-1.4/AUTHORS0000664000076500000240000000031712165000330013741 0ustar jekstaff00000000000000Blinker was originally written by Jason Kirtland. Contributors are: - Jason Kirtland Blinker includes code from Louie, by Patrick K. O'Brien, Mike C. Fletcher, and Matthew R. Scott. blinker-1.4/blinker/0000775000076500000240000000000012554156573014345 5ustar jekstaff00000000000000blinker-1.4/blinker/__init__.py0000664000076500000240000000045412554155674016462 0ustar jekstaff00000000000000from blinker.base import ( ANY, NamedSignal, Namespace, Signal, WeakNamespace, receiver_connected, signal, ) __all__ = [ 'ANY', 'NamedSignal', 'Namespace', 'Signal', 'WeakNamespace', 'receiver_connected', 'signal', ] __version__ = '1.4' blinker-1.4/blinker/_saferef.py0000664000076500000240000002200712165000330016443 0ustar jekstaff00000000000000# extracted from Louie, http://pylouie.org/ # updated for Python 3 # # Copyright (c) 2006 Patrick K. O'Brien, Mike C. Fletcher, # Matthew R. Scott # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # # * Neither the name of the nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # """Refactored 'safe reference from dispatcher.py""" import operator import sys import traceback import weakref try: callable except NameError: def callable(object): return hasattr(object, '__call__') if sys.version_info < (3,): get_self = operator.attrgetter('im_self') get_func = operator.attrgetter('im_func') else: get_self = operator.attrgetter('__self__') get_func = operator.attrgetter('__func__') def safe_ref(target, on_delete=None): """Return a *safe* weak reference to a callable target. - ``target``: The object to be weakly referenced, if it's a bound method reference, will create a BoundMethodWeakref, otherwise creates a simple weakref. - ``on_delete``: If provided, will have a hard reference stored to the callable to be called after the safe reference goes out of scope with the reference object, (either a weakref or a BoundMethodWeakref) as argument. """ try: im_self = get_self(target) except AttributeError: if callable(on_delete): return weakref.ref(target, on_delete) else: return weakref.ref(target) else: if im_self is not None: # Turn a bound method into a BoundMethodWeakref instance. # Keep track of these instances for lookup by disconnect(). assert hasattr(target, 'im_func') or hasattr(target, '__func__'), ( "safe_ref target %r has im_self, but no im_func, " "don't know how to create reference" % target) reference = BoundMethodWeakref(target=target, on_delete=on_delete) return reference class BoundMethodWeakref(object): """'Safe' and reusable weak references to instance methods. BoundMethodWeakref objects provide a mechanism for referencing a bound method without requiring that the method object itself (which is normally a transient object) is kept alive. Instead, the BoundMethodWeakref object keeps weak references to both the object and the function which together define the instance method. Attributes: - ``key``: The identity key for the reference, calculated by the class's calculate_key method applied to the target instance method. - ``deletion_methods``: Sequence of callable objects taking single argument, a reference to this object which will be called when *either* the target object or target function is garbage collected (i.e. when this object becomes invalid). These are specified as the on_delete parameters of safe_ref calls. - ``weak_self``: Weak reference to the target object. - ``weak_func``: Weak reference to the target function. Class Attributes: - ``_all_instances``: Class attribute pointing to all live BoundMethodWeakref objects indexed by the class's calculate_key(target) method applied to the target objects. This weak value dictionary is used to short-circuit creation so that multiple references to the same (object, function) pair produce the same BoundMethodWeakref instance. """ _all_instances = weakref.WeakValueDictionary() def __new__(cls, target, on_delete=None, *arguments, **named): """Create new instance or return current instance. Basically this method of construction allows us to short-circuit creation of references to already- referenced instance methods. The key corresponding to the target is calculated, and if there is already an existing reference, that is returned, with its deletion_methods attribute updated. Otherwise the new instance is created and registered in the table of already-referenced methods. """ key = cls.calculate_key(target) current = cls._all_instances.get(key) if current is not None: current.deletion_methods.append(on_delete) return current else: base = super(BoundMethodWeakref, cls).__new__(cls) cls._all_instances[key] = base base.__init__(target, on_delete, *arguments, **named) return base def __init__(self, target, on_delete=None): """Return a weak-reference-like instance for a bound method. - ``target``: The instance-method target for the weak reference, must have im_self and im_func attributes and be reconstructable via the following, which is true of built-in instance methods:: target.im_func.__get__( target.im_self ) - ``on_delete``: Optional callback which will be called when this weak reference ceases to be valid (i.e. either the object or the function is garbage collected). Should take a single argument, which will be passed a pointer to this object. """ def remove(weak, self=self): """Set self.isDead to True when method or instance is destroyed.""" methods = self.deletion_methods[:] del self.deletion_methods[:] try: del self.__class__._all_instances[self.key] except KeyError: pass for function in methods: try: if callable(function): function(self) except Exception: try: traceback.print_exc() except AttributeError: e = sys.exc_info()[1] print ('Exception during saferef %s ' 'cleanup function %s: %s' % (self, function, e)) self.deletion_methods = [on_delete] self.key = self.calculate_key(target) im_self = get_self(target) im_func = get_func(target) self.weak_self = weakref.ref(im_self, remove) self.weak_func = weakref.ref(im_func, remove) self.self_name = str(im_self) self.func_name = str(im_func.__name__) def calculate_key(cls, target): """Calculate the reference key for this reference. Currently this is a two-tuple of the id()'s of the target object and the target function respectively. """ return (id(get_self(target)), id(get_func(target))) calculate_key = classmethod(calculate_key) def __str__(self): """Give a friendly representation of the object.""" return "%s(%s.%s)" % ( self.__class__.__name__, self.self_name, self.func_name, ) __repr__ = __str__ def __nonzero__(self): """Whether we are still a valid reference.""" return self() is not None def __cmp__(self, other): """Compare with another reference.""" if not isinstance(other, self.__class__): return cmp(self.__class__, type(other)) return cmp(self.key, other.key) def __call__(self): """Return a strong reference to the bound method. If the target cannot be retrieved, then will return None, otherwise returns a bound instance method for our object and function. Note: You may call this method any number of times, as it does not invalidate the reference. """ target = self.weak_self() if target is not None: function = self.weak_func() if function is not None: return function.__get__(target) return None blinker-1.4/blinker/_utilities.py0000664000076500000240000001055112165000330017044 0ustar jekstaff00000000000000from weakref import ref from blinker._saferef import BoundMethodWeakref try: callable except NameError: def callable(object): return hasattr(object, '__call__') try: from collections import defaultdict except: class defaultdict(dict): def __init__(self, default_factory=None, *a, **kw): if (default_factory is not None and not hasattr(default_factory, '__call__')): raise TypeError('first argument must be callable') dict.__init__(self, *a, **kw) self.default_factory = default_factory def __getitem__(self, key): try: return dict.__getitem__(self, key) except KeyError: return self.__missing__(key) def __missing__(self, key): if self.default_factory is None: raise KeyError(key) self[key] = value = self.default_factory() return value def __reduce__(self): if self.default_factory is None: args = tuple() else: args = self.default_factory, return type(self), args, None, None, self.items() def copy(self): return self.__copy__() def __copy__(self): return type(self)(self.default_factory, self) def __deepcopy__(self, memo): import copy return type(self)(self.default_factory, copy.deepcopy(self.items())) def __repr__(self): return 'defaultdict(%s, %s)' % (self.default_factory, dict.__repr__(self)) try: from contextlib import contextmanager except ImportError: def contextmanager(fn): def oops(*args, **kw): raise RuntimeError("Python 2.5 or above is required to use " "context managers.") oops.__name__ = fn.__name__ return oops class _symbol(object): def __init__(self, name): """Construct a new named symbol.""" self.__name__ = self.name = name def __reduce__(self): return symbol, (self.name,) def __repr__(self): return self.name _symbol.__name__ = 'symbol' class symbol(object): """A constant symbol. >>> symbol('foo') is symbol('foo') True >>> symbol('foo') foo A slight refinement of the MAGICCOOKIE=object() pattern. The primary advantage of symbol() is its repr(). They are also singletons. Repeated calls of symbol('name') will all return the same instance. """ symbols = {} def __new__(cls, name): try: return cls.symbols[name] except KeyError: return cls.symbols.setdefault(name, _symbol(name)) try: text = (str, unicode) except NameError: text = str def hashable_identity(obj): if hasattr(obj, '__func__'): return (id(obj.__func__), id(obj.__self__)) elif hasattr(obj, 'im_func'): return (id(obj.im_func), id(obj.im_self)) elif isinstance(obj, text): return obj else: return id(obj) WeakTypes = (ref, BoundMethodWeakref) class annotatable_weakref(ref): """A weakref.ref that supports custom instance attributes.""" def reference(object, callback=None, **annotations): """Return an annotated weak ref.""" if callable(object): weak = callable_reference(object, callback) else: weak = annotatable_weakref(object, callback) for key, value in annotations.items(): setattr(weak, key, value) return weak def callable_reference(object, callback=None): """Return an annotated weak ref, supporting bound instance methods.""" if hasattr(object, 'im_self') and object.im_self is not None: return BoundMethodWeakref(target=object, on_delete=callback) elif hasattr(object, '__self__') and object.__self__ is not None: return BoundMethodWeakref(target=object, on_delete=callback) return annotatable_weakref(object, callback) class lazy_property(object): """A @property that is only evaluated once.""" def __init__(self, deferred): self._deferred = deferred self.__doc__ = deferred.__doc__ def __get__(self, obj, cls): if obj is None: return self value = self._deferred(obj) setattr(obj, self._deferred.__name__, value) return value blinker-1.4/blinker/base.py0000664000076500000240000003767712554150522015641 0ustar jekstaff00000000000000# -*- coding: utf-8; fill-column: 76 -*- """Signals and events. A small implementation of signals, inspired by a snippet of Django signal API client code seen in a blog post. Signals are first-class objects and each manages its own receivers and message emission. The :func:`signal` function provides singleton behavior for named signals. """ from warnings import warn from weakref import WeakValueDictionary from blinker._utilities import ( WeakTypes, contextmanager, defaultdict, hashable_identity, lazy_property, reference, symbol, ) ANY = symbol('ANY') ANY.__doc__ = 'Token for "any sender".' ANY_ID = 0 class Signal(object): """A notification emitter.""" #: An :obj:`ANY` convenience synonym, allows ``Signal.ANY`` #: without an additional import. ANY = ANY @lazy_property def receiver_connected(self): """Emitted after each :meth:`connect`. The signal sender is the signal instance, and the :meth:`connect` arguments are passed through: *receiver*, *sender*, and *weak*. .. versionadded:: 1.2 """ return Signal(doc="Emitted after a receiver connects.") @lazy_property def receiver_disconnected(self): """Emitted after :meth:`disconnect`. The sender is the signal instance, and the :meth:`disconnect` arguments are passed through: *receiver* and *sender*. Note, this signal is emitted **only** when :meth:`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 :attr:`receiver_connected` and setting up a custom weakref cleanup callback on weak receivers and senders. .. versionadded:: 1.2 """ return Signal(doc="Emitted after a receiver disconnects.") def __init__(self, doc=None): """ :param doc: optional. If provided, will be assigned to the signal's __doc__ attribute. """ if doc: self.__doc__ = doc #: A mapping of connected receivers. #: #: The values of this mapping are not meaningful outside of the #: internal :class:`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. self.receivers = {} self._by_receiver = defaultdict(set) self._by_sender = defaultdict(set) self._weak_senders = {} def connect(self, receiver, sender=ANY, weak=True): """Connect *receiver* to signal events sent by *sender*. :param receiver: A callable. Will be invoked by :meth:`send` with `sender=` as a single positional argument and any \*\*kwargs that were provided to a call to :meth:`send`. :param sender: Any object or :obj:`ANY`, defaults to ``ANY``. Restricts notifications delivered to *receiver* to only those :meth:`send` emissions sent by *sender*. If ``ANY``, the receiver will always be notified. A *receiver* may be connected to multiple *sender* values on the same Signal through multiple calls to :meth:`connect`. :param weak: If true, the Signal will hold a weakref to *receiver* and automatically disconnect when *receiver* goes out of scope or is garbage collected. Defaults to True. """ receiver_id = hashable_identity(receiver) if weak: receiver_ref = reference(receiver, self._cleanup_receiver) receiver_ref.receiver_id = receiver_id else: receiver_ref = receiver if sender is ANY: sender_id = ANY_ID else: sender_id = hashable_identity(sender) self.receivers.setdefault(receiver_id, receiver_ref) self._by_sender[sender_id].add(receiver_id) self._by_receiver[receiver_id].add(sender_id) del receiver_ref if sender is not ANY and sender_id not in self._weak_senders: # wire together a cleanup for weakref-able senders try: sender_ref = reference(sender, self._cleanup_sender) sender_ref.sender_id = sender_id except TypeError: pass else: self._weak_senders.setdefault(sender_id, sender_ref) del sender_ref # broadcast this connection. if receivers raise, disconnect. if ('receiver_connected' in self.__dict__ and self.receiver_connected.receivers): try: self.receiver_connected.send(self, receiver=receiver, sender=sender, weak=weak) except: self.disconnect(receiver, sender) raise if receiver_connected.receivers and self is not receiver_connected: try: receiver_connected.send(self, receiver_arg=receiver, sender_arg=sender, weak_arg=weak) except: self.disconnect(receiver, sender) raise return receiver def connect_via(self, sender, weak=False): """Connect the decorated function as a receiver for *sender*. :param sender: Any object or :obj:`ANY`. The decorated function will only receive :meth:`send` emissions sent by *sender*. If ``ANY``, the receiver will always be notified. A function may be decorated multiple times with differing *sender* values. :param weak: If true, the Signal will hold a weakref to the decorated function and automatically disconnect when *receiver* goes out of scope or is garbage collected. Unlike :meth:`connect`, this defaults to False. The decorated function will be invoked by :meth:`send` with `sender=` as a single positional argument and any \*\*kwargs that were provided to the call to :meth:`send`. .. versionadded:: 1.1 """ def decorator(fn): self.connect(fn, sender, weak) return fn return decorator @contextmanager def connected_to(self, receiver, sender=ANY): """Execute a block with the signal temporarily connected to *receiver*. :param receiver: a receiver callable :param sender: optional, a sender to filter on 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: .. testsetup:: from __future__ import with_statement from blinker import Signal on_ready = Signal() receiver = lambda sender: None .. testcode:: with on_ready.connected_to(receiver): # do stuff on_ready.send(123) .. versionadded:: 1.1 """ self.connect(receiver, sender=sender, weak=False) try: yield None except: self.disconnect(receiver) raise else: self.disconnect(receiver) def temporarily_connected_to(self, receiver, sender=ANY): """An alias for :meth:`connected_to`. :param receiver: a receiver callable :param sender: optional, a sender to filter on .. versionadded:: 0.9 .. versionchanged:: 1.1 Renamed to :meth:`connected_to`. ``temporarily_connected_to`` was deprecated in 1.2 and will be removed in a subsequent version. """ warn("temporarily_connected_to is deprecated; " "use connected_to instead.", DeprecationWarning) return self.connected_to(receiver, sender) def send(self, *sender, **kwargs): """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. :param \*sender: Any object or ``None``. If omitted, synonymous with ``None``. Only accepts one positional argument. :param \*\*kwargs: Data to be sent to receivers. """ # Using '*sender' rather than 'sender=None' allows 'sender' to be # used as a keyword argument- i.e. it's an invisible name in the # function signature. if len(sender) == 0: sender = None elif len(sender) > 1: raise TypeError('send() accepts only one positional argument, ' '%s given' % len(sender)) else: sender = sender[0] if not self.receivers: return [] 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*. Performs an optimistic check only. Does not guarantee that all weakly referenced receivers are still alive. See :meth:`receivers_for` for a stronger search. """ if not self.receivers: return False if self._by_sender[ANY_ID]: return True if sender is ANY: return False return hashable_identity(sender) in self._by_sender def receivers_for(self, sender): """Iterate all live receivers listening for *sender*.""" # TODO: test receivers_for(ANY) if self.receivers: sender_id = hashable_identity(sender) if sender_id in self._by_sender: ids = (self._by_sender[ANY_ID] | self._by_sender[sender_id]) else: ids = self._by_sender[ANY_ID].copy() for receiver_id in ids: receiver = self.receivers.get(receiver_id) if receiver is None: continue if isinstance(receiver, WeakTypes): strong = receiver() if strong is None: self._disconnect(receiver_id, ANY_ID) continue receiver = strong yield receiver def disconnect(self, receiver, sender=ANY): """Disconnect *receiver* from this signal's events. :param receiver: a previously :meth:`connected` callable :param sender: a specific sender to disconnect from, or :obj:`ANY` to disconnect from all senders. Defaults to ``ANY``. """ if sender is ANY: sender_id = ANY_ID else: sender_id = hashable_identity(sender) receiver_id = hashable_identity(receiver) self._disconnect(receiver_id, sender_id) if ('receiver_disconnected' in self.__dict__ and self.receiver_disconnected.receivers): self.receiver_disconnected.send(self, receiver=receiver, sender=sender) def _disconnect(self, receiver_id, sender_id): if sender_id == ANY_ID: if self._by_receiver.pop(receiver_id, False): for bucket in self._by_sender.values(): bucket.discard(receiver_id) self.receivers.pop(receiver_id, None) else: self._by_sender[sender_id].discard(receiver_id) self._by_receiver[receiver_id].discard(sender_id) def _cleanup_receiver(self, receiver_ref): """Disconnect a receiver from all senders.""" self._disconnect(receiver_ref.receiver_id, ANY_ID) def _cleanup_sender(self, sender_ref): """Disconnect all receivers from a sender.""" sender_id = sender_ref.sender_id assert sender_id != ANY_ID self._weak_senders.pop(sender_id, None) for receiver_id in self._by_sender.pop(sender_id, ()): self._by_receiver[receiver_id].discard(sender_id) def _cleanup_bookkeeping(self): """Prune unused sender/receiver bookeeping. Not threadsafe. Connecting & disconnecting leave behind a small amount of bookeeping for the receiver and sender values. Typical workloads using Blinker, for example in most web apps, Flask, CLI scripts, etc., are not adversely affected by this bookkeeping. With a long-running Python process performing dynamic signal routing with high volume- e.g. connecting to function closures, "senders" are all unique object instances, and doing all of this over and over- you may see memory usage will grow due to extraneous bookeeping. (An empty set() for each stale sender/receiver pair.) This method will prune that bookeeping away, with the caveat that such pruning is not threadsafe. The risk is that cleanup of a fully disconnected receiver/sender pair occurs while another thread is connecting that same pair. If you are in the highly dynamic, unique receiver/sender situation that has lead you to this method, that failure mode is perhaps not a big deal for you. """ for mapping in (self._by_sender, self._by_receiver): for _id, bucket in list(mapping.items()): if not bucket: mapping.pop(_id, None) def _clear_state(self): """Throw away all signal state. Useful for unit tests.""" self._weak_senders.clear() self.receivers.clear() self._by_sender.clear() self._by_receiver.clear() receiver_connected = Signal("""\ Sent by a :class:`Signal` after a receiver connects. :argument: the Signal that was connected to :keyword receiver_arg: the connected receiver :keyword sender_arg: the sender to connect to :keyword weak_arg: true if the connection to receiver_arg is a weak reference .. deprecated:: 1.2 As of 1.2, individual signals have their own private :attr:`~Signal.receiver_connected` and :attr:`~Signal.receiver_disconnected` signals with a slightly simplified call signature. This global signal is planned to be removed in 1.6. """) class NamedSignal(Signal): """A named generic notification emitter.""" def __init__(self, name, doc=None): Signal.__init__(self, doc) #: The name of this signal. self.name = name def __repr__(self): base = Signal.__repr__(self) return "%s; %r>" % (base[:-1], self.name) class Namespace(dict): """A mapping of signal names to signals.""" def signal(self, name, doc=None): """Return the :class:`NamedSignal` *name*, creating it if required. Repeated calls to this function will return the same signal object. """ try: return self[name] except KeyError: return self.setdefault(name, NamedSignal(name, doc)) class WeakNamespace(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. .. versionadded:: 1.3 """ def signal(self, name, doc=None): """Return the :class:`NamedSignal` *name*, creating it if required. Repeated calls to this function will return the same signal object. """ try: return self[name] except KeyError: return self.setdefault(name, NamedSignal(name, doc)) signal = Namespace().signal blinker-1.4/blinker.egg-info/0000775000076500000240000000000012554156573016037 5ustar jekstaff00000000000000blinker-1.4/blinker.egg-info/dependency_links.txt0000664000076500000240000000000112554156573022105 0ustar jekstaff00000000000000 blinker-1.4/blinker.egg-info/PKG-INFO0000664000076500000240000000747512554156573017151 0ustar jekstaff00000000000000Metadata-Version: 1.0 Name: blinker Version: 1.4 Summary: Fast, simple object-to-object and broadcast signaling Home-page: http://pythonhosted.org/blinker/ Author: Jason Kirtland Author-email: jek@discorporate.us License: MIT License Description: [![Build Status](https://travis-ci.org/jek/blinker.svg?branch=master)](https://travis-ci.org/jek/blinker) # Blinker Blinker provides a fast dispatching system that allows any number of interested parties to subscribe to events, or "signals". Signal receivers can subscribe to specific senders or receive signals sent by any sender. >>> from blinker import signal >>> started = signal('round-started') >>> def each(round): ... print "Round %s!" % round ... >>> started.connect(each) >>> def round_two(round): ... print "This is round two." ... >>> started.connect(round_two, sender=2) >>> for round in range(1, 4): ... started.send(round) ... Round 1! Round 2! This is round two. Round 3! See the [Blinker documentation](https://pythonhosted.org/blinker/) for more information. ## Requirements Blinker requires Python 2.4 or higher, Python 3.0 or higher, or Jython 2.5 or higher. ## Changelog Summary 1.3 (July 3, 2013) - The global signal stash behind blinker.signal() is now backed by a regular name-to-Signal dictionary. Previously, weak references were held in the mapping and ephemeral usage in code like ``signal('foo').connect(...)`` could have surprising program behavior depending on import order of modules. - blinker.Namespace is now built on a regular dict. Use blinker.WeakNamespace for the older, weak-referencing behavior. - Signal.connect('text-sender') uses an alternate hashing strategy to avoid sharp edges in text identity. 1.2 (October 26, 2011) - 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!) 1.1 (July 21, 2010) - Added ``@signal.connect_via(sender)`` decorator - Added ``signal.connected_to`` shorthand name for the ``temporarily_connected_to`` context manager. 1.0 (March 28, 2010) - Python 3.x compatibility 0.9 (February 26, 2010) - Sphinx docs, project website - Added ``with a_signal.temporarily_connected_to(receiver): ...`` support Keywords: signal emit events broadcast Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.4 Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.0 Classifier: Programming Language :: Python :: 3.1 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Utilities blinker-1.4/blinker.egg-info/SOURCES.txt0000664000076500000240000000230412554156573017722 0ustar jekstaff00000000000000AUTHORS CHANGES LICENSE MANIFEST.in README.md setup.cfg setup.py blinker/__init__.py blinker/_saferef.py blinker/_utilities.py blinker/base.py blinker.egg-info/PKG-INFO blinker.egg-info/SOURCES.txt blinker.egg-info/dependency_links.txt blinker.egg-info/top_level.txt docs/html/genindex.html docs/html/index.html docs/html/objects.inv docs/html/search.html docs/html/searchindex.js docs/html/_sources/index.txt docs/html/_static/basic.css docs/html/_static/blinker-named.png docs/html/_static/blinker64.png docs/html/_static/comment-bright.png docs/html/_static/comment-close.png docs/html/_static/comment.png docs/html/_static/doctools.js docs/html/_static/down-pressed.png docs/html/_static/down.png docs/html/_static/file.png docs/html/_static/flasky.css docs/html/_static/jquery.js docs/html/_static/minus.png docs/html/_static/plus.png docs/html/_static/pygments.css docs/html/_static/searchtools.js docs/html/_static/underscore.js docs/html/_static/up-pressed.png docs/html/_static/up.png docs/html/_static/websupport.js docs/source/conf.py docs/source/index.rst docs/source/_themes/flask_theme_support.py docs/text/index.txt tests/test_context.py tests/test_saferef.py tests/test_signals.py tests/test_utilities.pyblinker-1.4/blinker.egg-info/top_level.txt0000664000076500000240000000001012554156573020560 0ustar jekstaff00000000000000blinker blinker-1.4/CHANGES0000664000076500000240000000344612554156235013714 0ustar jekstaff00000000000000 Blinker Changelog ================= Version 1.4 ----------- Released July 23, 2015 - 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 July 3, 2013 - The global signal stash behind blinker.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. - blinker.Namespace is now built on a regular dict. Use blinker.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 October 26, 2011 - 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 July 21, 2010 - Added ``@signal.connect_via(sender)`` decorator - Added ``signal.connected_to`` shorthand name for the ``temporarily_connected_to`` context manager. Version 1.0 ----------- Released March 28, 2010 - Python 3.0 and 3.1 compatibility Version 0.9 ----------- Released February 26, 2010 - Added ``Signal.temporarily_connected_to`` context manager - Docs! Sphinx docs, project web site. Version 0.8 ----------- Released February 14, 2010 - 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.) blinker-1.4/docs/0000775000076500000240000000000012554156573013647 5ustar jekstaff00000000000000blinker-1.4/docs/html/0000775000076500000240000000000012554156573014613 5ustar jekstaff00000000000000blinker-1.4/docs/html/_sources/0000775000076500000240000000000012554156573016435 5ustar jekstaff00000000000000blinker-1.4/docs/html/_sources/index.txt0000664000076500000240000001751512510555122020277 0ustar jekstaff00000000000000Blinker Documentation ===================== 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. Decoupling With Named Signals ----------------------------- Named signals are created with :func:`signal`: .. doctest:: >>> 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. .. doctest:: >>> def subscriber(sender): ... print("Got a signal sent by %r" % sender) ... >>> ready = signal('ready') >>> ready.connect(subscriber) 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. .. doctest:: >>> 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 '' % self.name ... >>> processor_a = Processor('a') >>> processor_a.go() Got a signal sent by 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: .. doctest:: >>> 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) This function has been subscribed to ``ready`` but only when sent by ``processor_b``: .. doctest:: >>> processor_a.go() Got a signal sent by Processing. >>> processor_b.go() Got a signal sent by 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: .. doctest:: >>> 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: .. doctest:: >>> result [(, '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: .. doctest:: >>> 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 '' % 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: .. doctest:: >>> 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: .. doctest:: >>> 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: .. doctest:: >>> 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: .. doctest:: >>> 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 .. include:: ../../CHANGES blinker-1.4/docs/html/_static/0000775000076500000240000000000012554156573016241 5ustar jekstaff00000000000000blinker-1.4/docs/html/_static/basic.css0000664000076500000240000002041712554156450020032 0ustar jekstaff00000000000000/* * basic.css * ~~~~~~~~~ * * Sphinx stylesheet -- basic theme. * * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ /* -- main layout ----------------------------------------------------------- */ div.clearer { clear: both; } /* -- relbar ---------------------------------------------------------------- */ div.related { width: 100%; font-size: 90%; } div.related h3 { display: none; } div.related ul { margin: 0; padding: 0 0 0 10px; list-style: none; } div.related li { display: inline; } div.related li.right { float: right; margin-right: 5px; } /* -- sidebar --------------------------------------------------------------- */ div.sphinxsidebarwrapper { padding: 10px 5px 0 10px; } div.sphinxsidebar { float: left; width: 230px; margin-left: -100%; font-size: 90%; } div.sphinxsidebar ul { list-style: none; } div.sphinxsidebar ul ul, div.sphinxsidebar ul.want-points { margin-left: 20px; list-style: square; } div.sphinxsidebar ul ul { margin-top: 0; margin-bottom: 0; } div.sphinxsidebar form { margin-top: 10px; } div.sphinxsidebar input { border: 1px solid #98dbcc; font-family: sans-serif; font-size: 1em; } div.sphinxsidebar #searchbox input[type="text"] { width: 170px; } div.sphinxsidebar #searchbox input[type="submit"] { width: 30px; } img { border: 0; } /* -- search page ----------------------------------------------------------- */ ul.search { margin: 10px 0 0 20px; padding: 0; } ul.search li { padding: 5px 0 5px 20px; background-image: url(file.png); background-repeat: no-repeat; background-position: 0 7px; } ul.search li a { font-weight: bold; } ul.search li div.context { color: #888; margin: 2px 0 0 30px; text-align: left; } ul.keywordmatches li.goodmatch a { font-weight: bold; } /* -- index page ------------------------------------------------------------ */ table.contentstable { width: 90%; } table.contentstable p.biglink { line-height: 150%; } a.biglink { font-size: 1.3em; } span.linkdescr { font-style: italic; padding-top: 5px; font-size: 90%; } /* -- general index --------------------------------------------------------- */ table.indextable { width: 100%; } table.indextable td { text-align: left; vertical-align: top; } table.indextable dl, table.indextable dd { margin-top: 0; margin-bottom: 0; } table.indextable tr.pcap { height: 10px; } table.indextable tr.cap { margin-top: 10px; background-color: #f2f2f2; } img.toggler { margin-right: 3px; margin-top: 3px; cursor: pointer; } div.modindex-jumpbox { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; margin: 1em 0 1em 0; padding: 0.4em; } div.genindex-jumpbox { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; margin: 1em 0 1em 0; padding: 0.4em; } /* -- general body styles --------------------------------------------------- */ a.headerlink { visibility: hidden; } h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink { visibility: visible; } div.body p.caption { text-align: inherit; } div.body td { text-align: left; } .field-list ul { padding-left: 1em; } .first { margin-top: 0 !important; } p.rubric { margin-top: 30px; font-weight: bold; } img.align-left, .figure.align-left, object.align-left { clear: left; float: left; margin-right: 1em; } img.align-right, .figure.align-right, object.align-right { clear: right; float: right; margin-left: 1em; } img.align-center, .figure.align-center, object.align-center { display: block; margin-left: auto; margin-right: auto; } .align-left { text-align: left; } .align-center { text-align: center; } .align-right { text-align: right; } /* -- sidebars -------------------------------------------------------------- */ div.sidebar { margin: 0 0 0.5em 1em; border: 1px solid #ddb; padding: 7px 7px 0 7px; background-color: #ffe; width: 40%; float: right; } p.sidebar-title { font-weight: bold; } /* -- topics ---------------------------------------------------------------- */ div.topic { border: 1px solid #ccc; padding: 7px 7px 0 7px; margin: 10px 0 10px 0; } p.topic-title { font-size: 1.1em; font-weight: bold; margin-top: 10px; } /* -- admonitions ----------------------------------------------------------- */ div.admonition { margin-top: 10px; margin-bottom: 10px; padding: 7px; } div.admonition dt { font-weight: bold; } div.admonition dl { margin-bottom: 0; } p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; } div.body p.centered { text-align: center; margin-top: 25px; } /* -- tables ---------------------------------------------------------------- */ table.docutils { border: 0; border-collapse: collapse; } table.docutils td, table.docutils th { padding: 1px 8px 1px 5px; border-top: 0; border-left: 0; border-right: 0; border-bottom: 1px solid #aaa; } table.field-list td, table.field-list th { border: 0 !important; } table.footnote td, table.footnote th { border: 0 !important; } th { text-align: left; padding-right: 5px; } table.citation { border-left: solid 1px gray; margin-left: 1px; } table.citation td { border-bottom: none; } /* -- other body styles ----------------------------------------------------- */ ol.arabic { list-style: decimal; } ol.loweralpha { list-style: lower-alpha; } ol.upperalpha { list-style: upper-alpha; } ol.lowerroman { list-style: lower-roman; } ol.upperroman { list-style: upper-roman; } dl { margin-bottom: 15px; } dd p { margin-top: 0px; } dd ul, dd table { margin-bottom: 10px; } dd { margin-top: 3px; margin-bottom: 10px; margin-left: 30px; } dt:target, .highlighted { background-color: #fbe54e; } dl.glossary dt { font-weight: bold; font-size: 1.1em; } .field-list ul { margin: 0; padding-left: 1em; } .field-list p { margin: 0; } .refcount { color: #060; } .optional { font-size: 1.3em; } .versionmodified { font-style: italic; } .system-message { background-color: #fda; padding: 5px; border: 3px solid red; } .footnote:target { background-color: #ffa; } .line-block { display: block; margin-top: 1em; margin-bottom: 1em; } .line-block .line-block { margin-top: 0; margin-bottom: 0; margin-left: 1.5em; } .guilabel, .menuselection { font-family: sans-serif; } .accelerator { text-decoration: underline; } .classifier { font-style: oblique; } abbr, acronym { border-bottom: dotted 1px; cursor: help; } /* -- code displays --------------------------------------------------------- */ pre { overflow: auto; overflow-y: hidden; /* fixes display issues on Chrome browsers */ } td.linenos pre { padding: 5px 0px; border: 0; background-color: transparent; color: #aaa; } table.highlighttable { margin-left: 0.5em; } table.highlighttable td { padding: 0 0.5em 0 0.5em; } tt.descname { background-color: transparent; font-weight: bold; font-size: 1.2em; } tt.descclassname { background-color: transparent; } tt.xref, a tt { background-color: transparent; font-weight: bold; } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { background-color: transparent; } .viewcode-link { float: right; } .viewcode-back { float: right; font-family: sans-serif; } div.viewcode-block:target { margin: -1px -10px; padding: 0 10px; } /* -- math display ---------------------------------------------------------- */ img.math { vertical-align: middle; } div.body div.math p { text-align: center; } span.eqno { float: right; } /* -- printout stylesheet --------------------------------------------------- */ @media print { div.document, div.documentwrapper, div.bodywrapper { margin: 0 !important; width: 100%; } div.sphinxsidebar, div.related, div.footer, #top-link { display: none; } }blinker-1.4/docs/html/_static/blinker-named.png0000664000076500000240000000705312165264003021446 0ustar jekstaff00000000000000‰PNG  IHDR¨@Ž\<[sRGB®ÎébKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEÚ.˜\g+ «IDATxÚí{x”ŽÇ?›d’@ ˆî AQ¹>R­XE¼Tð´BUlÑ#ú-=ôÐÚ+J¡(‚xçÞJQA!‘› ü0Ã%H›Ën²{þØ n–ÝdCv“ÔÌ÷yöɾï;;óî;Ÿ™ùý~3³±Ì}|œ+!±““¨Â‚¼°ÿþÓÇŒš­";9g<ðlXKüòKÏvš·ÂÌ#02€@  FFP##¨‘ÔÈÈjd522€ýôu:¦F  ÍS¹9¯µá&œU¦VBŠøwÿUÎr¾Ûñ$G½À=óé;èAÉ[)e¢}4d;àW€ù´l"b§´$@ËËNðÍW¿áì™mÎÉ~‘äÎãHH¾º¡`Î~ÄûIfWJv뀷Dä¼¼&kÅÀC^×b€ÑÀ—"Rdpü ñE§³Èühb 8Ýr±'s‡­!ÙßÌ©N€H p+ð°_)u•t·àN×þ|,ÕsRJ TJ-RJõhñ=èÑ¢Cœ´§ðüiJÊm´‹I¤]l© =h›¼r­`ÿŽßãtÚ/nqaQ¤t»0‹µ!E ÷:®V››wp¤Ó¤ÿRJõ‘Ÿí¨ÿ¶SJňH©Çµï€2`{ëøÀÏ—wÞ"ýþL6Ÿì—Í9ë9˜¿×w7m c@ê0FôÇÄþwÑ.&ñÒìͪr²¿™KnΫ>¯·ït郞&6®gC¿V´×ñDùØëÜ ¥ÔÀóÀ¯õ¹DàAàI?Ï8¸¨ˆ,–„¸ŽSôß´Õƒæçñæ×/°zÇ2*«jï8]NvånaWî^Ýúw›Éä!3ˆŽˆ ¸¼²ÒãìÉœIѩ̋®EŤ>h.SoÅ3ÚèÎjÀÊ”R3€~À0}úÊfjÆE¶4ëðçL[>Š•_/©NoÙÊϲøóyL[6šÜÂC}¦ðt&Ynºΰ°HÒú>ȵ¿œh'ȯ´g¾ÚãT FM臻_ç‘wî è|Aƒ þ¡àÓ_ÀÎܯjMwìÐklÿìÊKójç®aøøuôøáá­CùŒ %yÚ•?¬šhˆß˜½†ùk‹+ :«[gË ™³ê^¼w=iíûÔ´7+KÉÞý4¹9¯Ô4cRè5à RºþB;Ç!WŸÒ\ëñþõð°Ã€Í"R@ú–´J€½À±…ê(¥RÚáséFø¥ˆ”Õ3Ÿ¶@ GDò‚ èþüÝÌû×Ì ÁY­â ­¾——§~D\«¶”—eOæÃžÚz!ÅA·>¿¦‡šEDd»ÆlÄã•RJDÄÏC¿˜¬ˆÈ–zä}?îøèß™yv^ZÛ€ÚÙ¹¸Ì×cTJM‘·ƒep/ð[`pPG3zjnŠ•RÏóDÄîõÙIÀážÐÈ ¥£ ÑÀy¥TR €¨Ëåbá§s)­( G òòægøÝØÿÁ^Q@Ö†I”•¿p=¡Ãú xŠø„M5ÊlTJÍÕa¦Ý{õÇÛœè1Ì?Y_øõߟyª£côûaç[d »ÇÒxS)•'"_ø)ËR ŒÀ `¥ˆlVJÅ«ô=lz‹HŽN<Ìž®WJM¨ž¤Ð£Â2ýŒ|…ìJ•@yÐzÐÖ°ã‡Í!¥àÝ]Ë™ào ­µ8'oQÀ6¥Ôà=àzí Nô4?´)ñg¥Ô1ÝËÔ#À4$ÉNO;~î ‰Lï·ÁNÒÊm‹CN€£ÊÎëY èqù#ôÎxŒ«'|Bç´)M g êª”ªo€×â§wó„©˜à §å´ˆ<<ãqz¸Rª‹Ÿ²zéaÛÎ;w4œï¯¿Òp,®Å6~8ªßOUJ]î§Ós“Dä>ÙT8ôpÁöÍj”Þt`-N—kd<Ý/ÿ‘Qí› |vÜÓ™S€Qú5YÃQm‹Äï(¥¢‚\öR©-÷4pÎãØŸÔ˜ Á´(¥foáÀ7À/uO7ÛÓW­%¼æòŠ^Üâ'éE䃅™²Öh”ä³ïÄvš¡ÆŠÈtY©{M"òŽˆÌÁ=ÿwîz`QË~¿¶‹Ú¬XãÃlðÙë)¥Ÿ/èžÛÜ¢óIóŠZ<ª”êT‹ià™Ö×\%° ¤a¦ƒ§ö5* ßÜMFç¡~œ5;{1Uöb•6v•Ç9÷yÇ9vûY޳8*lT9Î2ô†w±F%^ê-}!"›j¤B)5KÛWÿ¡”zCD6é‘df§öðj[„ÏÅóñ‘êØmºÛwJ©UÀ. X7€ë<œÃjòü‘³!ôìù3 è™’“?üœ:±‡ý•ö²ÓØ+ /iÕ¼ËÕ e˜»ëJ "N¥Ô«ʯ´÷  ͉äŸå°·ât$!VéѸgËr€.Ú øø³Ïž F¥V–5. %î†XbË&ÿèºæ0¼Œö„¤{˯ïLV}•|¢ß{Ç‹[EdÃ¥ú¾ ýòuÚ ÖðÆ]c`ˆ¢™)PO­w0+¦žªÏñ­Ð+¥Zé÷ÞÀó €3ÐÆÕ°4!¶q=éö±ܵñ8iéQe·a·Ÿ£ÊQŒÝqŽJ»JÇYmkžsÛ›v•En»Óî~5pX÷ÔH¥”¥¶íz ï!/š£¦‰È ¥T{ÜøQº·xPDò”RÕ+o šú†ë4¦C£ÞPr·Óh ³™‘ -ά³Ù:«¨tœ£²Ò†½¼ˆˆÈ¶õ½J/Ga*ðŠ8{áX{®·\Ñ á,ÂçDDÎ(¥&›µs÷€Rj—ˆ¼¤ÃLcµ­y—Rj¡ˆ8š- R¯bõŽ¥vCWv½¦ÁyX±F%`J :¶Û¥dáí‘.Ó{‹6gtåuýHdœ—©4WD¾ö8öŒ%–ûµ Uõ¼÷R?yåznÚ‘óJ©[u °H)•-"›”R·ƒ*¥f‰ÈFÙ¸G7ÞY"rÚϽW†ÐÁ]G…½2ôÛy{wÈ ¥mWÀÅ™“Ÿ‘˜< KXxS4\ï௸C¿ü €ÇDd¹çé”Y™\ý÷ˆ×ùê•>…¸çþëô-=Þçx]«†'ßGôáˆRêÜ›þ"€·•R}Dd­Rj¤îqÇÙJ©}¸'%R=ÂQeÀeT—W¦ñÁ;I 1‰\Ÿ~s£P1VÝê®Ü÷ÙþùÝìÜt7¶¢=N§ˆlý—'¨ðz×=¬èŠý+îMv]|À î)Ábàe×êJœçuþÀ1`~€[›·ã^Èò°ÃëÚ[º‡^îç»~‚{ÑKÐI ˆHîHSõÈ‘ªG‹¾ÀaÜ3kWŠÈ¯üìÀïý¸Wj5l4\ð—©Uuý…ù{™¶üœ®ªAѾM'VÍØF8v¾\;ŠŠ²|=\GÒ½ï ÒúÎ$"2.¨e.Y<Ûùð®h’.º1¥”Š‘Š:Ò¤àž&Ýè+­ÞŠm¥¿ ´ £w‡~Lºâ¾ÞÈý#£•5«5ž~CÿJd«DíðØ9$/°åãñä[ƒÑ%¤9!"ëü¥—ˆØ΀˜~íãôꘒ›~ 7üå…ã¤NczûÄ%ôûÑò/9®/ïgç¦û8_ü½¡®…(`@ã[µcÞÍKIˆM ê ¤wÈ“_Àb©¹â¬u\o†\·ŠN—ý¼¦{}b=[?Ë¡}£ÊYnjÐú£º&ödÁ«éšØ;(…î6Šç§¬"Úê{#¤52žþÃÑ{À5Ö„VV–’óí3l]?Ž‚ü/L-@T¯¤ËYtÏ\ÛûÆK/4,œÛ‡Lç¹É+iÓªöýEKÝÓgqÅÈeDFÕL[bËaÛgSØùàE»>Z( ‰1É<ó‹×øßÛß&=¥~¿S0²×VLÛÈ#cæøÏÓ$uÏÐÑïѺmúE×ò޼ÇWë®ã‡œ¥¸œ•¦VBjÐOߌè9–áÝG³ïÄN2ÊŽÜÍ+:LAÉ)\.'áa$·Iá²Äž KÍÕ=ÆÐ5ñÒ–¦u\®ýßfÎæÔñµ5®96öïxŠc߯¤ß?Ÿp¥©Ý–¨{¸#£Ë`2º æ~æ¸=îŠlçHˆiOdW'Y­ñ ù"‡öýƒ{ŸÅ{ÁLqÑ^²>½Q?Ë"*:ÙÔpKâëRLTk:Æu:œ?Ú¥áôì÷(WŒ\ŠÕGð¾g¿G œЦWr—:úƒ¿f—4œ´¾˜š5€6µ‰ï˰1ÿ$)e4Vk<Ãü»lS6j ´9ÈÙ–+®YAq‘›jjÕô ÍOK8q ¦F  FFP##¨‘ÔÈÈjd522€@  FFP#¨‘QS+¢° /lÉâÙΖøå òLmæú ŠÛ¨ï¬ÁIEND®B`‚blinker-1.4/docs/html/_static/blinker64.png0000664000076500000240000000373212165264007020542 0ustar jekstaff00000000000000‰PNG  IHDR@@ªiqÞsBIT|dˆ pHYsÁÁE…\9tEXtSoftwarewww.inkscape.org›î<WIDATxœí›{PT×Ç?»°»°½qŒ‹e§¹YíuÔ*5³#SH‹~Š•³¾KÞä—ÅÒCɶSYvàñàð%ÄÍý~1²ûtÚ€úŽZþüï=ͳåѯ«ÍJAå% */q û-Ö¥ldmÒñõÖËæë(g#­ 9Ãb:}qs·3>r•Ããpʀ܊ó¼öq&­÷šn+ô´ñÞùœ¼z˜×Wd’1Ún›–ÆвLOWí rµZKTìˆIx//‡µ€‹àß ²é¯Ï:5ø¸Ó\Jæå\©üü‘õî–ÈåsÏ|pØ"R3N1}ΫN4 «ä;Oþ³Åì4á@´u·°õÈz*šJ†Å,æ.®çoãZÞ–A‹¯>‚Y©{H|â†Ç\Ö Û€õ…ìøÇFlØ\&ˆŽ^-G¿‡ÐÓÖ_ÖÓUEþ…õT–}Ð_¦Ry3eÆ‹¤-?CDÔj@å~YØl6Þ9»®Þ·EUËMþpqbo3¹gž¡¥!»?n K#eÙ ¦ÏÙŽ·6ȭܲ È*=Aþ‹n%Šc¤ºí6Z‰°ÈøøŽ'aÁn’–Æ`œí^YoCyïy„| $‹ÈÁÜwØšñÑ37¡Õ11æy´º`òÚ}*šK)ªÊõ¨ˆû¸Pz«ÍŠFk`êÌ—=>xa@nÅ9‹¸æÎzŠk.Ș7ŠGBG?¾¨+$aBòc6›ˆ$v`;Ì’(`–$©½¯\jG$± IjCê°Hm$?y îÁÛo»´ÝkrmD¢©³€êŠÃ4ÔœFÛ1Kbw#bo‹SY›ÍúИ]ZºGÖ€æÎ:…ê«NyœÏî ñÒz\Ä >o݈òÙ}Œ~ž_‰"دïªBlÂ+L‰û Q@Û±HˆR;fQÀ,µýo®·÷ÍwQÀÜÛÚ7ïž¿G=öaßýÈÞ @¥Ö ÕAkÄ×Á>lV f©³Y@ìiÅ[;î¡uíNÙ‘ ¤w ó£¹Ü‡Jí…FgÄ×o2Ó\Ôê‡g‡ìµíÍËØ°"ÆE6šê²°Y-ç´k€Qoâ‰8Ç3-Î`Ùcß ¶òo\>¿Ž+Ö!´y”SÖ¡uIQ«¼<*$8 œ5ó7 Ií|QðKë>#ûÓ§)+ú5fÑ3ÙdÏ3ó^ðˆ€ûض/øä7ÐúôíÜlV‘òë{¸ôÏ êïžp;¯ì„Hæã¯0m|‚Û¤Ç}“Usžïÿ¾”ä'hŒï/ëê¼MÁ¿6på Üë¸å6nÙ|‚رjF¿·‘Ä…Ïáç+÷ R ÎðøÆ’´äᓾ1¨¼¡æ4ÙŸ,£¼x7kËüå£L1¼ý£D™b]&Hœ¼˜ß=w_̓Óã­Y©ï;{*Õÿ¥šÍ]”]ÝEöé§h®ÿÌ% g…§…ÌäÝõñxì çIÕ^¬IÊä­µ‡ðytŠK¥R35î%æ-|­npÝN¡Œ¼sÏQ˜3?µ„;eû°YËX»t4–³ŒÔ©é×\!§â,ù•¹ÛZAsg6›/µ7¡L2Å2%¯E/%Ê$ÿØj(ü§³ ý8Ws~FCõÉA1I¸‘ÿ*wo">é7Œò¾—ÏÕj5 I˜˜È¶ÐÕÛ‰ÐÛŽQìö]¤Fc`î½”ïææµßÂ4}Gë5rÏ®fñ×sÑùÚ?õÈñ¸^çÏøÀ ÛB«T^ÄÄofÞÂ}h´Ãâ1ñ›e Fñý€Ð‰+HNÿhÐi°1$•)3~$»Qm@€a)K?&$"Æ@BÊÛƒ^™öàÖ JA£ǼEûéh½Ž¯_¤CmGýp*•FÇ·ê_œÅ˜J Pc(-@iŒ ´¥1f€Ò”ƘJ Pª¯ú¿ÏÿœcµÇ ¿ŽIEND®B`‚blinker-1.4/docs/html/_static/comment-bright.png0000664000076500000240000000665412165263663021676 0ustar jekstaff00000000000000‰PNG  IHDRóÿa OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-ÛbKGDÿÿÿ ½§“ pHYs  šœtIMEÚ 6 B©\<ÞIDAT8Ë…’Kh]e…¿½ÿs1mAÛÄÚ`j‚Ïh[-ˆE(FEŠÁaAœ! bI« àÈ*–BX‘"Ø4)NŠõUR‚Zˆ¹­!’×Mhj“›ssÎùÿíà–¨àãmØ‹Å^‹-\ggßÏ ÷ßì]o|ÑÑÒ¬[3±¶4§Á§6»”û©òèø¯×>zd‘¿ ]½#Œ»î8ÙþüáÇOݺ±t{5·uIÍXN!I=@Vf¾®Ÿ=v×ÀÞþ1ûº}e>;ØÉö×fvìénøvËÍÅxaÉHrÏʪJ’¦Fȹ`œÈðDò¹WZ®]ÀžSíýŸø%S)ÌWAÌœb¹ |0K=âSo7D†~\~qâÍ-ïÀËŸ\ùaóMÅ“Z,S'*æô™‘È} óF`—†ÎNnzæ674¸öËUÈç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-ÛbKGDÿÿÿ ½§“ pHYs  šœtIMEÚ!â›ÈÝ,IDAT8Ëe’_HuÇ?Ïï}ßsŽž3ÍyòË•¶¦‹U2MvQÉÖŠFÔE¬.ŠÑÍÃÅ‚­ÄŠbÑE$DD­‹ËZF5b@QÌ"š:2§›š¦¾ïû{Ÿn.êsõåçû<_ø yî?ô½m÷²ýè·wV™ê@t£R`}Z íÄÐ_£# _=œá_@ÝËý ßw^óRë®·•%6gC-έ(K>ä| $„Éï{¯}ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-ÛbKGDÿÿÿ ½§“ pHYs  šœtIMEÚ 1;ïV·¿§IDAT8Ëu‘ËkÜUÇ?ßsgœ4ÔØøhª‚`µ©ÖG1 RQ‚”îܸp%èBªø”n"‚bРXJ ‹.4V iZð##T;m£µ!4™üæžãbâP­~7÷rîù>ιbwïý†cû†; m;‡oª”ÓAÜàΆ ζZ^«/®þôä£Ãç¸|îs¯ÝÉø{Óý;†¯y¿»Rº¥ð¸Â=È9(rÉt¦Vo¼¾û¡­ûG÷Í1±wíÞÿ#_àÓ©¹›{»¿ìî*•›E&ç å!€€ˆÀƒ(—Lç–VŸßuïÀ«oœéêûÁᲵ‘DŽÀ€ P„‡²G”“4ÿçÊ Ü:&€¯ç~™êî*ݳÖreˆuá: ‚ááS­-,ßUšœ©^Ÿ’ú›E&·™JY[ÃPà!RˆìB ŖޞʖR@_ÎôÈ€dBfó”€NvHfÂ"è2ØTÊî]­ˆR‘’ ³ö j§'BàÖ1‰ddAak…/DIJD… ’D2‘ÌH&L`&L† $Ex,6‹|Ö~_\©¿Pœ‘ $™ýMH`I˜©=Ÿ @¨±Z|õÈÎÁ|ttv´gcåЕ—WTZ'¤õ3rŽÈîje"ܵx¾9ÿö›¯°W> ¹mb©Ñ|by¥ˆ•fFRx{wí%Dúõå¹Z½±€áCíÿÞüô$õwdüÀôðÖ«ÞH¦mW÷nètaµ(ŠM<~;9¿ôáž]C/ñ_¸ãåŸ;÷ÉãÕ«§æã‹Õ#Ÿ}ûÀáÉïoÿ`zS§áÚ·ù_>:;x컓§?Ÿ©yóÝ©ÿ|}æ’~ûwam-/ž®7ž=¾0úìS÷5è»ØíR翚¾P"*Ö¯ IEND®B`‚blinker-1.4/docs/html/_static/down-pressed.png0000664000076500000240000000056012165263663021357 0ustar jekstaff00000000000000‰PNG  IHDRóÿasRGB®ÎébKGDùC» pHYs × ×B(›xtIMEÚ -vF#ðIDAT8ËÍÒ!OAàïÚJ, ++@ I v¢bÿ@Wñ7F’ HNâ±ú# ‚4¡8Ì6¹4×6Tñ’MvvÞ¼7³»êœûöDs¿‡aóxâ1†U îq‚;<¦ˆÏ E¸Â-f)âºj%ßpˆo4xFà78G…>æ)â-ƒ ž ¡ÂEYm4%7YTk-¾–Q¶a–"NWAo-y†eqÒá¾,)â ÓÒYÓÑú´ptŽÐå½\hóq´Îím˜sÔz¦ìG]ÄNñ‡Òa…‡röçß¶¨s^lã vh\î2Ù%ðâßãŽ0EeRvØIEND®B`‚blinker-1.4/docs/html/_static/down.png0000664000076500000240000000055312165263663017716 0ustar jekstaff00000000000000‰PNG  IHDRóÿasRGB®ÎébKGDùC» pHYs × ×B(›xtIMEÚ"ÅíU{ëIDAT8ËÍÒ¡NCAÐóÚJ, ++@ ™4>‡¨â/ÐUü’¤^,†~T&Ô3M^^^ÛPÅM6ÙÙ¹sïÌî*¥ôí‰RJ¿‡a)e¼GñÃ*ƒœàñ¹¡èW¸Å<"®«Fò ‡øFgÜã78G…>q ƒ†ÁOI¨p‘«‰:s“õAÕjñ5GÙ†yDœ®ƒ^+y†U:ép_%G§@D|ašÕ­O“£s„Æ(ïy¡M,"â¨Íím˜sÔx:÷£.b§@D|`–V˜åÙŸÛ²”²ÜÆìиÜe²KàÅ¿Ç/êG!‚ ™IEND®B`‚blinker-1.4/docs/html/_static/file.png0000664000076500000240000000061012165263663017660 0ustar jekstaff00000000000000‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIMEÕ  )¶TIDAT8Ë­‘±JÄ@†¿Ir('[ "&xØÙYZ ‚Xø0‚!i|†_@±Ô÷•t§ÓDÄæÏ] ¹#¹Äxÿjv˜ùç› Y–ÐN‡ažE‘i«(ŠÌÄÉ™yž£µ@D¦£&±ˆ`Û6®ë–P¦Zk’$)5%"ôz½Ê.NñA#Aœba‘`Vsø¾_3ñc°,«™àä2m¼Ýñþjó [kŸìlv¹y|!IÕ´ðþyô;ÀðvÈé "Œß®°—a©?ŸAúðÄ7Œ`ô˜ñÇc^énôk?¸²Bg}»TЙ¹D#ÁÑÞ "R¹D1÷£çyüEŽRê*ŽãÝ6MJ©3þK_U«t8F~ÇIEND®B`‚blinker-1.4/docs/html/_static/flasky.css0000664000076500000240000001067712554156450020251 0ustar jekstaff00000000000000/* * flasky.css_t * ~~~~~~~~~~~~ * * Sphinx stylesheet -- flasky theme based on nature theme. * * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @import url("basic.css"); /* -- page layout ----------------------------------------------------------- */ body { font-family: 'Georgia', serif; font-size: 17px; color: #000; background: white; margin: 0; padding: 0; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 40px auto 0 auto; width: 700px; } hr { border: 1px solid #B1B4B6; } div.body { background-color: #ffffff; color: #3E4349; padding: 0 30px 30px 30px; } img.floatingflask { padding: 0 0 10px 10px; float: right; } div.footer { text-align: right; color: #888; padding: 10px; font-size: 14px; width: 650px; margin: 0 auto 40px auto; } div.footer a { color: #888; text-decoration: underline; } div.related { line-height: 32px; color: #888; } div.related ul { padding: 0 0 0 10px; } div.related a { color: #444; } /* -- body styles ----------------------------------------------------------- */ a { color: #004B6B; text-decoration: underline; } a:hover { color: #6D4100; text-decoration: underline; } div.body { padding-bottom: 40px; /* saved for footer */ } div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: 'Garamond', 'Georgia', serif; font-weight: normal; margin: 30px 0px 10px 0px; padding: 0; } div.indexwrapper h1 { text-indent: -999999px; background: url(blinker-named.png) no-repeat center center; height: 120px; } div.body h2 { font-size: 180%; } div.body h3 { font-size: 150%; } div.body h4 { font-size: 130%; } div.body h5 { font-size: 100%; } div.body h6 { font-size: 100%; } a.headerlink { color: white; padding: 0 4px; text-decoration: none; } a.headerlink:hover { color: #444; background: #eaeaea; } div.body p, div.body dd, div.body li { line-height: 1.4em; } div.admonition { background: #fafafa; margin: 20px -30px; padding: 10px 30px; border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; } div.admonition p.admonition-title { font-family: 'Garamond', 'Georgia', serif; font-weight: normal; font-size: 24px; margin: 0 0 10px 0; padding: 0; line-height: 1; } div.admonition p.last { margin-bottom: 0; } div.highlight{ background-color: white; } dt:target, .highlight { background: #FAF3E8; } div.note { background-color: #eee; border: 1px solid #ccc; } div.seealso { background-color: #ffc; border: 1px solid #ff6; } div.topic { background-color: #eee; } div.warning { background-color: #ffe4e4; border: 1px solid #f66; } p.admonition-title { display: inline; } p.admonition-title:after { content: ":"; } pre, tt { font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; font-size: 0.85em; } img.screenshot { } tt.descname, tt.descclassname { font-size: 0.95em; } tt.descname { padding-right: 0.08em; } img.screenshot { -moz-box-shadow: 2px 2px 4px #eee; -webkit-box-shadow: 2px 2px 4px #eee; box-shadow: 2px 2px 4px #eee; } table.docutils { border: 1px solid #888; -moz-box-shadow: 2px 2px 4px #eee; -webkit-box-shadow: 2px 2px 4px #eee; box-shadow: 2px 2px 4px #eee; } table.docutils td, table.docutils th { border: 1px solid #888; padding: 0.25em 0.7em; } table.field-list, table.footnote { border: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } table.footnote { margin: 15px 0; width: 100%; border: 1px solid #eee; } table.field-list th { padding: 0 0.8em 0 0; } table.field-list td { padding: 0; } table.footnote td { padding: 0.5em; } dl { margin: 0; padding: 0; } dl dd { margin-left: 30px; } pre { padding: 0; margin: 15px -30px; padding: 8px; line-height: 1.3em; padding: 7px 30px; background: #eee; border-radius: 2px; -moz-border-radius: 2px; -webkit-border-radius: 2px; } dl pre { margin-left: -60px; padding-left: 60px; } tt { background-color: #ecf0f3; color: #222; /* padding: 1px 2px; */ } tt.xref, a tt { background-color: #FBFBFB; } a:hover tt { background: #EEE; }blinker-1.4/docs/html/_static/minus.png0000664000076500000240000000030712165263663020077 0ustar jekstaff00000000000000‰PNG  IHDR &Îàq pHYs  šœtIME× <®8åtEXtCommentöÌ–¿RIDATÓczô(BÅñãÇáÒpö¿ÿ¨èˆip»‘¹P÷îÝÃc· ¸ |¶IEND®B`‚blinker-1.4/docs/html/_static/pygments.css0000664000076500000240000000753412554156450020624 0ustar jekstaff00000000000000.highlight .hll { background-color: #ffffcc } .highlight { background: #eeffcc; } .highlight .c { color: #408090; font-style: italic } /* Comment */ .highlight .err { border: 1px solid #FF0000 } /* Error */ .highlight .k { color: #007020; font-weight: bold } /* Keyword */ .highlight .o { color: #666666 } /* Operator */ .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #007020 } /* Comment.Preproc */ .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #333333 } /* Generic.Output */ .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0044DD } /* Generic.Traceback */ .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #007020 } /* Keyword.Pseudo */ .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #902000 } /* Keyword.Type */ .highlight .m { color: #208050 } /* Literal.Number */ .highlight .s { color: #4070a0 } /* Literal.String */ .highlight .na { color: #4070a0 } /* Name.Attribute */ .highlight .nb { color: #007020 } /* Name.Builtin */ .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ .highlight .no { color: #60add5 } /* Name.Constant */ .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #007020 } /* Name.Exception */ .highlight .nf { color: #06287e } /* Name.Function */ .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #bb60d5 } /* Name.Variable */ .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #208050 } /* Literal.Number.Float */ .highlight .mh { color: #208050 } /* Literal.Number.Hex */ .highlight .mi { color: #208050 } /* Literal.Number.Integer */ .highlight .mo { color: #208050 } /* Literal.Number.Oct */ .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ .highlight .sc { color: #4070a0 } /* Literal.String.Char */ .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ .highlight .sx { color: #c65d09 } /* Literal.String.Other */ .highlight .sr { color: #235388 } /* Literal.String.Regex */ .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ .highlight .ss { color: #517918 } /* Literal.String.Symbol */ .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */blinker-1.4/docs/html/_static/up-pressed.png0000664000076500000240000000056412165263663021040 0ustar jekstaff00000000000000‰PNG  IHDRóÿasRGB®ÎébKGDùC» pHYs × ×B(›xtIMEÚ ,ˆ±ZeôIDAT8ËÍ“¿jAÆ—»*ÿW¥KkåØÙ-,ÓÙBòy‘@Ò- ÛÙˆ/`cáXYh!6jÎf GrOlXvØùvæûf¸k2±!ûóp!GOOÔ² &z·®f 6|M ©~¥%‘Ï`]*ð äΛM]K Õ‰úËZĆ1Eé¹rÅ%èȶÀc…¼ØmçÍ1Æ` Index — Blinker Fork me on GitHub blinker-1.4/docs/html/index.html0000664000076500000240000014244712554156450016616 0ustar jekstaff00000000000000 Blinker Documentation — Blinker

Blinker Documentation¶

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.

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("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 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.

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("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!')]

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 '<AltProcessor %s>' % 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 "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.

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.

See the documentation of the 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

Basic Signals¶

blinker.base.ANY = ANY¶

Token for “any sender”.

blinker.base.receiver_connected = <blinker.base.Signal object at 0x1041516d0>¶

Sent by a Signal after a receiver connects.

Argument :

the Signal that was connected to

Parameters:
  • receiver_arg – the connected receiver
  • sender_arg – the sender to connect to
  • weak_arg – true if the connection to receiver_arg is a weak reference

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.

class blinker.base.Signal(doc=None)¶

A notification emitter.

Parameters:doc – optional. If provided, will be assigned to the signal’s __doc__ attribute.
ANY = ANY¶

An ANY convenience synonym, allows Signal.ANY without an additional import.

receiver_connected¶

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.

receiver_disconnected¶

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.

receivers = None¶

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, sender=ANY, weak=True)¶

Connect receiver to signal events sent by sender.

Parameters:
  • receiver – A callable. Will be invoked by send() with sender= as a single positional argument and any **kwargs that were provided to a call to send().
  • sender – Any object or ANY, defaults to ANY. Restricts notifications delivered to receiver to only those send() emissions sent by sender. If ANY, the receiver will always be notified. A receiver may be connected to multiple sender values on the same Signal through multiple calls to connect().
  • weak – If true, the Signal will hold a weakref to receiver and automatically disconnect when receiver goes out of scope or is garbage collected. Defaults to True.
connect_via(sender, weak=False)¶

Connect the decorated function as a receiver for sender.

Parameters:
  • sender – Any object or ANY. The decorated function will only receive send() emissions sent by sender. If ANY, the receiver will always be notified. A function may be decorated multiple times with differing sender values.
  • weak – If true, the Signal will hold a weakref to the decorated function and automatically disconnect when receiver goes out of scope or is garbage collected. Unlike connect(), this defaults to False.
The decorated function will be invoked by send() with
sender= as a single positional argument and any **kwargs that were provided to the call to send().

New in version 1.1.

connected_to(*args, **kwds)¶

Execute a block with the signal temporarily connected to receiver.

Parameters:
  • receiver – a receiver callable
  • sender – optional, a sender to filter on

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, sender=ANY)¶

Disconnect receiver from this signal’s events.

Parameters:
  • receiver – a previously connected callable
  • sender – a specific sender to disconnect from, or ANY to disconnect from all senders. Defaults to ANY.
has_receivers_for(sender)¶

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.

receivers_for(sender)¶

Iterate all live receivers listening for sender.

send(*sender, **kwargs)¶

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:
  • *sender – Any object or None. If omitted, synonymous with None. Only accepts one positional argument.
  • **kwargs – Data to be sent to receivers.
temporarily_connected_to(receiver, sender=ANY)¶

An alias for connected_to().

Parameters:
  • receiver – a receiver callable
  • sender – optional, a sender to filter on

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.

Named Signals¶

blinker.base.signal(name, doc=None)¶

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.

class blinker.base.NamedSignal(name, doc=None)¶

Bases: blinker.base.Signal

A named generic notification emitter.

name = None¶

The name of this signal.

class blinker.base.Namespace¶

Bases: dict

A mapping of signal names to signals.

signal(name, doc=None)¶

Return the NamedSignal name, creating it if required.

Repeated calls to this function will return the same signal object.

class blinker.base.WeakNamespace(*args, **kw)¶

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.

signal(name, doc=None)¶

Return the NamedSignal name, creating it if required.

Repeated calls to this function will return the same signal object.

Blinker Changelog¶

Version 1.4¶

Released July 23, 2015

  • 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 July 3, 2013

  • The global signal stash behind blinker.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.
  • blinker.Namespace is now built on a regular dict. Use blinker.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 October 26, 2011

  • 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 July 21, 2010

  • Added @signal.connect_via(sender) decorator
  • Added signal.connected_to shorthand name for the temporarily_connected_to context manager.

Version 1.0¶

Released March 28, 2010

  • Python 3.0 and 3.1 compatibility

Version 0.9¶

Released February 26, 2010

  • Added Signal.temporarily_connected_to context manager
  • Docs! Sphinx docs, project web site.

Version 0.8¶

Released February 14, 2010

  • 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.)
Fork me on GitHub
blinker-1.4/docs/html/objects.inv0000664000076500000240000000061412554156450016755 0ustar jekstaff00000000000000# Sphinx inventory version 2 # Project: Blinker # Version: 1.4 # The remainder of this file is compressed using zlib. xÚ¥S»nÃ0 ÜóšÕ²fëØ¡AE&ƒ–[ˆ†HÍß'®Ýº) Tr7ºãŽ” Úx‚Xï-ÔhE±ݵpÓ…lÙÙ'ñtoQ¼ ¥Mƒ~ôU#”¢jcm‚•#à ÜÔÖø3FY¡,MãÁJB¯ï~ö¹ ZìÄ­(9*xŠQWr¹Œ® ¢±×ê?}"*4Œ49šºgüƒûŽp>€Cê@¡¤ÏNk…g÷9u]~ + DY¡WÉ–ŸÇ«!Kdu&ÚЪX~µ:…˜kbzlÎ$VíÁ ?õ^± >}æÒßÏ+‚™sÍL%uÉ~F*iù+$l[¶ÒC T%ïÈ ô8ì#blinker-1.4/docs/html/search.html0000664000076500000240000000466512554156450016753 0ustar jekstaff00000000000000 Search — Blinker

Search

Please activate JavaScript to enable the search functionality.

From here you can search these documents. Enter your search words into the box below and click "search". Note that the search function will automatically search for all of the words. Pages containing fewer words won't appear in the result list.

Fork me on GitHub blinker-1.4/docs/source/0000775000076500000240000000000012554156573015147 5ustar jekstaff00000000000000blinker-1.4/docs/source/_themes/0000775000076500000240000000000012554156573016573 5ustar jekstaff00000000000000blinker-1.4/docs/source/_themes/flask_theme_support.py0000664000076500000240000001141312165263275023217 0ustar jekstaff00000000000000# flasky extensions. flasky pygments style based on tango style from pygments.style import Style from pygments.token import Keyword, Name, Comment, String, Error, \ Number, Operator, Generic, Whitespace, Punctuation, Other, Literal class FlaskyStyle(Style): background_color = "#f8f8f8" default_style = "" styles = { # No corresponding class for the following: #Text: "", # class: '' Whitespace: "underline #f8f8f8", # class: 'w' Error: "#a40000 border:#ef2929", # class: 'err' Other: "#000000", # class 'x' Comment: "italic #8f5902", # class: 'c' Comment.Preproc: "noitalic", # class: 'cp' Keyword: "bold #004461", # class: 'k' Keyword.Constant: "bold #004461", # class: 'kc' Keyword.Declaration: "bold #004461", # class: 'kd' Keyword.Namespace: "bold #004461", # class: 'kn' Keyword.Pseudo: "bold #004461", # class: 'kp' Keyword.Reserved: "bold #004461", # class: 'kr' Keyword.Type: "bold #004461", # class: 'kt' Operator: "#582800", # class: 'o' Operator.Word: "bold #004461", # class: 'ow' - like keywords Punctuation: "bold #000000", # class: 'p' # because special names such as Name.Class, Name.Function, etc. # are not recognized as such later in the parsing, we choose them # to look the same as ordinary variables. Name: "#000000", # class: 'n' Name.Attribute: "#c4a000", # class: 'na' - to be revised Name.Builtin: "#004461", # class: 'nb' Name.Builtin.Pseudo: "#3465a4", # class: 'bp' Name.Class: "#000000", # class: 'nc' - to be revised Name.Constant: "#000000", # class: 'no' - to be revised Name.Decorator: "#888", # class: 'nd' - to be revised Name.Entity: "#ce5c00", # class: 'ni' Name.Exception: "bold #cc0000", # class: 'ne' Name.Function: "#000000", # class: 'nf' Name.Property: "#000000", # class: 'py' Name.Label: "#f57900", # class: 'nl' Name.Namespace: "#000000", # class: 'nn' - to be revised Name.Other: "#000000", # class: 'nx' Name.Tag: "bold #004461", # class: 'nt' - like a keyword Name.Variable: "#000000", # class: 'nv' - to be revised Name.Variable.Class: "#000000", # class: 'vc' - to be revised Name.Variable.Global: "#000000", # class: 'vg' - to be revised Name.Variable.Instance: "#000000", # class: 'vi' - to be revised Number: "#990000", # class: 'm' Literal: "#000000", # class: 'l' Literal.Date: "#000000", # class: 'ld' String: "#4e9a06", # class: 's' String.Backtick: "#4e9a06", # class: 'sb' String.Char: "#4e9a06", # class: 'sc' String.Doc: "italic #8f5902", # class: 'sd' - like a comment String.Double: "#4e9a06", # class: 's2' String.Escape: "#4e9a06", # class: 'se' String.Heredoc: "#4e9a06", # class: 'sh' String.Interpol: "#4e9a06", # class: 'si' String.Other: "#4e9a06", # class: 'sx' String.Regex: "#4e9a06", # class: 'sr' String.Single: "#4e9a06", # class: 's1' String.Symbol: "#4e9a06", # class: 'ss' Generic: "#000000", # class: 'g' Generic.Deleted: "#a40000", # class: 'gd' Generic.Emph: "italic #000000", # class: 'ge' Generic.Error: "#ef2929", # class: 'gr' Generic.Heading: "bold #000080", # class: 'gh' Generic.Inserted: "#00A000", # class: 'gi' Generic.Output: "#888", # class: 'go' Generic.Prompt: "#745334", # class: 'gp' Generic.Strong: "bold #000000", # class: 'gs' Generic.Subheading: "bold #800080", # class: 'gu' Generic.Traceback: "bold #a40000", # class: 'gt' } blinker-1.4/docs/source/conf.py0000664000076500000240000001032612510555122016431 0ustar jekstaff00000000000000# -*- coding: utf-8 -*- # # Blinker documentation build configuration file, created by # sphinx-quickstart on Mon Feb 15 10:54:13 2010. # # This file is execfile()d with the current directory set to its containing # dir. import os from os import path import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.append(os.path.abspath('../../')) sys.path.append(os.path.abspath('_themes')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Blinker' copyright = u'2010, Jason Kirtland' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = 'tip' # The full version, including alpha/beta/rc tags. release = 'tip' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] autoclass_content = "both" autodoc_member_order = "groupwise" import sphinx.ext.autodoc sphinx.ext.autodoc.AttributeDocumenter.member_order = 25 sphinx.ext.autodoc.InstanceAttributeDocumenter.member_order = 26 # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- html_static_path = ['_static'] html_theme_path = ['_themes'] html_theme = 'flask_small' html_theme_options = { 'index_logo': 'blinker-named.png', 'github_fork': 'jek/blinker' } html_title = 'Blinker' # Output file base name for HTML help builder. htmlhelp_basename = 'Blinkerdoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'Blinker.tex', u'Blinker Documentation', u'Jason Kirtland', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True blinker-1.4/docs/source/index.rst0000664000076500000240000001751512510555122017002 0ustar jekstaff00000000000000Blinker Documentation ===================== 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. Decoupling With Named Signals ----------------------------- Named signals are created with :func:`signal`: .. doctest:: >>> 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. .. doctest:: >>> def subscriber(sender): ... print("Got a signal sent by %r" % sender) ... >>> ready = signal('ready') >>> ready.connect(subscriber) 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. .. doctest:: >>> 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 '' % self.name ... >>> processor_a = Processor('a') >>> processor_a.go() Got a signal sent by 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: .. doctest:: >>> 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) This function has been subscribed to ``ready`` but only when sent by ``processor_b``: .. doctest:: >>> processor_a.go() Got a signal sent by Processing. >>> processor_b.go() Got a signal sent by 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: .. doctest:: >>> 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: .. doctest:: >>> result [(, '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: .. doctest:: >>> 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 '' % 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: .. doctest:: >>> 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: .. doctest:: >>> 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: .. doctest:: >>> 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: .. doctest:: >>> 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 .. include:: ../../CHANGES blinker-1.4/docs/text/0000775000076500000240000000000012554156573014633 5ustar jekstaff00000000000000blinker-1.4/docs/text/index.txt0000664000076500000240000004016012554156450016476 0ustar jekstaff00000000000000 Blinker Documentation ********************* 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. 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("Got a signal sent by %r" % sender) ... >>> ready = signal('ready') >>> ready.connect(subscriber) 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 '' % self.name ... >>> processor_a = Processor('a') >>> processor_a.go() Got a signal sent by 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) This function has been subscribed to "ready" but only when sent by "processor_b": >>> processor_a.go() Got a signal sent by Processing. >>> processor_b.go() Got a signal sent by 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("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 [(, 'received!')] 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 '' % 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 "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. 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. See the documentation of the "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 Basic Signals ------------- blinker.base.ANY = ANY Token for "any sender". blinker.base.receiver_connected = Sent by a "Signal" after a receiver connects. Argument : the Signal that was connected to Parameters: * **receiver_arg** -- the connected receiver * **sender_arg** -- the sender to connect to * **weak_arg** -- true if the connection to receiver_arg is a weak reference 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. class class blinker.base.Signal(doc=None) A notification emitter. Parameters: **doc** -- optional. If provided, will be assigned to the signal's __doc__ attribute. ANY = ANY An "ANY" convenience synonym, allows "Signal.ANY" without an additional import. receiver_connected 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. receiver_disconnected 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. receivers = None 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, sender=ANY, weak=True) Connect *receiver* to signal events sent by *sender*. Parameters: * **receiver** -- A callable. Will be invoked by "send()" with *sender=* as a single positional argument and any **kwargs that were provided to a call to "send()". * **sender** -- Any object or "ANY", defaults to "ANY". Restricts notifications delivered to *receiver* to only those "send()" emissions sent by *sender*. If "ANY", the receiver will always be notified. A *receiver* may be connected to multiple *sender* values on the same Signal through multiple calls to "connect()". * **weak** -- If true, the Signal will hold a weakref to *receiver* and automatically disconnect when *receiver* goes out of scope or is garbage collected. Defaults to True. connect_via(sender, weak=False) Connect the decorated function as a receiver for *sender*. Parameters: * **sender** -- Any object or "ANY". The decorated function will only receive "send()" emissions sent by *sender*. If "ANY", the receiver will always be notified. A function may be decorated multiple times with differing *sender* values. * **weak** -- If true, the Signal will hold a weakref to the decorated function and automatically disconnect when *receiver* goes out of scope or is garbage collected. Unlike "connect()", this defaults to False. The decorated function will be invoked by "send()" with *sender=* as a single positional argument and any **kwargs that were provided to the call to "send()". New in version 1.1. connected_to(*args, **kwds) Execute a block with the signal temporarily connected to *receiver*. Parameters: * **receiver** -- a receiver callable * **sender** -- optional, a sender to filter on 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, sender=ANY) Disconnect *receiver* from this signal's events. Parameters: * **receiver** -- a previously "connected" callable * **sender** -- a specific sender to disconnect from, or "ANY" to disconnect from all senders. Defaults to "ANY". has_receivers_for(sender) 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. receivers_for(sender) Iterate all live receivers listening for *sender*. send(*sender, **kwargs) 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: * ***sender** -- Any object or "None". If omitted, synonymous with "None". Only accepts one positional argument. * ****kwargs** -- Data to be sent to receivers. temporarily_connected_to(receiver, sender=ANY) An alias for "connected_to()". Parameters: * **receiver** -- a receiver callable * **sender** -- optional, a sender to filter on 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. Named Signals ------------- blinker.base.signal(name, doc=None) 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". class class blinker.base.NamedSignal(name, doc=None) Bases: "blinker.base.Signal" A named generic notification emitter. name = None The name of this signal. class class blinker.base.Namespace Bases: "dict" A mapping of signal names to signals. signal(name, doc=None) Return the "NamedSignal" *name*, creating it if required. Repeated calls to this function will return the same signal object. class class blinker.base.WeakNamespace(*args, **kw) 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. signal(name, doc=None) Return the "NamedSignal" *name*, creating it if required. Repeated calls to this function will return the same signal object. Blinker Changelog ***************** Version 1.4 =========== Released July 23, 2015 * 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 July 3, 2013 * The global signal stash behind blinker.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. * blinker.Namespace is now built on a regular dict. Use blinker.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 October 26, 2011 * 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 July 21, 2010 * Added "@signal.connect_via(sender)" decorator * Added "signal.connected_to" shorthand name for the "temporarily_connected_to" context manager. Version 1.0 =========== Released March 28, 2010 * Python 3.0 and 3.1 compatibility Version 0.9 =========== Released February 26, 2010 * Added "Signal.temporarily_connected_to" context manager * Docs! Sphinx docs, project web site. Version 0.8 =========== Released February 14, 2010 * 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.) blinker-1.4/LICENSE0000664000076500000240000000210612165000330013674 0ustar jekstaff00000000000000Copyright (c) The Blinker authors and contributors 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. blinker-1.4/MANIFEST.in0000664000076500000240000000034012554150522014436 0ustar jekstaff00000000000000include LICENSE include README.md include AUTHORS include CHANGES recursive-include tests *py recursive-include docs/source *rst *py recursive-include docs/text *txt recursive-include docs/html *html *txt *png *css *js *inv blinker-1.4/PKG-INFO0000664000076500000240000000747512554156573014031 0ustar jekstaff00000000000000Metadata-Version: 1.0 Name: blinker Version: 1.4 Summary: Fast, simple object-to-object and broadcast signaling Home-page: http://pythonhosted.org/blinker/ Author: Jason Kirtland Author-email: jek@discorporate.us License: MIT License Description: [![Build Status](https://travis-ci.org/jek/blinker.svg?branch=master)](https://travis-ci.org/jek/blinker) # Blinker Blinker provides a fast dispatching system that allows any number of interested parties to subscribe to events, or "signals". Signal receivers can subscribe to specific senders or receive signals sent by any sender. >>> from blinker import signal >>> started = signal('round-started') >>> def each(round): ... print "Round %s!" % round ... >>> started.connect(each) >>> def round_two(round): ... print "This is round two." ... >>> started.connect(round_two, sender=2) >>> for round in range(1, 4): ... started.send(round) ... Round 1! Round 2! This is round two. Round 3! See the [Blinker documentation](https://pythonhosted.org/blinker/) for more information. ## Requirements Blinker requires Python 2.4 or higher, Python 3.0 or higher, or Jython 2.5 or higher. ## Changelog Summary 1.3 (July 3, 2013) - The global signal stash behind blinker.signal() is now backed by a regular name-to-Signal dictionary. Previously, weak references were held in the mapping and ephemeral usage in code like ``signal('foo').connect(...)`` could have surprising program behavior depending on import order of modules. - blinker.Namespace is now built on a regular dict. Use blinker.WeakNamespace for the older, weak-referencing behavior. - Signal.connect('text-sender') uses an alternate hashing strategy to avoid sharp edges in text identity. 1.2 (October 26, 2011) - 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!) 1.1 (July 21, 2010) - Added ``@signal.connect_via(sender)`` decorator - Added ``signal.connected_to`` shorthand name for the ``temporarily_connected_to`` context manager. 1.0 (March 28, 2010) - Python 3.x compatibility 0.9 (February 26, 2010) - Sphinx docs, project website - Added ``with a_signal.temporarily_connected_to(receiver): ...`` support Keywords: signal emit events broadcast Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.4 Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.0 Classifier: Programming Language :: Python :: 3.1 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Utilities blinker-1.4/README.md0000664000076500000240000000414312554150522014164 0ustar jekstaff00000000000000[![Build Status](https://travis-ci.org/jek/blinker.svg?branch=master)](https://travis-ci.org/jek/blinker) # Blinker Blinker provides a fast dispatching system that allows any number of interested parties to subscribe to events, or "signals". Signal receivers can subscribe to specific senders or receive signals sent by any sender. >>> from blinker import signal >>> started = signal('round-started') >>> def each(round): ... print "Round %s!" % round ... >>> started.connect(each) >>> def round_two(round): ... print "This is round two." ... >>> started.connect(round_two, sender=2) >>> for round in range(1, 4): ... started.send(round) ... Round 1! Round 2! This is round two. Round 3! See the [Blinker documentation](https://pythonhosted.org/blinker/) for more information. ## Requirements Blinker requires Python 2.4 or higher, Python 3.0 or higher, or Jython 2.5 or higher. ## Changelog Summary 1.3 (July 3, 2013) - The global signal stash behind blinker.signal() is now backed by a regular name-to-Signal dictionary. Previously, weak references were held in the mapping and ephemeral usage in code like ``signal('foo').connect(...)`` could have surprising program behavior depending on import order of modules. - blinker.Namespace is now built on a regular dict. Use blinker.WeakNamespace for the older, weak-referencing behavior. - Signal.connect('text-sender') uses an alternate hashing strategy to avoid sharp edges in text identity. 1.2 (October 26, 2011) - 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!) 1.1 (July 21, 2010) - Added ``@signal.connect_via(sender)`` decorator - Added ``signal.connected_to`` shorthand name for the ``temporarily_connected_to`` context manager. 1.0 (March 28, 2010) - Python 3.x compatibility 0.9 (February 26, 2010) - Sphinx docs, project website - Added ``with a_signal.temporarily_connected_to(receiver): ...`` support blinker-1.4/setup.cfg0000664000076500000240000000014112554156573014534 0ustar jekstaff00000000000000[upload_docs] upload-dir = docs/html [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 blinker-1.4/setup.py0000664000076500000240000000270412554150522014420 0ustar jekstaff00000000000000try: from setuptools import setup except ImportError: from distutils.core import setup readme = open('README.md').read() import blinker version = blinker.__version__ setup(name="blinker", version=version, packages=['blinker'], author='Jason Kirtland', author_email='jek@discorporate.us', description='Fast, simple object-to-object and broadcast signaling', keywords='signal emit events broadcast', long_description=readme, license='MIT License', url='http://pythonhosted.org/blinker/', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.4', 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.0', 'Programming Language :: Python :: 3.1', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Topic :: Software Development :: Libraries', 'Topic :: Utilities', ], ) blinker-1.4/tests/0000775000076500000240000000000012554156573014061 5ustar jekstaff00000000000000blinker-1.4/tests/test_context.py0000664000076500000240000000206512165000330017132 0ustar jekstaff00000000000000from __future__ import with_statement from blinker import Signal def test_temp_connection(): sig = Signal() canary = [] receiver = lambda sender: canary.append(sender) sig.send(1) with sig.connected_to(receiver): sig.send(2) sig.send(3) assert canary == [2] assert not sig.receivers def test_temp_connection_for_sender(): sig = Signal() canary = [] receiver = lambda sender: canary.append(sender) with sig.connected_to(receiver, sender=2): sig.send(1) sig.send(2) assert canary == [2] assert not sig.receivers def test_temp_connection_failure(): sig = Signal() canary = [] receiver = lambda sender: canary.append(sender) class Failure(Exception): pass try: sig.send(1) with sig.connected_to(receiver): sig.send(2) raise Failure sig.send(3) except Failure: pass else: raise AssertionError("Context manager did not propagate.") assert canary == [2] assert not sig.receivers blinker-1.4/tests/test_saferef.py0000664000076500000240000000726312165000330017066 0ustar jekstaff00000000000000# extracted from Louie, http://pylouie.org/ # updated for Python 3 # # Copyright (c) 2006 Patrick K. O'Brien, Mike C. Fletcher, # Matthew R. Scott # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # # * Neither the name of the nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # import unittest from blinker._saferef import safe_ref class _Sample1(object): def x(self): pass def _sample2(obj): pass class _Sample3(object): def __call__(self, obj): pass class TestSaferef(unittest.TestCase): # XXX: The original tests had a test for closure, and it had an # off-by-one problem, perhaps due to scope issues. It has been # removed from this test suite. def setUp(self): ts = [] ss = [] for x in range(100): t = _Sample1() ts.append(t) s = safe_ref(t.x, self._closure) ss.append(s) ts.append(_sample2) ss.append(safe_ref(_sample2, self._closure)) for x in range(30): t = _Sample3() ts.append(t) s = safe_ref(t, self._closure) ss.append(s) self.ts = ts self.ss = ss self.closure_count = 0 def tearDown(self): if hasattr(self, 'ts'): del self.ts if hasattr(self, 'ss'): del self.ss def test_In(self): """Test the `in` operator for safe references (cmp)""" for t in self.ts[:50]: assert safe_ref(t.x) in self.ss def test_Valid(self): """Test that the references are valid (return instance methods)""" for s in self.ss: assert s() def test_ShortCircuit(self): """Test that creation short-circuits to reuse existing references""" sd = {} for s in self.ss: sd[s] = 1 for t in self.ts: if hasattr(t, 'x'): assert safe_ref(t.x) in sd else: assert safe_ref(t) in sd def test_Representation(self): """Test that the reference object's representation works XXX Doesn't currently check the results, just that no error is raised """ repr(self.ss[-1]) def _closure(self, ref): """Dumb utility mechanism to increment deletion counter""" self.closure_count += 1 blinker-1.4/tests/test_signals.py0000664000076500000240000002747412554150522017134 0ustar jekstaff00000000000000import gc import sys import time import blinker from nose.tools import assert_raises jython = sys.platform.startswith('java') pypy = hasattr(sys, 'pypy_version_info') def collect_acyclic_refs(): # cpython releases these immediately without a collection if jython or pypy: gc.collect() if jython: time.sleep(0.1) class Sentinel(list): """A signal receipt accumulator.""" def make_receiver(self, key): """Return a generic signal receiver function logging as *key* When connected to a signal, appends (key, sender, kw) to the Sentinel. """ def receiver(*sentby, **kw): self.append((key, sentby[0], kw)) receiver.func_name = 'receiver_%s' % key return receiver def test_meta_connect(): sentinel = [] def meta_received(sender, **kw): sentinel.append(dict(kw, sender=sender)) assert not blinker.receiver_connected.receivers blinker.receiver_connected.connect(meta_received) assert not sentinel def receiver(sender, **kw): pass sig = blinker.Signal() sig.connect(receiver) assert sentinel == [dict(sender=sig, receiver_arg=receiver, sender_arg=blinker.ANY, weak_arg=True)] blinker.receiver_connected._clear_state() def _test_signal_signals(sender): sentinel = Sentinel() sig = blinker.Signal() connected = sentinel.make_receiver('receiver_connected') disconnected = sentinel.make_receiver('receiver_disconnected') receiver1 = sentinel.make_receiver('receiver1') receiver2 = sentinel.make_receiver('receiver2') assert not sig.receiver_connected.receivers assert not sig.receiver_disconnected.receivers sig.receiver_connected.connect(connected) sig.receiver_disconnected.connect(disconnected) assert sig.receiver_connected.receivers assert not sentinel for receiver, weak in [(receiver1, True), (receiver2, False)]: sig.connect(receiver, sender=sender, weak=weak) expected = ('receiver_connected', sig, dict(receiver=receiver, sender=sender, weak=weak)) assert sentinel[-1] == expected # disconnect from explicit sender sig.disconnect(receiver1, sender=sender) expected = ('receiver_disconnected', sig, dict(receiver=receiver1, sender=sender)) assert sentinel[-1] == expected # disconnect from ANY and all senders (implicit disconnect signature) sig.disconnect(receiver2) assert sentinel[-1] == ('receiver_disconnected', sig, dict(receiver=receiver2, sender=blinker.ANY)) def test_signal_signals_any_sender(): _test_signal_signals(blinker.ANY) def test_signal_signals_strong_sender(): _test_signal_signals("squiznart") def test_signal_weak_receiver_vanishes(): # non-edge-case path for weak receivers is exercised in the ANY sender # test above. sentinel = Sentinel() sig = blinker.Signal() connected = sentinel.make_receiver('receiver_connected') disconnected = sentinel.make_receiver('receiver_disconnected') receiver1 = sentinel.make_receiver('receiver1') receiver2 = sentinel.make_receiver('receiver2') sig.receiver_connected.connect(connected) sig.receiver_disconnected.connect(disconnected) # explicit disconnect on a weak does emit the signal sig.connect(receiver1, weak=True) sig.disconnect(receiver1) assert len(sentinel) == 2 assert sentinel[-1][2]['receiver'] is receiver1 del sentinel[:] sig.connect(receiver2, weak=True) assert len(sentinel) == 1 del sentinel[:] # holds a ref to receiver2 del receiver2 collect_acyclic_refs() # no disconnect signal is fired assert len(sentinel) == 0 # and everything really is disconnected sig.send('abc') assert len(sentinel) == 0 def test_signal_signals_weak_sender(): sentinel = Sentinel() sig = blinker.Signal() connected = sentinel.make_receiver('receiver_connected') disconnected = sentinel.make_receiver('receiver_disconnected') receiver1 = sentinel.make_receiver('receiver1') receiver2 = sentinel.make_receiver('receiver2') class Sender(object): """A weakref-able object.""" sig.receiver_connected.connect(connected) sig.receiver_disconnected.connect(disconnected) sender1 = Sender() sig.connect(receiver1, sender=sender1, weak=False) # regular disconnect of weak-able sender works fine sig.disconnect(receiver1, sender=sender1) assert len(sentinel) == 2 del sentinel[:] sender2 = Sender() sig.connect(receiver2, sender=sender2, weak=False) # force sender2 to go out of scope del sender2 collect_acyclic_refs() # no disconnect signal is fired assert len(sentinel) == 1 # and everything really is disconnected sig.send('abc') assert len(sentinel) == 1 def test_empty_bucket_growth(): sentinel = Sentinel() sig = blinker.Signal() senders = lambda: (len(sig._by_sender), sum(len(i) for i in sig._by_sender.values())) receivers = lambda: (len(sig._by_receiver), sum(len(i) for i in sig._by_receiver.values())) receiver1 = sentinel.make_receiver('receiver1') receiver2 = sentinel.make_receiver('receiver2') class Sender(object): """A weakref-able object.""" sender = Sender() sig.connect(receiver1, sender=sender) sig.connect(receiver2, sender=sender) assert senders() == (1, 2) assert receivers() == (2, 2) sig.disconnect(receiver1, sender=sender) assert senders() == (1, 1) assert receivers() == (2, 1) sig.disconnect(receiver2, sender=sender) assert senders() == (1, 0) assert receivers() == (2, 0) sig._cleanup_bookkeeping() assert senders() == (0, 0) assert receivers() == (0, 0) def test_meta_connect_failure(): def meta_received(sender, **kw): raise TypeError('boom') assert not blinker.receiver_connected.receivers blinker.receiver_connected.connect(meta_received) def receiver(sender, **kw): pass sig = blinker.Signal() assert_raises(TypeError, sig.connect, receiver) assert not sig.receivers assert not sig._by_receiver assert sig._by_sender == {blinker.base.ANY_ID: set()} blinker.receiver_connected._clear_state() def test_weak_namespace(): ns = blinker.WeakNamespace() assert not ns s1 = ns.signal('abc') assert s1 is ns.signal('abc') assert s1 is not ns.signal('def') assert 'abc' in ns collect_acyclic_refs() # weak by default, already out of scope assert 'def' not in ns del s1 collect_acyclic_refs() assert 'abc' not in ns def test_namespace(): ns = blinker.Namespace() assert not ns s1 = ns.signal('abc') assert s1 is ns.signal('abc') assert s1 is not ns.signal('def') assert 'abc' in ns del s1 collect_acyclic_refs() assert 'def' in ns assert 'abc' in ns def test_weak_receiver(): sentinel = [] def received(sender, **kw): sentinel.append(kw) sig = blinker.Signal() # XXX: weirdly, under jython an explicit weak=True causes this test # to fail, leaking a strong ref to the receiver somewhere. # http://bugs.jython.org/issue1586 if jython: sig.connect(received) # weak=True by default. else: sig.connect(received, weak=True) del received collect_acyclic_refs() assert not sentinel sig.send() assert not sentinel assert not sig.receivers values_are_empty_sets_(sig._by_receiver) values_are_empty_sets_(sig._by_sender) def test_strong_receiver(): sentinel = [] def received(sender): sentinel.append(sender) fn_id = id(received) sig = blinker.Signal() sig.connect(received, weak=False) del received collect_acyclic_refs() assert not sentinel sig.send() assert sentinel assert [id(fn) for fn in sig.receivers.values()] == [fn_id] def test_instancemethod_receiver(): sentinel = [] class Receiver(object): def __init__(self, bucket): self.bucket = bucket def received(self, sender): self.bucket.append(sender) receiver = Receiver(sentinel) sig = blinker.Signal() sig.connect(receiver.received) assert not sentinel sig.send() assert sentinel del receiver collect_acyclic_refs() sig.send() assert len(sentinel) == 1 def test_filtered_receiver(): sentinel = [] def received(sender): sentinel.append(sender) sig = blinker.Signal() sig.connect(received, 123) assert not sentinel sig.send() assert not sentinel sig.send(123) assert sentinel == [123] sig.send() assert sentinel == [123] sig.disconnect(received, 123) sig.send(123) assert sentinel == [123] sig.connect(received, 123) sig.send(123) assert sentinel == [123, 123] sig.disconnect(received) sig.send(123) assert sentinel == [123, 123] def test_filtered_receiver_weakref(): sentinel = [] def received(sender): sentinel.append(sender) class Object(object): pass obj = Object() sig = blinker.Signal() sig.connect(received, obj) assert not sentinel sig.send(obj) assert sentinel == [obj] del sentinel[:] del obj collect_acyclic_refs() # general index isn't cleaned up assert sig.receivers # but receiver/sender pairs are values_are_empty_sets_(sig._by_receiver) values_are_empty_sets_(sig._by_sender) def test_decorated_receiver(): sentinel = [] class Object(object): pass obj = Object() sig = blinker.Signal() @sig.connect_via(obj) def receiver(sender, **kw): sentinel.append(kw) assert not sentinel sig.send() assert not sentinel sig.send(1) assert not sentinel sig.send(obj) assert sig.receivers del receiver collect_acyclic_refs() assert sig.receivers def test_no_double_send(): sentinel = [] def received(sender): sentinel.append(sender) sig = blinker.Signal() sig.connect(received, 123) sig.connect(received) assert not sentinel sig.send() assert sentinel == [None] sig.send(123) assert sentinel == [None, 123] sig.send() assert sentinel == [None, 123, None] def test_has_receivers(): received = lambda sender: None sig = blinker.Signal() assert not sig.has_receivers_for(None) assert not sig.has_receivers_for(blinker.ANY) sig.connect(received, 'xyz') assert not sig.has_receivers_for(None) assert not sig.has_receivers_for(blinker.ANY) assert sig.has_receivers_for('xyz') class Object(object): pass o = Object() sig.connect(received, o) assert sig.has_receivers_for(o) del received collect_acyclic_refs() assert not sig.has_receivers_for('xyz') assert list(sig.receivers_for('xyz')) == [] assert list(sig.receivers_for(o)) == [] sig.connect(lambda sender: None, weak=False) assert sig.has_receivers_for('xyz') assert sig.has_receivers_for(o) assert sig.has_receivers_for(None) assert sig.has_receivers_for(blinker.ANY) assert sig.has_receivers_for('xyz') def test_instance_doc(): sig = blinker.Signal(doc='x') assert sig.__doc__ == 'x' sig = blinker.Signal('x') assert sig.__doc__ == 'x' def test_named_blinker(): sig = blinker.NamedSignal('squiznart') assert 'squiznart' in repr(sig) def values_are_empty_sets_(dictionary): for val in dictionary.values(): assert val == set() if sys.version_info < (2, 5): def test_context_manager_warning(): sig = blinker.Signal() receiver = lambda sender: None assert_raises(RuntimeError, sig.connected_to, receiver) blinker-1.4/tests/test_utilities.py0000664000076500000240000000071212165000330017456 0ustar jekstaff00000000000000import pickle from blinker._utilities import symbol def test_symbols(): foo = symbol('foo') assert foo.name == 'foo' assert foo is symbol('foo') bar = symbol('bar') assert foo is not bar assert foo != bar assert not foo == bar assert repr(foo) == 'foo' def test_pickled_symbols(): foo = symbol('foo') for protocol in 0, 1, 2: roundtrip = pickle.loads(pickle.dumps(foo)) assert roundtrip is foo