71 lines
2.2 KiB
Python
71 lines
2.2 KiB
Python
import logging
|
|
|
|
from collections import defaultdict
|
|
from itertools import chain
|
|
from threading import Thread, RLock
|
|
|
|
ALL_EVENTS = '*'
|
|
|
|
class EventBus(object):
|
|
""" Class provides an eventbus. Allows code to listen for events and fire them. """
|
|
|
|
def __init__(self):
|
|
self.listeners = defaultdict(list)
|
|
self.lock = RLock()
|
|
self.logger = logging.getLogger(__name__)
|
|
|
|
def fire(self, event):
|
|
""" Fire an event. """
|
|
assert isinstance(event, Event), "event needs to be an instance of Event"
|
|
|
|
# We dont want the eventbus to be blocking,
|
|
# We dont want the eventbus to crash when one of its listeners throws an Exception
|
|
# So run in a thread
|
|
def run():
|
|
self.lock.acquire()
|
|
|
|
self.logger.info("Event {}: {}".format(event.event_type, event.data))
|
|
|
|
for callback in chain(self.listeners[ALL_EVENTS], self.listeners[event.event_type]):
|
|
callback(event)
|
|
|
|
if event.remove_listener:
|
|
if callback in self.listeners[ALL_EVENTS]:
|
|
self.listeners[ALL_EVENTS].remove(callback)
|
|
|
|
if callback in self.listeners[event.event_type]:
|
|
self.listeners[event.event_type].remove(callback)
|
|
|
|
event.remove_listener = False
|
|
|
|
if event.stop_propegating:
|
|
break
|
|
|
|
self.lock.release()
|
|
|
|
Thread(target=run).start()
|
|
|
|
def listen(self, event_type, callback):
|
|
""" Listen for all events or events of a specific type.
|
|
|
|
To listen to all events specify the constant ``ALL_EVENTS`` as event_type. """
|
|
self.lock.acquire()
|
|
|
|
self.listeners[event_type].append(callback)
|
|
|
|
self.logger.info("New listener for event {}. Total: {}".format(event_type, len(self.listeners[event_type])))
|
|
|
|
self.lock.release()
|
|
|
|
|
|
class Event(object):
|
|
""" An event to be sent over the eventbus. """
|
|
|
|
def __init__(self, event_type, data):
|
|
self.event_type = event_type
|
|
self.data = data
|
|
self.stop_propegating = False
|
|
self.remove_listener = False
|
|
|
|
def __str__(self):
|
|
return str([self.event_type, self.data])
|