Refines config parser and gives basic structure for overall framework

This commit is contained in:
Maximilian Giller 2022-11-04 14:35:31 +01:00
parent 6ee324095e
commit 54eb509d4f
4 changed files with 133 additions and 8 deletions

View file

@ -1,5 +1,7 @@
import logging import logging
from config.reloadtrigger import ReloadTrigger
try: try:
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.events import ( from watchdog.events import (
@ -8,11 +10,11 @@ try:
FileModifiedEvent, FileModifiedEvent,
) )
class HotReloader(FileSystemEventHandler): class HotDogTrigger(ReloadTrigger, FileSystemEventHandler):
"""Might be unbound, if watchdog is not installed. Check if equal to None before use.""" """Trigger for config files. Might be unbound, if watchdog is not installed. Check if equal to None before use."""
def __init__(self, path): def __init__(self, path: str):
self.path = path super().__init__(path)
self.observer = Observer() self.observer = Observer()
self.observer.schedule(self, path, recursive=True) self.observer.schedule(self, path, recursive=True)
self.observer.start() self.observer.start()
@ -22,13 +24,13 @@ try:
self.observer.join() self.observer.join()
def on_modified(self, event: FileModifiedEvent): def on_modified(self, event: FileModifiedEvent):
logging.info("Config file modified. Triggering hot reload.") self.__trigger_reload()
def on_created(self, event: FileCreatedEvent): def on_created(self, event: FileCreatedEvent):
logging.info("New config file created. Triggering hot reload.") self.__trigger_reload()
logging.debug("Watchdog imported successfully. Hot reloading available.") logging.debug("Watchdog imported successfully. HotDogTrigger available.")
except ImportError: except ImportError:
logging.info("Watchdog is not installed. Hot reloading unavailable.") logging.info("Watchdog is not installed. HotDogTrigger unavailable.")

49
src/config/parser.py Normal file
View file

@ -0,0 +1,49 @@
import abc
from typing import Any
from models.home import Home
class ConfigParser:
def get_config(self) -> Home: # TODO: Check type
"""Load and parse the config."""
return self.__parse_config(self.__load_config())
@abc.abstractmethod
def __load_config(self) -> Any:
"""Load the config."""
raise NotImplementedError()
@abc.abstractmethod
def __parse_config(self, config) -> Home:
"""Parse the config."""
raise NotImplementedError()
class FileConfigParser(ConfigParser):
"""Scaffoling for parsers based on files."""
path: str
"""Path to the config file."""
def __init__(self, path: str):
self.path = path
class YAMLConfig(FileConfigParser):
"""Loads and parses config from a YAML file."""
def __init__(self, path: str):
super().__init__(path)
def __load_config(self) -> dict:
"""Loads config from a YAML file."""
import yaml
with open(self.path, "r") as file:
return yaml.safe_load(file)
def __parse_config(self, config: dict) -> Home:
"""Parses config in dict format. Usually based on JSON or YAML."""
# TODO: Implement
raise NotImplementedError()

View file

@ -0,0 +1,26 @@
class ReloadTrigger(object):
"""Abstract interface for config hot reloading."""
path: str
"""Path to the config file."""
callbacks: list
"""List of functions to callback on reload of config."""
def __init__(self, path: str):
"""Initialize the trigger.
Args:
path (str): Path to the config file.
"""
self.path = path
self.callbacks = []
def on_reload(self, callback):
"""Register a callback to be called when the config is reloaded."""
self.callbacks.append(callback)
def __trigger_reload(self):
"""Trigger a reload of the config."""
for callback in self.callbacks:
callback()

48
src/mash.py Normal file
View file

@ -0,0 +1,48 @@
from config.parser import ConfigParser
from config.reloadtrigger import ReloadTrigger
from models.home import Home
class MaSH:
"""Max' Smart Home. Smart home automation framework."""
config_parser: ConfigParser
"""Parser for the config file."""
config_trigger: list[ReloadTrigger]
"""Active triggers for reloading the config."""
home: Home
"""Home to control and make smart."""
def __init__(self, config_parser: ConfigParser) -> None:
"""Initialize the framework.
Args:
config_parser (ConfigParser): Parser for the config file.
"""
self.config_parser = config_parser
self.config_trigger = []
self.reload_config()
def reload_config(self) -> None:
"""Reload the config."""
self.home = self.config_parser.get_config() # TODO: Check type
def register_config_trigger(self, trigger: ReloadTrigger) -> bool:
"""Register a trigger for hot-reloading the config.
Args:
trigger (ReloadTrigger): Trigger for hot-reloading the config based on ReloadTrigger.
Returns:
bool: True if the trigger was registered, False if not.
"""
if trigger is None or trigger in self.config_trigger:
return False
trigger.on_reload(self.reload_config)
self.config_trigger.append(trigger)
return True