The Python Oracle

Which Python packages offer a stand-alone event system?

Become part of the top 3% of the developers by applying to Toptal https://topt.al/25cXVn

--

Music by Eric Matyas
https://www.soundimage.org
Track title: Future Grid Looping

--

Chapters
00:00 Question
00:31 Accepted answer (Score 291)
07:05 Answer 2 (Score 117)
08:01 Answer 3 (Score 76)
08:57 Answer 4 (Score 23)
09:29 Thank you

--

Full question
https://stackoverflow.com/questions/1092...

Question links:
[pydispatcher]: http://pydispatcher.sourceforge.net/

Accepted answer links:
[PyDispatcher]: https://pypi.org/project/PyDispatcher/
[blinker]: https://pypi.org/project/blinker/
[pymitter]: https://pypi.org/project/pymitter/
[python-dispatch]: https://pypi.org/project/python-dispatch/
[pluggy]: https://pypi.org/project/pluggy/
[Events]: https://pypi.org/project/Events/
[zope.event]: https://pypi.org/project/zope.event/
[RxPy3]: https://pypi.org/project/RxPy3/
[Louie]: https://pypi.org/project/Louie/
[PyPubSub]: https://pypi.org/project/PyPubSub/
[pyeventdispatcher]: https://pypi.org/project/pyeventdispatch.../
[buslane]: https://pypi.org/project/buslane/
[PyPyDispatcher]: https://pypi.org/project/PyPyDispatcher/
[axel]: https://pypi.org/project/axel/
[dispatcher]: https://pypi.org/project/dispatcher/
[py-notify]: https://pypi.org/project/py-notify/
[Observer pattern]: http://en.wikipedia.org/wiki/Observer_pa...
[publish-subscribe pattern]: http://en.wikipedia.org/wiki/Publish%E2%...
[Mediator pattern]: https://en.wikipedia.org/wiki/Mediator_p...
[threading.Event]: https://docs.python.org/3.5/library/thre...
[pyzmq]: https://pypi.org/project/pyzmq/
[pymq]: https://github.com/thrau/pymq
[Twisted]: https://twistedmatrix.com/trac/
[Tornado]: https://www.tornadoweb.org/
[gevent]: http://www.gevent.org/
[eventlet]: http://eventlet.net/
[zope.event]: https://pypi.python.org/pypi/zope.event
[Lennart's answer]: https://stackoverflow.com/a/1092617/1075...
[LongPoke's 'callable list']: https://stackoverflow.com/a/2022629/1075...
[EventHook]: https://stackoverflow.com/questions/1092...
[spassig's EventHook]: https://stackoverflow.com/a/1094423/1075...
[Josip's Valued Lessons Event class]: https://stackoverflow.com/a/1096614/1075...
[PyNotify]: https://pypi.org/project/py-notify/
[axel]: https://pypi.python.org/pypi/axel
[python-dispatch]: https://pypi.org/project/python-dispatch/
[buslane]: https://pypi.org/project/buslane/
[Observer/Event]: https://stackoverflow.com/questions/1092...
[blinker]: https://pypi.org/project/blinker/
[PyPubSub]: http://pypubsub.readthedocs.io/en/stable/
[pymitter]: https://github.com/riga/pymitter
[PyDispatcher]: http://pydispatcher.sourceforge.net/
[louie]: https://github.com/11craft/louie
[pypydispatcher]: https://github.com/scrapy/pypydispatcher
[django.dispatch]: https://code.djangoproject.com/browser/d...
[pyeventdispatcher]: https://github.com/whisller/pyeventdispa...
[dispatcher]: https://pypi.org/project/dispatcher/
[EventManger]: https://stackoverflow.com/questions/1092...
[pluggy]: https://pypi.org/project/pluggy/
[RxPy3]: https://pypi.org/project/RxPy3/
[PyQt]: http://pyqt.sourceforge.net/Docs/PyQt4/n...
[PySide2]: https://wiki.qt.io/Qt_for_Python_Signals...

Answer 3 links:
[Event Pattern]: http://www.voidspace.org.uk/python/weblo...

Answer 4 links:
[zope.event]: http://pypi.python.org/pypi/zope.event

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #events #eventhandling #dispatcher

#avk47



ANSWER 1

Score 119


I've been doing it this way:

class Event(list):
    """Event subscription.

    A list of callable objects. Calling an instance of this will cause a
    call to each item in the list in ascending order by index.

    Example Usage:
    >>> def f(x):
    ...     print 'f(%s)' % x
    >>> def g(x):
    ...     print 'g(%s)' % x
    >>> e = Event()
    >>> e()
    >>> e.append(f)
    >>> e(123)
    f(123)
    >>> e.remove(f)
    >>> e()
    >>> e += (f, g)
    >>> e(10)
    f(10)
    g(10)
    >>> del e[0]
    >>> e(2)
    g(2)

    """
    def __call__(self, *args, **kwargs):
        for f in self:
            f(*args, **kwargs)

    def __repr__(self):
        return "Event(%s)" % list.__repr__(self)

However, like with everything else I've seen, there is no auto generated pydoc for this, and no signatures, which really sucks.




ANSWER 2

Score 77


We use an EventHook as suggested from Michael Foord in his Event Pattern:

Just add EventHooks to your classes with:

class MyBroadcaster()
    def __init__():
        self.onChange = EventHook()

theBroadcaster = MyBroadcaster()

# add a listener to the event
theBroadcaster.onChange += myFunction

# remove listener from the event
theBroadcaster.onChange -= myFunction

# fire event
theBroadcaster.onChange.fire()

We add the functionality to remove all listener from an object to Michaels class and ended up with this:

class EventHook(object):

    def __init__(self):
        self.__handlers = []

    def __iadd__(self, handler):
        self.__handlers.append(handler)
        return self

    def __isub__(self, handler):
        self.__handlers.remove(handler)
        return self

    def fire(self, *args, **keywargs):
        for handler in self.__handlers:
            handler(*args, **keywargs)

    def clearObjectHandlers(self, inObject):
        for theHandler in self.__handlers:
            if theHandler.im_self == inObject:
                self -= theHandler



ANSWER 3

Score 23


I use zope.event. It's the most bare bones you can imagine. :-) In fact, here is the complete source code:

subscribers = []

def notify(event):
    for subscriber in subscribers:
        subscriber(event)

Note that you can't send messages between processes, for example. It's not a messaging system, just an event system, nothing more, nothing less.




ANSWER 4

Score 18


I found this small script on Valued Lessons. It seems to have just the right simplicity/power ratio I'm after. Peter Thatcher is the author of following code (no licensing is mentioned).

class Event:
    def __init__(self):
        self.handlers = set()

    def handle(self, handler):
        self.handlers.add(handler)
        return self

    def unhandle(self, handler):
        try:
            self.handlers.remove(handler)
        except:
            raise ValueError("Handler is not handling this event, so cannot unhandle it.")
        return self

    def fire(self, *args, **kargs):
        for handler in self.handlers:
            handler(*args, **kargs)

    def getHandlerCount(self):
        return len(self.handlers)

    __iadd__ = handle
    __isub__ = unhandle
    __call__ = fire
    __len__  = getHandlerCount

class MockFileWatcher:
    def __init__(self):
        self.fileChanged = Event()

    def watchFiles(self):
        source_path = "foo"
        self.fileChanged(source_path)

def log_file_change(source_path):
    print "%r changed." % (source_path,)

def log_file_change2(source_path):
    print "%r changed!" % (source_path,)

watcher              = MockFileWatcher()
watcher.fileChanged += log_file_change2
watcher.fileChanged += log_file_change
watcher.fileChanged -= log_file_change2
watcher.watchFiles()