#!/usr/bin/env python3 from contextlib import suppress import importlib class Hook(): def __init__(self): self.attached_functions = [] def attach(self, new_func): if not callable(new_func): raise TypeError("Argument to Hook.attach must be callable") self.attached_functions.append(new_func) def __call__(self, *args, **kwargs): for func in self.attached_functions: func(*args, **kwargs) class Module(): def __init__(self, config, core_interface): self.config = config self.shepherd = core_interface #self.shepherd.scheduler self.interface = Interface(self) self.modules = {} # dummy interface in case module doesn't want one def init_other_interfaces(self, interfaces): if not isinstance(self.interface, Interface): raise TypeError("shepherd.module.Module interface attribute must " "be subclass of type shepherd.module.Interface") self.modules = interfaces # Look at providing a run() function or similar, which is a thread started # post_modules_setup class SimpleModule(Module): def __init__(self, config, core_interface): super().__init__(config, core_interface) self.setup() def init_other_interfaces(self, interfaces): super().init_other_interfaces(interfaces) self.setup_other_modules() # Users override this, self.shepherd and self.config are available now. # User still needs to define self.interface if it is used. def setup(self): pass def setup_other_modules(self): pass """ An interface to a Shepherd module, accessible by other modules. All public methods in a module interface need to be threadsafe, as they will be called by other modules (which generally run in a seperate thread) """ class Interface(): def __init__(self, module): self._module = module def find_modules(module_names): module_classes = {} for module_name in module_names: mod = importlib.import_module("shepherd.modules." + module_name) attrs = [getattr(mod, name) for name in dir(mod)] for attr in attrs: with suppress(TypeError): if issubclass(attr, Module): module_classes[module_name] = attr break else: raise Exception("Imported shepherd modules must contain a " "subclass of shepherd.module.Module, such as" "shepherd.module.SimpleModule") return module_classes