diff --git a/core/meow.py b/core/meow.py index 77ca564..9fba97b 100644 --- a/core/meow.py +++ b/core/meow.py @@ -4,11 +4,12 @@ import sys import threading from multiprocessing import Pipe +from random import randrange from typing import Any, Union from core.correctness.vars import VALID_RECIPE_NAME_CHARS, \ VALID_PATTERN_NAME_CHARS, VALID_RULE_NAME_CHARS, VALID_CHANNELS, \ - get_drt_imp_msg, DEBUG_WARNING, DEBUG_INFO + get_drt_imp_msg, DEBUG_WARNING, DEBUG_INFO, EVENT_TYPE from core.correctness.validation import valid_string, check_type, \ check_implementation, valid_list, valid_dict, setup_debugging from core.functionality import print_debug, wait, generate_id @@ -175,41 +176,73 @@ class BaseHandler: pass -# TODO reformat to allow for updated monitor / handler interaction -# TODO expand this to allow for lists of monitors / handlers class MeowRunner: - monitor:BaseMonitor - handler:BaseHandler - from_monitor: VALID_CHANNELS - def __init__(self, monitor:BaseMonitor, handler:BaseHandler, + monitors:list[BaseMonitor] + handlers:dict[str:BaseHandler] + from_monitor: list[VALID_CHANNELS] + def __init__(self, monitors:Union[BaseMonitor,list[BaseMonitor]], + handlers:Union[BaseHandler,list[BaseHandler]], print:Any=sys.stdout, logging:int=0) -> None: - self._is_valid_monitor(monitor) - self.monitor = monitor - monitor_to_runner_reader, monitor_to_runner_writer = Pipe() - self.monitor.to_runner = monitor_to_runner_writer - self.from_monitor = monitor_to_runner_reader - self._is_valid_handler(handler) - self.handler = handler + self._is_valid_handlers(handlers) + if not type(handlers) == list: + handlers = [handlers] + self.handlers = {} + for handler in handlers: + handler_events = handler.valid_event_types() + for event in handler_events: + if event in self.handlers.keys(): + self.handlers[event].append(handler) + else: + self.handlers[event] = [handler] + + self._is_valid_monitors(monitors) + if not type(monitors) == list: + monitors = [monitors] + self.monitors = monitors + self.from_monitors = [] + for monitor in self.monitors: + monitor_to_runner_reader, monitor_to_runner_writer = Pipe() + monitor.to_runner = monitor_to_runner_writer + self.from_monitors.append(monitor_to_runner_reader) + self._stop_pipe = Pipe() self._worker = None self._print_target, self.debug_level = setup_debugging(print, logging) def run(self)->None: - all_inputs = [self.from_monitor, self._stop_pipe[0]] + all_inputs = self.from_monitors + [self._stop_pipe[0]] while True: ready = wait(all_inputs) if self._stop_pipe[0] in ready: return else: - message = self.from_monitor.recv() - event = message - self.handler.handle(event) + for from_monitor in self.from_monitors: + if from_monitor in ready: + message = from_monitor.recv() + event = message + if not self.handlers[event[EVENT_TYPE]]: + print_debug(self._print_target, self.debug_level, + "Could not process event as no relevent " + f"handler for '{EVENT_TYPE}'", DEBUG_INFO) + return + if len(self.handlers[event[EVENT_TYPE]]) == 1: + self.handlers[event[EVENT_TYPE]][0].handle(event) + else: + self.handlers[event[EVENT_TYPE]][ + randrange(len(self.handlers[event[EVENT_TYPE]])) + ].handle(event) def start(self)->None: - self.monitor.start() - if hasattr(self.handler, "start"): - self.handler.start() + for monitor in self.monitors: + monitor.start() + startable = [] + for handler_list in self.handlers.values(): + for handler in handler_list: + if hasattr(handler, "start") and handler not in startable: + startable.append() + for handler in startable: + handler.start() if self._worker is None: self._worker = threading.Thread( @@ -227,9 +260,16 @@ class MeowRunner: def stop(self)->None: - self.monitor.stop() - if hasattr(self.handler, "stop"): - self.handler.stop() + for monitor in self.monitors: + monitor.stop() + + stopable = [] + for handler_list in self.handlers.values(): + for handler in handler_list: + if hasattr(handler, "stop") and handler not in stopable: + stopable.append() + for handler in stopable: + handler.stop() if self._worker is None: msg = "Cannot stop thread that is not started." @@ -243,11 +283,17 @@ class MeowRunner: "Worker thread stopped", DEBUG_INFO) - def _is_valid_monitor(self, monitor:BaseMonitor)->None: - check_type(monitor, BaseMonitor) + def _is_valid_monitors(self, + monitors:Union[BaseMonitor,list[BaseMonitor]])->None: + check_type(monitors, BaseMonitor, alt_types=[list[BaseMonitor]]) + if type(monitors) == list: + valid_list(monitors, BaseMonitor, min_length=1) - def _is_valid_handler(self, handler:BaseHandler)->None: - check_type(handler, BaseHandler) + def _is_valid_handlers(self, + handlers:Union[BaseHandler,list[BaseHandler]])->None: + check_type(handlers, BaseHandler, alt_types=[list[BaseHandler]]) + if type(handlers) == list: + valid_list(handlers, BaseHandler, min_length=1) def create_rules(patterns:Union[dict[str,BasePattern],list[BasePattern]], recipes:Union[dict[str,BaseRecipe],list[BaseRecipe]],