Giter Site home page Giter Site logo

signaller's Introduction

Signaller

Signals and slots implementation with asyncio support

Slots can be functions, methods or coroutines. Weak references are used by default. If slot is coroutine, it will be scheduled to run asynchronously with asyncio.async() (but you must run event loop by yourself).

You can also run blocking functions asynchronously by specifying force_async=True when connecting signal to slot (it will only apply to that slot) or when creating signal (it will apply to all connected slots). ThreadPoolExecutor with 5 worker threads is used by default, but it can be changed when creating signal with executor argument.

Requirements

  • Python >= 3.4

Usage

Example:

import logging
from signaller import Signal, autoconnect

# Enable verbose logging
logging.basicConfig(level=logging.DEBUG)

# Creating signals (you can set signal name, but it is not required,
# signals can be anonymous):
sig_test = Signal('sig_test')

# Connecting signals to slots (uses weak references by default,
# but you can force strong references by specifying weak=False):
def slot(arg):
    print('slot:', arg)

sig_test.connect(slot)
sig_test.connect(lambda arg: print('slot_lambda:', arg), weak=False)

# You can also use decorators for connecting signals to slots:
@sig_test.connect
def slot2(arg):
    print('slot2:', arg)

# And keyword arguments can be specified when using decorators too:
@sig_test.connect(force_async=True)
def slot3(arg):
    print('slot3:', arg)

# You can also use decorators on methods, then signals will be connected to instance
# methods automatically whenever new instance is created. But you must decorate class
# with @autoconnect decorator for autoconnection to work. Class methods and
# static methods are not supported.
@autoconnect
class Cls:
    @sig_test.connect
    def slot4(self, arg):
        print('slot4:', arg)

obj = Cls()

# Slots are automatically disconnected from signals
# when using weak references:
del slot

# Or you can disconnect slots manually:
sig_test.disconnect(slot2)

# Emitting signals (you can send both positional and keyword
# arguments to connected slots):
sig_test.emit('Hello world!')

Output:

INFO:signaller:Connecting signal <Signal 'sig_test' at 0x7f3c468bfc50> to slot <function slot at 0x7f3c46cc6f28>
INFO:signaller:Connecting signal <Signal 'sig_test' at 0x7f3c468bfc50> to slot <function <lambda> at 0x7f3c468c97b8>
INFO:signaller:Connecting signal <Signal 'sig_test' at 0x7f3c468bfc50> to slot <function slot2 at 0x7f3c43c9e400>
INFO:signaller:Connecting signal <Signal 'sig_test' at 0x7f3c468bfc50> to slot <function slot3 at 0x7f3c43c9e598>
DEBUG:signaller:Marking instance method <function Cls.slot4 at 0x7f3c43c9e6a8> for autoconnect to signal <Signal 'sig_test' at 0x7f3c468bfc50>
INFO:signaller:Connecting signal <Signal 'sig_test' at 0x7f3c468bfc50> to slot <bound method Cls.slot4 of <__main__.Cls object at 0x7f3c43f11d30>>
DEBUG:signaller:Object <function slot at 0x7f3c46cc6f28> has been deleted
INFO:signaller:Disconnecting slot <Reference (weak) to <function slot at 0x7f3c46cc6f28> (dead)> from signal <Signal 'sig_test' at 0x7f3c468bfc50>
INFO:signaller:Disconnecting slot <function slot2 at 0x7f3c43c9e400> from signal <Signal 'sig_test' at 0x7f3c468bfc50>
INFO:signaller:Emitting signal <Signal 'sig_test' at 0x7f3c468bfc50>
DEBUG:signaller:Calling slot <Reference (weak) to <function slot3 at 0x7f3c43c9e598>> asynchronously (in executor <concurrent.futures.thread.ThreadPoolExecutor object at 0x7f3c468bff28>)
slot3: Hello world!
DEBUG:signaller:Calling slot <Reference (strong) to <function <lambda> at 0x7f3c468c97b8>>
slot_lambda: Hello world!
DEBUG:signaller:Calling slot <Reference (weak) to <bound method Cls.slot4 of <__main__.Cls object at 0x7f3c43f11d30>>>
slot4: Hello world!

signaller's People

Contributors

xmikos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

ttimo yucongo

signaller's Issues

Set changed size during iteration

I'm trying to use this library for a loose coupling problem with some network code. In this case, there is manager/child relationship. The child gets added to the manager which uses signals to connect callbacks into itself. When the child closes, the manager needs to be notified. When that happens the manager will disconnect itself from the child.

The problem is that using set() for the _slots variable means this isn't possible. Because the slot calls disconnect which changes the size of the slots and triggers the error.

import signaller

class Manager:
    def __init__(self):
        self.children = []
        
    def add(self, child):
        self.children.append(child)
        child.closing.connect(self.remove)

    def remove(self,child):
        self.children.remove(child)
        child.closing.disconnect(self.remove)

class Child:
    def __init__(self):
        self.closing = signaller.Signal("closing")

    def close(self):
        self.closing.emit(self)

mgr = Manager()
child = Child()
mgr.add(child)
child.close()

which yields:

Traceback (most recent call last):
  File "bug.py", line 25, in <module>
    child.close()
  File "bug.py", line 20, in close
    self.closing.emit(self)
  File "/home/ted/miniconda3/envs/devenv/lib/python3.6/site-packages/signaller.py", line 107, in emit
    for ref in self._slots:
RuntimeError: Set changed size during iteration

At least for me, this is common use case that I've encountered in several systems I've built (usually network related). There are several ways that it could be supported: not using set (use list and iterate backwards), making a copy of the set before calling, etc.

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.