import logging from abc import ABC, abstractmethod from typing import Iterable from tfw.networking import Scope LOG = logging.getLogger(__name__) class EventHandlerBase(ABC): """ Abstract base class for all Python based EventHandlers. Useful implementation template for other languages. Derived classes must implement the handle_event() method """ _instances = set() def __init__(self, key, scope=Scope.ZMQ): type(self)._instances.add(self) self.server_connector = self._build_server_connector() self.scope = scope self.keys = [] if isinstance(key, str): self.keys.append(key) elif isinstance(key, Iterable): self.keys = list(key) self.subscribe(*self.keys) self.server_connector.register_callback(self.event_handler_callback) @abstractmethod def _build_server_connector(self): raise NotImplementedError() def subscribe(self, *keys): """ Subscribe this EventHandler to receive events for given keys. Note that you can subscribe to the same key several times in which case you will need to unsubscribe multiple times in order to stop receiving events. :param keys: list of keys to subscribe to """ for key in keys: self.server_connector.subscribe(key) self.keys.append(key) def event_handler_callback(self, message): """ Callback that is invoked when receiving a message. Dispatches messages to handler methods and sends a response back in case the handler returned something. This is subscribed in __init__(). """ if self.check_key(message): self.dispatch_handling(message) def check_key(self, message): """ Checks whether the message is intended for this EventHandler. This is necessary because ZMQ handles PUB - SUB connetions with pattern matching (e.g. someone subscribed to 'fsm' will receive 'fsm_update' messages as well. """ if '' in self.keys: return True return message['key'] in self.keys def dispatch_handling(self, message): """ Used to dispatch messages to their specific handlers. :param message: the message received :returns: the message to send back """ self.handle_event(message) def handle_event(self, message): """ Abstract method that implements the handling of messages. :param message: the message received :returns: the message to send back """ raise NotImplementedError def send_message(self, message): self.server_connector.send_message(message, self.scope) def unsubscribe(self, *keys): """ Unsubscribe this eventhandler from the given keys. :param keys: list of keys to unsubscribe from """ for key in keys: self.server_connector.unsubscribe(key) self.keys.remove(key) @classmethod def stop_all_instances(cls): for instance in cls._instances: instance.stop() def stop(self): self.server_connector.close() self.cleanup() def cleanup(self): """ Perform cleanup actions such as releasing database connections and stuff like that. """