class BaseComponent(PublishingElement): """ """ cls_add_component_hooks = EventHook() listeners = [] _init_hooks = EventHook() def __init__(self): super(BaseComponent, self).__init__() self.obj_listeners = [] self.gen_id = generate_id(self) self.router = Router() self._internal_router = Router() # This was init because I couldn't figure out how to append to a cls variable # in the subclass definitions. The mixin can do it, but I'd need a metaclass # to do it for the Series and Listening Component # also was done to bind to self self.add_component_hooks = EventHook() self.components = [] self.broadcast_hooks = EventHook() self.broadcast_hooks += self.publish self.broadcast_hooks += self.send self.front = self self._init_hooks.fire(self) def broadcast(self, event): self.broadcast_hooks.fire(event) def add_component(self, component, contained=True, **kwargs): if not contained: raise Exception("Defaulted to contained always True") # always contained component.front = self self.components.append(component) self.add_component_hooks.fire(component, **kwargs) # TODO add check to see if hook is unbound. Which I think it will always be self.cls_add_component_hooks.fire(self, component, **kwargs) # shortcut for end and start end_func = getattr(component, 'end', None) if end_func and callable(end_func): self.bind(EndEvent, end_func, 'event') # delegate to router def bind(self, key, callback, exchange='event'): if self.router is None: self.router = Router() self.router.bind(key, callback, exchange) def send(self, message): # note that front isn't always a bouncebox self.front.router.send(message)
class PublishingElement(Element): """ >>> pub.subscribe(sub.handle_event) >>> pub.publish(Event()) """ def __init__(self): super(PublishingElement, self).__init__() self.pubsub_router = Router() # Subscribe/Publish Paradigm def subscribe(self, callback, event_cls=None): """ Parameters ---------- callback : callable Handler called when publishing an event event_cls : Event Class (optional) Type of Event to register the handler. If omitted, it will default to all Events """ if event_cls is None: event_cls = Event self.pubsub_router.bind(event_cls, callback, 'event') def publish(self, event): """ Note, this is called by Component.broadcast """ self.pubsub_router.send(event)
def test_router_queue(self): """ Testing that broadcasting during event propogation gets queued properly """ r = Router() # test binding logger = LoggingObj(r) logger.handle_source_event = MagicMock(side_effect=logger.handle_source_event) logger.handle_event = MagicMock(side_effect=logger.handle_event) r.bind(SourceEvent, logger.handle_source_event, "event") r.bind(be.Event, logger.handle_event, "event") sevt = SourceEvent() r.send(sevt) logger.handle_source_event.assert_called_once_with(sevt) # called once for SourceEvent and TestEvent assert logger.handle_event.call_count == 2 assert isinstance(logger.logs[1], TestEvent)
def test_router(self): r = Router() # test binding comp = MagicMock() r.bind(SourceEvent, comp.handle_source_event, "event") r.bind(be.Event, comp.handle_event, "event") sevt = SourceEvent() r.send(sevt) # assert that SourceEvent triggers both handlers comp.handle_source_event.assert_called_once_with(sevt) comp.handle_event.assert_called_once_with(sevt) evt = be.Event() r.send(evt) # assert be.Event only triggers handle_event assert comp.handle_source_event.call_count == 1 assert comp.handle_event.call_count == 2