""" A signal/slot implementation

File:    signal.py
Author:  Thiago Marcos P. Santos
Author:  Christopher S. Case
Author:  David H. Bronke
Created: August 28, 2008
Updated: December 12, 2011
License: MIT

"""
import inspect
from weakref import WeakKeyDictionary


class Signal(object):
    def __init__(self):
        # The original implementation used WeakSet to store functions,
        # but that causes lambdas without any other reference to be
        # garbage collected. So we use a normal set to avoid that.
        self._functions = set()
        self._methods = WeakKeyDictionary()

    # The original implementation used __call__, so one would just call the signal itself:
    #
    # my_signal("foo")
    #
    # This has been changed to the emit() method to both be more consistent with how signals/slots
    # work in Qt & GTK and to make it more easily apparent that a signal is being triggered.
    # The correct way to trigger a signal is therefore:
    #
    # my_signal.emit("foo")
    def emit(self, *args, **kargs):
        # Call handler functions
        for func in self._functions.copy():
            func(*args, **kargs)

        # Call handler methods
        for obj, funcs in self._methods.copy().items():
            for func in funcs.copy():
                func(obj, *args, **kargs)

    def connect(self, slot):
        if inspect.ismethod(slot):
            if slot.__self__ not in self._methods:
                self._methods[slot.__self__] = set()

            self._methods[slot.__self__].add(slot.__func__)

        else:
            self._functions.add(slot)

    def disconnect(self, slot):
        if inspect.ismethod(slot):
            if slot.__self__ in self._methods:
                self._methods[slot.__self__].discard(slot.__func__)
        else:
            if slot in self._functions:
                self._functions.discard(slot)

    def clear(self):
        self._functions.clear()
        self._methods.clear()
