Giter Site home page Giter Site logo

blinker's Introduction

Blinker

Blinker provides a fast dispatching system that allows any number of interested parties to subscribe to events, or "signals".

Pallets Community Ecosystem

Important

This project is part of the Pallets Community Ecosystem. Pallets is the open source organization that maintains Flask; Pallets-Eco enables community maintenance of related projects. If you are interested in helping maintain this project, please reach out on the Pallets Discord server.

Example

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(f"Round {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!

blinker's People

Contributors

andriyor avatar davidism avatar dependabot[bot] avatar graingert avatar homeworkprod avatar hugovk avatar jek avatar jfinkels avatar kurtmckee avatar pgajdos avatar pgjones avatar razerm avatar secrus avatar thomaswaldmann avatar tirkarthi avatar tomasd avatar wbolster avatar yackx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blinker's Issues

Support multiple senders in connect_via

https://pythonhosted.org/blinker/#blinker.base.Signal.connect_via

We have a use case to send a signal to multiple senders, so we can do:

@some_signal.connect_via_multiple([sender1, sender2, ..., senderN])
def func():
	pass

# instead of
@some_signal.connect_via(sender1)
@some_signal.connect_via(sender2)
...
@some_signal.connect_via(senderN)

def func():
	pass

@jek Would like to get some opinion from you. Are you leaning on adding a new method connect_via_multiple that does the same thing as connect_via except that it accepts senders as a list, or updating the current connect_via to accept a senders as either a single sender or list of senders? I will be happy to throw in a PR.

Receiver function without sender argument

We have a project where we are using a bunch of signals, but we don't care about the sender in the receiver functions, just the kwargs passed to the signal.
Would it be possible to modify blinker to allow connecting functions without a sender first argument?

Signal.send(*senders) means you must omit `sender=` if using a single arg

https://github.com/jek/blinker/blob/42aad6110a02a86a9a51a77c75c2d638a50656b7/blinker/base.py#L261

If you don't write the my_signal.send() call as blinker expects, you get a silent failure.

In my_signal.send(sender=MyObject()) means that sender is None. I'm on Python3, but I've rigged a simple example to show what is happening. The cases where you don't pass in a named arg it works, but otherwise sender sticks around in kwargs).

I'm not sure of a good solution but the way it works definitely caught me off guard.

In [2]: def foo(*sender, **kwargs):
   ...:     print(f"len: {len(sender)} and sender is: {sender}")
   ...:     print(f"kwargs is {kwargs}")
   ...:

In [3]: foo(123)
len: 1 and sender is: (123,)
kwargs is {}

In [4]: foo(sender=123)
len: 0 and sender is: ()
kwargs is {'sender': 123}

In [5]: foo([123, 'abc'])
len: 1 and sender is: ([123, 'abc'],)
kwargs is {}

Now, if you swap *sender, to sender=None, sender is always defined, even if the len() fails on an int.


In [6]: def bar(sender=None, **kwargs):
   ...:     print(f"len: {len(sender)} and sender is: {sender}")
   ...:     print(f"kwargs is {kwargs}")
   ...:

In [7]: bar(123)
TypeError                                 Traceback (most recent call last)
<ipython> in <module>()
----> 1 bar(123)

<ipython> in bar(sender, **kwargs)
      1 def bar(sender=None, **kwargs):
----> 2     print(f"len: {len(sender)} and sender is: {sender}")
      3     print(f"kwargs is {kwargs}")
      4

TypeError: object of type 'int' has no len()

In [8]: bar(sender=123)
TypeError                                 Traceback (most recent call last)
<ipython> in <module>()
----> 1 bar(sender=123)

<ipython> in bar(sender, **kwargs)
      1 def bar(sender=None, **kwargs):
----> 2     print(f"len: {len(sender)} and sender is: {sender}")
      3     print(f"kwargs is {kwargs}")
      4

TypeError: object of type 'int' has no len()

In [9]: bar([123, 'abc'])
len: 2 and sender is: [123, 'abc']
kwargs is {}

I'm not sure if there's a way to reconcile the two approaches, but I figure I might as well write it down.

get rid of dead snakes

from setup.py / tox.ini:

[tox]
envlist = py25,py26,py27,py30,py31,py32,py33,py34,py35,jython
          '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',

Python 2.6 didn't get new releases since 5 years, so I guess for a new blinker release everything older than 2.7 could be dropped.

The last 3.2 release is also 4y old, so guess all < 3.3 could be dropped (if not even more).

People still using ancient versions can still use the older blinker release(s).

Also, tox and setup.py pypi metadata should be kept in sync.

1.4: sphinx warnings

+ /usr/bin/python3 setup.py build_sphinx -b man --build-dir build/sphinx
running build_sphinx
Running Sphinx v4.0.2
making output directory... done
WARNING: html_static_path entry '_static' does not exist
building [mo]: targets for 0 po files that are out of date
building [man]: all manpages
updating environment: [new config] 1 added, 0 changed, 0 removed
reading sources... [100%] index
/home/tkloczko/rpmbuild/BUILD/blinker-1.4/blinker/base.py:docstring of blinker.base.NamedSignal:4: WARNING: Field list ends without a blank line; unexpected unindent.
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
writing... python-blinker.3 { } done
build succeeded, 2 warnings.

the performance of process works not well

Hey, I am trying to use blinker to process events from the server.
As I subscribe to many contracts, blinker seems to process events too slowly.
So I want to ask you it can support thread or gevent ?

Consider asyncio features ala asyncblink

"AsyncBlink is a small extention to Blinker and enables you to use coroutines as receivers for your signals."

Seems to be essentially a modification of send() like so:

        # the only difference is here. If it's a coroutine,
        # run it with asyncio.async()
        receivers = self.receivers_for(sender) or []
        return_list = []

        for receiver in receivers:
            ret = receiver(sender, **kwargs)
            if asyncio.coroutines.iscoroutine(ret):
                ret = asyncio.async(ret)
            return_list.append((receiver, ret))

        return return_list

Useful to have this in Blinker natively? Perhaps something ala:

def send_async(self, *sender, **kw):
    wrapped = lambda v: asyncio.coroutine(lambda: v)
    values = [(r, wrapped(v)) if asyncio.iscoroutine(v) else (r, v)
                   for r, v in self.send(*sender, **kw)]
    return asyncio.gather(*values)

cc: @jucacrispim

Using `int` as a sender does not always work.

Hi, I stumbled upon an issue when using int as signal sender. Receivers where called for some random senders, but not for others.

Please consider this minimal example:

import blinker

my_signal = blinker.Signal()


def handler(sender):
    print("Got it!")


my_signal.connect(handler, sender=123456789)
my_signal.send(123456789)
# prints "Got it!"
my_signal.send(123456789 + 0)
# .. nothing ...

This behaviour seems to be caused by comparing senders by id(sender), result of which might be different for integers with the same value during lifetime of a program.

One way to fix this, could be to use hash() instead of id(). In my opinion it would result in a more intuitive behaviour - e.g. namedtuples and any value objects could be safely used as sender arguments. However, just replacing id(sender) with hash(sender) will fail on hash collisions (most notably, hash(-1) == hash(-2)). Also, that can be a backward incompatible change for some.
Any other ideas?

Version 1.6 maybe include some breaking changes

Hey @pgjones!

I maintain the Sentry Python Repo and we run tests to make sure our SDK works with the Quart framework. Quart uses Blinker.

With the new release 1.6 our tests fail with the Blinker error RuntimeError: Cannot send to a coroutine function:
https://github.com/getsentry/sentry-python/actions/runs/4596847777/jobs/8137198226#step:5:608

This is the line in the test that fails:
https://github.com/getsentry/sentry-python/blob/master/tests/integrations/quart/test_quart.py#L147

I am not an expert in Blinker nor Quart, so I do not know why this is happening.

I just wanted to bring to your attention that some behavior changed with the 1.6 release.

tox result is strange

  py25: commands succeeded  # no python2.5 on my system
  py26: commands succeeded  # no python2.6 on my system
  py27: commands succeeded  # ok
  py30: commands succeeded  # no python3.0 on my system
  py31: commands succeeded  # no python3.1 on my system
  py32: commands succeeded  # no python3.2 on my system
  py33: commands succeeded  # no python3.3 on my system
ERROR:  py34: InterpreterNotFound: python3.4  # ok
ERROR:  py35: InterpreterNotFound: python3.5  # ok
  py36: commands succeeded  # ok
ERROR:  jython: InterpreterNotFound: jython  # ok

So it says "succeeded" although there was no such python interpreter...

Dependencies on fresh install cannot be resolved.

Adding blinker to Pipfile's [packages]…
Pipfile.lock not found, creating…
Locking [dev-packages] dependencies…
Locking [packages] dependencies…

Warning: Your dependencies could not be resolved. You likely have a mismatch in your sub-dependencies.
You can use $ pipenv install --skip-lock to bypass this mechanism, then run $ pipenv graph to inspect the situation.
Could not find a version that matches vine==1.3.0,==5.0.0
Tried: 0.9.0, 0.9.0, 0.9.1, 0.9.1, 1.0.0, 1.0.0, 1.0.1, 1.0.1, 1.0.2, 1.0.2, 1.1.0, 1.1.0, 1.1.1, 1.1.1, 1.1.2, 1.1.2, 1.1.3, 1.1.3, 1.1.4, 1.1.4, 1.2.0, 1.2.0, 1.3.0, 1.3.0, 5.0.0a1, 5.0.0a1, 5.0.0, 5.0.0

Release new version to PyPI

There have been several fixes on master since the last release (1.4). Could you release a new version to PyPI?

Use of ParamSpec only works in Python 3.10+

This morning we faced this issue
42580

for a working service that has received no code change in dependencies recently. By tracking the root cause, I was able to detect that it was caused by typing-extension, which was used by blinker, which was part of Flask (our direct dependency). A similar issue with the same footprint could be found here and here. Typing extension didnt include a change log for version < 4 so I cannot track down which version starts this issue.

We either need to do some if else check to ensure that when a combination of packages is used, blinker is able to use paramspec. Another intrusive way is to declare a minimum version for typing extension.

Temporarily disable a signal in a block

I have an application that generates reports from a bunch of sources. When a report is generated, a Blinker signal report_created is called, and one of the receivers sends an email to subscribers of that report type.

It sometimes happens, however, that we want to (re/)generate a large amount of reports. In such cases we don’t want email sending to happen. However, if an actual report gets generated meanwhile by the normal means, i want the receiver to be called, so i can’t just disconnect that receiver.

What i could imagine is something like this:

with report_created.disable():
    generate_a_lot_of_reports()

As far as i understand, this is not possible in the current Blinker version, but please correct me if i’m wrong. Also, if this use case seems valid (ie. not a unicorn case, when only my project needs it) i’m willing to dig deeper and submit a PR for this.

Project Active?

Is this project still active? I am asking this since I see lot of old pending PRs.

Asynchronus signal call

Is something like the following possible with the current blinker framework?

import time
import multiprocessing
import blinker

sig = blinker.signal('test')

@sig.connect
def callback(data):
    print(data)

with multiprocessing.Pool(2) as p:
    p.map(sig.send, [1, 2])

Basically, I would like for the signal to create a thread for the callback function.
Is there any optimal way of doing this currently?

Does blinker works with gevent?

If I spawn multiple greenlets does blinker signals will find their way among them? In my case it seems that the signals can't travel between greenlets.

DeprecationWarning: invalid escape sequence \*

When running pytest on test suite of my work project I have the following warnings at the end of test run:

.venv/lib/python3.8/site-packages/blinker/base.py:93: 11 warnings
  /usr/lib/publishing/.venv/lib/python3.8/site-packages/blinker/base.py:93: DeprecationWarning: invalid escape sequence \*
    """Connect *receiver* to signal events sent by *sender*.

.venv/lib/python3.8/site-packages/blinker/base.py:161: 11 warnings
  /usr/lib/publishing/.venv/lib/python3.8/site-packages/blinker/base.py:161: DeprecationWarning: invalid escape sequence \*
    """Connect the decorated function as a receiver for *sender*.

.venv/lib/python3.8/site-packages/blinker/base.py:242: 11 warnings
  /usr/lib/publishing/.venv/lib/python3.8/site-packages/blinker/base.py:242: DeprecationWarning: invalid escape sequence \*
    """Emit this signal on behalf of *sender*, passing on \*\*kwargs.

I guess those docstrings should be r"""...""" strings to avoid such warnings. Or \* sequences should be simply replaced by *.

Deps:

  • CPython 3.8.6
  • pytest 6.1.0
  • blinker 1.4

cannot install on centos 7

OS

CentOS Linux release 7.6.1810 (Core)

Install error

[root@VM-4-4-centos ~]# pip3 install blinker
Looking in indexes: http://mirrors.tencentyun.com/pypi/simple
Collecting blinker
  Downloading http://mirrors.tencentyun.com/pypi/packages/1b/51/e2a9f3b757eb802f61dc1f2b09c8c99f6eb01cf06416c0671253536517b6/blinker-1.4.tar.gz (111 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 111.5/111.5 KB 7.1 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... error
  error: subprocess-exited-with-error

  × python setup.py egg_info did not run successfully.
  │ exit code: 1
  ╰─> [1 lines of output]
      ERROR: Can not execute `setup.py` since setuptools is not available in the build environment.
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

× Encountered error while generating package metadata.
╰─> See above for output.

note: This is an issue with the package mentioned above, not pip.
hint: See above for details.

then I try to use yum

yum install python-blinker-1.3-2.el7.noarch.rpm

But it only can be used in python2, not have python3, I don't know what can I do.

I see https://centos.pkgs.org/7/centos-extras-x86_64/python-blinker-1.3-2.el7.noarch.rpm.html, the change log in 2014 say it support python3, but actually it not. Or I made a mistake, hope get your help.

@_utilities.lazy_property isn't thread safe

Just thought I'd let u know (from having made this same mistake myself),

But the application of @_utilities.lazy_property isn't actually thread-safe.

An example program you can try:

import threading
import thread

import time

from blinker import _utilities


class SlowThing(object):

    @_utilities.lazy_property
    def b(self):
        print "starting reading 'b' thread %s" % thread.get_ident()
        time.sleep(0.5)
        print "finished reading 'b' thread %s" % thread.get_ident()
        return 1

s = SlowThing()


def _run():
    s.b


threads = []
for i in range(0, 5):
    t = threading.Thread(target=_run)
    threads.append(t)
    t.start()


while threads:
    t = threads.pop()
    t.join()

The output of this will create:

starting reading 'b' thread 140684440516352
starting reading 'b' thread 140684432123648
starting reading 'b' thread 140684423730944
starting reading 'b' thread 140684415338240
starting reading 'b' thread 140684203915008
finished reading 'b' thread 140684432123648
finished reading 'b' thread 140684440516352
finished reading 'b' thread 140684423730944
finished reading 'b' thread 140684415338240
finished reading 'b' thread 140684203915008

So u can see there are many threads at the same time recomputing that property (in this case it doesn't matter, but in other cases it might?)

Some code that I have made that does appear to work fine:

Perhaps we can share it somehow...

https://github.com/openstack/taskflow/blob/master/taskflow/utils/misc.py#L346

Signal connected to unittest.Mock and MagicMock does not work in Python 3.8

After upgrading Blinker from 1.5 to 1.6.2, tests in my project started to fail.

In short, I found this issue and also suggesting a workaround. Whole test case for reproducing this issue is here.


My test cases are checking if a signal was called. I use MagicMock to check if and how signals are called. I also used spec parameter of MagicMock, because plain initialization did not work for some other compatibility issues, you'll see it below

Here is an equivalent of what I have in my tests:

def foo(sender, **kwargs):
    """Stub function for spec parameter"""
    pass

# Test case code
mock = MagicMock(spec=foo)
self.signal.connect(mock, sender=self)
self.signal.send(self) # RuntimeError: Cannot send to a coroutine function here
# see this for details: https://bugs.python.org/issue37251
mock.assert_called_once_with(self)
Stack trace
Error
Traceback (most recent call last):
  File "/home/denis/devel/python_scripts/test_blinker.py", line 24, in test_magicmock_spec
    self.signal.send(self)  # RuntimeError: Cannot send to a coroutine function here
  File "/home/denis/devel/uvis/venv3/lib/python3.8/site-packages/blinker/base.py", line 298, in send
    raise RuntimeError("Cannot send to a coroutine function")
RuntimeError: Cannot send to a coroutine function

So Blinker received async support after the upgrade. The check
asyncio.iscoroutinefunction(func) has been added to send(), where func is my MagicMock. This is indeed a problem
of Python, rather than Blinker, I found this ticket here: https://bugs.python.org/issue37251

So I've tried to get rid of spec

But receive another error, at connect()

def test_magicmock(self):
    mock = MagicMock()
    self.signal.connect(mock, sender=self)  # AttributeError: __self__
    self.signal.send(self)
    mock.assert_called_once_with(self)
Stack trace
Error
Traceback (most recent call last):
  File "/home/denis/devel/python_scripts/test_blinker.py", line 30, in test_magicmock
    self.signal.connect(mock, sender=self)  # AttributeError: __self__
  File "/home/denis/devel/uvis/venv3/lib/python3.8/site-packages/blinker/base.py", line 131, in connect
    receiver_ref = reference(receiver, self._cleanup_receiver)
  File "/home/denis/devel/uvis/venv3/lib/python3.8/site-packages/blinker/_utilities.py", line 80, in reference
    weak = callable_reference(object, callback)
  File "/home/denis/devel/uvis/venv3/lib/python3.8/site-packages/blinker/_utilities.py", line 91, in callable_reference
    return BoundMethodWeakref(target=object, on_delete=callback)
  File "/home/denis/devel/uvis/venv3/lib/python3.8/site-packages/blinker/_saferef.py", line 124, in __new__
    key = cls.calculate_key(target)
  File "/home/denis/devel/uvis/venv3/lib/python3.8/site-packages/blinker/_saferef.py", line 190, in calculate_key
    return (id(get_self(target)), id(get_func(target)))
  File "/usr/lib/python3.8/unittest/mock.py", line 639, in __getattr__
    raise AttributeError(name)
AttributeError: __self__

Plain MagicMock does not work. The same for Mock.

Workaround

Have a callable class and pass it to spec parameter:

class FooStub:
    """Stub class for spec"""
    def __call__(self, sender, **kwargs):
        pass

    ...
def test_magicmock_stub(self):
    # This test passes
    mock = MagicMock(spec=FooStub)
    self.signal.connect(mock, sender=self)
    self.signal.send(self)
    mock.assert_called_once_with(self)

Also works for Mock.

See the code here

NameError: name 'Exception' is not defined

Exception ignored in: <function BoundMethodWeakref.__init__.<locals>.remove at 0x7f6a4404cae8> Traceback (most recent call last): File "/opt/python/run/venv/local/lib/python3.6/site-packages/blinker/_saferef.py", line 174, in remove NameError: name 'Exception' is not defined Exception ignored in: <function BoundMethodWeakref.__init__.<locals>.remove at 0x7f6a4404ca60> Traceback (most recent call last): File "/opt/python/run/venv/local/lib/python3.6/site-packages/blinker/_saferef.py", line 174, in remove NameError: name 'Exception' is not defined

I think the line 174 except Exception: in https://github.com/jek/blinker/blob/master/blinker/_saferef.py shoud be only except: in python 3.

SyntaxWarning on python3.12

After upgrading to python3.12, we see these warnings:

/usr/lib/python3/dist-packages/blinker/base.py:93: SyntaxWarning: invalid escape sequence '\*'
  """Connect *receiver* to signal events sent by *sender*.
/usr/lib/python3/dist-packages/blinker/base.py:161: SyntaxWarning: invalid escape sequence '\*'
  """Connect the decorated function as a receiver for *sender*.
/usr/lib/python3/dist-packages/blinker/base.py:242: SyntaxWarning: invalid escape sequence '\*'
  """Emit this signal on behalf of *sender*, passing on \*\*kwargs.

Feature Request: Signal which only sends once

Hi, I have a use-case of having a signal which only sends once. Currently I solve this, by subclassing Signal and having a boolean instance variable which is set to true if send is called. If then send is called again and the boolean value is true, no real sending is performend.

IMHO, this would be a nice feature for this project and I'm happy to contribute. I opened this issue before to ask if you share my view or not (opinions differ which is fine)

Continuous Integration

Hello,

It will be nice to add Continuous Integration.
Please go to https://travis-ci.org/ and enable your blinker project

add a .travis.yml file with

language: python
python:
  - "2.7"
  - "3.2"
  - "3.3"
  - "3.4"

# command to install dependencies
install:
  - "pip install ."

# command to run tests
script: nosetests

then you can push your code.

Kind regards

got a problem when use `signal` to create a signal

Hey jek, I am using your blinker as a event-driven,

when I use a class to create a signals like this

from blinker import signal
from uuid import uuid4
class Appsignal:
    def __init__(self):
        self.a = signal(uuid4())

when I try to create two objects of Appsinal,
the output id(self.a) is equal, when I use NamedSignal, it works normally, can you explain why they are equal? or this is a bug.

Support for receivers without positional parameters?

On one of the things I'm working on, blinker.py seems like a perfect solution -- if I could connect receivers that don't take any positional or keyword arguments. That way, I could integrate blinker without having to add an empty positional just to provide access. The necessary modification for this seems fairly light-weight and potentially useful in several cases -- but I'm wondering if it might create unforeseen problems.

In blinker/base.py:

def _get_ret_val(func, val, **kwargs):
    try:
        ret_val = func(val, **kwargs)
    except TypeError:
        if val is None:
            ret_val = func(**kwargs)
        else:
            raise
    return ret_val

Then, adding it to the final statement in Signal.send...

    def send(self, *sender, **kwargs):
        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, _get_ret_val(receiver, sender, **kwargs))
                    for receiver in self.receivers_for(sender)]

