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
from config.reloadtrigger import ReloadTrigger
try:
from watchdog.observers import Observer
from watchdog.events import (
@ -8,11 +10,11 @@ try:
FileModifiedEvent,
)
class HotReloader(FileSystemEventHandler):
"""Might be unbound, if watchdog is not installed. Check if equal to None before use."""
class HotDogTrigger(ReloadTrigger, FileSystemEventHandler):
"""Trigger for config files. Might be unbound, if watchdog is not installed. Check if equal to None before use."""
def __init__(self, path):
self.path = path
def __init__(self, path: str):
super().__init__(path)
self.observer = Observer()
self.observer.schedule(self, path, recursive=True)
self.observer.start()
@ -22,13 +24,13 @@ try:
self.observer.join()
def on_modified(self, event: FileModifiedEvent):
logging.info("Config file modified. Triggering hot reload.")
self.__trigger_reload()
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:
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