Refines config parser and gives basic structure for overall framework
This commit is contained in:
parent
6ee324095e
commit
54eb509d4f
4 changed files with 133 additions and 8 deletions
|
@ -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
49
src/config/parser.py
Normal 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()
|
26
src/config/reloadtrigger.py
Normal file
26
src/config/reloadtrigger.py
Normal 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
48
src/mash.py
Normal 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
|
Loading…
Reference in a new issue