The only problem with this I can think of is that None sender might carry special significance; does it?

(Apologies in advance if this is not the place to ask these questions; I'm a bit new to this!)

Blocking API / waiting API?

Is there any support to execute a blocking wait for a specific signal or signals?

Usecase: agent-based modelling where the agents communicate

Problem in _saferef.py

My code reported this error:
File "/home/lib/python3.4/site-packages/blinker/_saferef.py", line 197, in calculate_key
return (id(get_self(target)), id(get_func(target)))
AttributeError: 'function' object has no attribute 'self' .
When i print(dir(target)) i got this:
['annotations', 'call', 'class', 'closure', 'code', 'defaults', 'delattr', 'dict', 'dir', 'doc', 'eq', 'format', 'ge', 'get', 'getattribute', 'globals', 'gt', 'hash', 'init', 'kwdefaults', 'le', 'lt', 'module', 'name', 'ne', 'new', 'qualname', 'reduce', 'reduce_ex', 'repr', 'setattr', 'sizeof', 'str', 'subclasshook', 'accept', 'im_func', 'im_self', 'is_signal_handler']

There is no 'self', just 'im_self'. I tried to change the code on line 50 in file _saferef.py:
from if sys.version_info < (3,):
to if sys.version_info > (3,):

and my code works. Is it a mistake in my code or is it a bug ?
Thank you for your patience.

move to github.com/discorporate/blinker

@jek iirc you can click somewhere in the settings to move a repo to a new github org.

guess that is the easiest option to get it there.

i'ld offer to do some basic maintenance if i get the permissions (like for flatland, including a new pypi release with the new URL).

Using object proxy as sender do not work

When application uses object proxy as sender in connect then signal is never delivered. This is due use of object id, which is different for real object and proxy.

For example when using https://pypi.python.org/pypi/lazy-object-proxy the signal is never received.

import blinker
from lazy_object_proxy import Proxy

class X(object):
    def receive(self, *args, **kwargs):
      print 'got signal'
    def send(self):
      print 'sending signal'
      test_signal.send(self)

x = X()

def get_x():
    return x

x_proxy = Proxy(get_x)  # Lazy object proxy is useful sometimes

test_signal = blinker.Signal()
test_signal.connect(x_proxy.receive, sender=x_proxy)

x_proxy.send()  # never received because id(x) != id(x_proxy)

Even though x and x_proxy have different id values, the normal equal comparison works, as well as hash() returns same value:

>>> id(x) == id(x_proxy)
False
>>> x == x_proxy
True
>>> hash(x) == hash(x_proxy)
True

It is similar issue as reported in #30 and similar solution (using hash) would help.

Needed a queued connection

Sometimes I have a coil-like topology of objects. In Qt I just used QueuedConnection signal connection for them. Cannot you implement something like that?

import blinker
from blinker import Signal


mega_signal = Signal()

   class QueuedObject(object):

    def __init__(self):
        self.counter = 0

    def on_received(self, *args):
        print(self.counter)
        self.counter += 1
        if self.counter <= 50000:
            mega_signal.send()


foo = QueuedObject()

mega_signal.connect(foo.on_received)
mega_signal.send()

Tuple of strings as sender

As of blinker 1.3 we can now reliably use strings as senders, but it seems that the next logical step, using a tuple of strings, does not work reliably.

if I do:

def blah(sender, **kwargs):
    print sender, kwargs

s = blinker.Signal()

s.connect(blah, ('ab', 'cde'))
s.send(('ab', 'cde'))

The call to s.send(('ab', 'cde')) will result in blah() sometimes being called, sometimes not.

Using a tuple of strings seems, to me at least, a nice way to have a finer grained signal subscription mechanism without having separate signals.

Exceptions prevent all connected receivers from being called.

I would like a way of ensuring all receivers are called, even if one misbehaves.

Happy to do a PR if you can give some direction.

Example below (and of course the order of receivers is not specified).

>>> import blinker
... sig = blinker.signal('asignal')
... def a(sender):
...     print (sender)
...     print ('a done')
... def bad(sender):
...     print (sender)
...     1/0
...     print ('never done')
... def c(sender):
...     print (sender)
...     print ('c done')
... sig.connect(a)
... sig.connect(bad)
... sig.connect(c)
... sig.send('THESENDERSTRING')
THESENDERSTRING
a done
THESENDERSTRING
Traceback (most recent call last):
  File "<pyshell#2>", line 16, in <module>
    sig.send('THESENDERSTRING')
  File "D:\Python27\lib\site-packages\blinker\base.py", line 267, in send
    for receiver in self.receivers_for(sender)]
  File "<pyshell#1>", line 8, in bad
    1/0
ZeroDivisionError: integer division or modulo by zero
>>> 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.