diff options
author | Youri Ackx <varia@ackx.net> | 2020-10-02 16:57:28 +0200 |
---|---|---|
committer | Youri Ackx <varia@ackx.net> | 2020-10-02 16:57:28 +0200 |
commit | 74df4c141934c01b3b2f7e27f8438b07b1c12b63 (patch) | |
tree | 2692b8685d133018c07daa5d2cdfb877f7464e8b | |
parent | d841e82241a209573a1da638aa64921c7e65b811 (diff) | |
parent | bf5f8f0968552670744fee7960419b8aea27c5b5 (diff) | |
download | blinker-74df4c141934c01b3b2f7e27f8438b07b1c12b63.tar.gz |
Merge branch 'master' into feature/send-async
# Conflicts:
# CHANGES
-rw-r--r-- | .travis.yml | 33 | ||||
-rw-r--r-- | CHANGES | 5 | ||||
-rw-r--r-- | README.md | 42 | ||||
-rw-r--r-- | blinker/_saferef.py | 2 | ||||
-rw-r--r-- | blinker/_utilities.py | 14 | ||||
-rw-r--r-- | blinker/base.py | 27 | ||||
-rw-r--r-- | docs/source/index.rst | 4 | ||||
-rw-r--r-- | setup.cfg | 3 | ||||
-rw-r--r-- | setup.py | 15 | ||||
-rw-r--r-- | tests/test_signals.py | 21 | ||||
-rw-r--r-- | tox.ini | 2 |
11 files changed, 92 insertions, 76 deletions
diff --git a/.travis.yml b/.travis.yml index 952e3d6..995721e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,31 @@ language: python -python: - - "2.6" - - "2.7" - - "3.2" - - "3.3" - - "3.4" + +matrix: + include: + - python: 2.7 + os: linux + dist: trusty + env: TOXENV=py27 + - python: pypy + os: linux + dist: trusty + env: TOXENV=pypy + - python: 3.5 + os: linux + dist: trusty + env: TOXENV=py35 + - python: 3.6 + os: linux + dist: trusty + env: TOXENV=py36 + - python: 3.7 + os: linux + dist: xenial + env: TOXENV=py37 + - python: 3.8 + os: linux + dist: xenial + env: TOXENV=py38 # command to install dependencies install: @@ -5,6 +5,11 @@ Blinker Changelog Version 1.5dev -------------- +Not yet released. + +- Verified Python 3.7 support (no changes needed). +- Verified Python 3.6 support (no changes needed). +- Verified Python 3.5 support (no changes needed). - Added Signal.send_async, dispatching to an arbitrary mix of connected coroutines and receiver functions. @@ -9,31 +9,33 @@ 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! +```python +>>> 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. +Blinker requires Python 2.7, Python 3.4 or higher, or Jython 2.7 or higher. ## Changelog Summary diff --git a/blinker/_saferef.py b/blinker/_saferef.py index 269e362..081173d 100644 --- a/blinker/_saferef.py +++ b/blinker/_saferef.py @@ -198,7 +198,7 @@ class BoundMethodWeakref(object): def __str__(self): """Give a friendly representation of the object.""" - return "%s(%s.%s)" % ( + return "{}({}.{})".format( self.__class__.__name__, self.self_name, self.func_name, diff --git a/blinker/_utilities.py b/blinker/_utilities.py index 056270d..133c57a 100644 --- a/blinker/_utilities.py +++ b/blinker/_utilities.py @@ -36,7 +36,7 @@ except: def __reduce__(self): if self.default_factory is None: - args = tuple() + args = () else: args = self.default_factory, return type(self), args, None, None, self.items() @@ -53,20 +53,10 @@ except: copy.deepcopy(self.items())) def __repr__(self): - return 'defaultdict(%s, %s)' % (self.default_factory, + return 'defaultdict({}, {})'.format(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): diff --git a/blinker/base.py b/blinker/base.py index 67aa41d..3ff0984 100644 --- a/blinker/base.py +++ b/blinker/base.py @@ -8,12 +8,12 @@ each manages its own receivers and message emission. The :func:`signal` function provides singleton behavior for named signals. """ +from contextlib import contextmanager from warnings import warn from weakref import WeakValueDictionary from blinker._utilities import ( WeakTypes, - contextmanager, defaultdict, hashable_identity, lazy_property, @@ -250,6 +250,14 @@ class Signal(object): :param \*\*kwargs: Data to be sent to receivers. """ + if not self.receivers: + # Ensure correct signature even on no-op sends, disable with -O + # for lowest possible cost. + if __debug__ and sender and len(sender) > 1: + raise TypeError('send() accepts only one positional ' + 'argument, %s given' % len(sender)) + return [] + # 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. @@ -260,11 +268,8 @@ class Signal(object): '%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)] + return [(receiver, receiver(sender, **kwargs)) + for receiver in self.receivers_for(sender)] def send_async(self, *sender, **kwargs): """Send and collect results from connected functions and coroutines. @@ -360,9 +365,9 @@ class Signal(object): self._by_receiver[receiver_id].discard(sender_id) def _cleanup_bookkeeping(self): - """Prune unused sender/receiver bookeeping. Not threadsafe. + """Prune unused sender/receiver bookkeeping. Not threadsafe. - Connecting & disconnecting leave behind a small amount of bookeeping + Connecting & disconnecting leave behind a small amount of bookkeeping 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. @@ -370,10 +375,10 @@ class Signal(object): 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 + may see memory usage will grow due to extraneous bookkeeping. (An empty set() for each stale sender/receiver pair.) - This method will prune that bookeeping away, with the caveat that such + This method will prune that bookkeeping 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 @@ -422,7 +427,7 @@ class NamedSignal(Signal): def __repr__(self): base = Signal.__repr__(self) - return "%s; %r>" % (base[:-1], self.name) + return "{}; {!r}>".format(base[:-1], self.name) class Namespace(dict): diff --git a/docs/source/index.rst b/docs/source/index.rst index 28976d8..bdb40ef 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -16,8 +16,8 @@ The core of Blinker is quite small but provides powerful features: - 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. +License. The library supports Python 2.7 and Python 3.5 or later; +or Jython 2.7 or later; or PyPy 2.7 or later. Decoupling With Named Signals @@ -1,2 +1,5 @@ [upload_docs] upload-dir = docs/html + +[bdist_wheel] +universal = 1 @@ -16,7 +16,8 @@ setup(name="blinker", keywords='signal emit events broadcast', long_description=readme, license='MIT License', - url='http://pythonhosted.org/blinker/', + url='https://pythonhosted.org/blinker/', + python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', @@ -24,16 +25,12 @@ setup(name="blinker", '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', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Topic :: Software Development :: Libraries', 'Topic :: Utilities', ], diff --git a/tests/test_signals.py b/tests/test_signals.py index e651465..9771e40 100644 --- a/tests/test_signals.py +++ b/tests/test_signals.py @@ -53,10 +53,10 @@ def test_meta_connect(): sig = blinker.Signal() sig.connect(receiver) - assert sentinel == [dict(sender=sig, - receiver_arg=receiver, - sender_arg=blinker.ANY, - weak_arg=True)] + assert sentinel == [{'sender': sig, + 'receiver_arg': receiver, + 'sender_arg': blinker.ANY, + 'weak_arg': True}] blinker.receiver_connected._clear_state() @@ -83,7 +83,7 @@ def _test_signal_signals(sender): expected = ('receiver_connected', sig, - dict(receiver=receiver, sender=sender, weak=weak)) + {'receiver': receiver, 'sender': sender, 'weak': weak}) assert sentinel[-1] == expected @@ -92,14 +92,14 @@ def _test_signal_signals(sender): expected = ('receiver_disconnected', sig, - dict(receiver=receiver1, sender=sender)) + {'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)) + {'receiver': receiver2, 'sender': blinker.ANY}) def test_signal_signals_any_sender(): @@ -491,10 +491,3 @@ def test_named_blinker(): 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) @@ -1,5 +1,5 @@ [tox] -envlist = py25,py26,py27,py30,py31,py32,py33,py34,jython +envlist = py27,py35,py36,py37,py38,jython [testenv] deps=nose |