-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reworked Parameters: - can now be used to setup rules dynamically - can be imported and created through ``HABApp.Parameter`` - own documentation page Reworked ``MultiValueItem``: - Removed ``MultiValue`` - Added lots of documentation Other: - ``run_in`` supports ``timedelta`` - Fixed example (fixes #72) - Added more documentation
- Loading branch information
1 parent
f193226
commit 22556a4
Showing
36 changed files
with
929 additions
and
521 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
.idea | ||
__pycache__ | ||
/conf | ||
/conf_testing | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
__VERSION__ = '0.7.2' | ||
__VERSION__ = '0.8.0' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,4 +6,4 @@ | |
import HABApp.core.items | ||
|
||
import HABApp.core.EventBus | ||
import HABApp.core.Items | ||
import HABApp.core.Items |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
|
||
from .parameter import Parameter |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import typing | ||
|
||
from .parameters import add_parameter as _add_parameter | ||
from .parameters import get_value as _get_value | ||
|
||
|
||
class Parameter: | ||
def __init__(self, filename: str, *keys, default_value: typing.Any = 'ToDo'): | ||
"""Class to dynamically access parameters which are loaded from file. | ||
:param filename: filename (without extension) | ||
:param keys: structure in the file | ||
:param default_value: default value for the parameter. | ||
Is used to create the file and the structure if it does not exist yet. | ||
""" | ||
|
||
assert isinstance(filename, str), type(filename) | ||
self.filename: str = filename | ||
self.keys = keys | ||
|
||
# as a convenience try to create the file and the file structure | ||
_add_parameter(self.filename, *self.keys, default_value=default_value) | ||
|
||
@property | ||
def value(self): | ||
"""Return the current value. This will do the lookup so make sure to not cache this value, otherwise | ||
the parameter might not work as expected. | ||
""" | ||
return _get_value(self.filename, *self.keys) | ||
|
||
def __eq__(self, other): | ||
return self.value == other | ||
|
||
def __lt__(self, other): | ||
if not isinstance(other, (int, float)): | ||
return NotImplemented | ||
|
||
return self.value < other | ||
|
||
def __le__(self, other): | ||
if not isinstance(other, (int, float)): | ||
return NotImplemented | ||
|
||
return self.value <= other | ||
|
||
def __ge__(self, other): | ||
if not isinstance(other, (int, float)): | ||
return NotImplemented | ||
|
||
return self.value >= other | ||
|
||
def __gt__(self, other): | ||
if not isinstance(other, (int, float)): | ||
return NotImplemented | ||
|
||
return self.value > other | ||
|
||
def __repr__(self): | ||
return f'<Parameter file: {self.filename}, keys: {self.keys}, value: {self.value}' | ||
|
||
def __str__(self): | ||
return str(self.value) |
241 changes: 101 additions & 140 deletions
241
HABApp/rule_manager/rule_parameters.py → HABApp/parameters/parameter_file_watcher.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,140 +1,101 @@ | ||
import logging | ||
import threading | ||
import traceback | ||
import typing | ||
from pathlib import Path | ||
|
||
import ruamel.yaml | ||
|
||
import HABApp | ||
from HABApp.runtime import FileEventTarget | ||
from HABApp.util import PrintException | ||
|
||
log = logging.getLogger('HABApp.RuleParameters') | ||
|
||
_yml_setup = ruamel.yaml.YAML() | ||
_yml_setup.default_flow_style = False | ||
_yml_setup.default_style = False | ||
_yml_setup.width = 1000000 | ||
_yml_setup.allow_unicode = True | ||
_yml_setup.sort_base_mapping_type_on_output = False | ||
|
||
|
||
class RuleParameters(FileEventTarget): | ||
UNITTEST = False | ||
|
||
@PrintException | ||
def __init__(self, config, folder_watcher): | ||
|
||
# serialize loading | ||
self.__lock = threading.Lock() | ||
|
||
self.params: typing.Dict[str, dict] = {} | ||
|
||
if RuleParameters.UNITTEST: | ||
return | ||
assert isinstance(config, HABApp.config.Config) | ||
assert isinstance(folder_watcher, HABApp.runtime.folder_watcher.FolderWatcher) | ||
self.config = config | ||
|
||
if not self.config.directories.param.is_dir(): | ||
log.info(f'Parameter files disabled. Folder {self.config.directories.param} does not exist!') | ||
return | ||
|
||
# folder watcher | ||
folder_watcher.watch_folder( | ||
folder=self.config.directories.param, | ||
file_ending='.yml', | ||
event_target=self, | ||
worker_factory=lambda x : HABApp.core.WrappedFunction(x, logger=log).run, | ||
watch_subfolders=False | ||
) | ||
|
||
# load all parameter files | ||
def load_all_files(): | ||
for f in self.config.directories.param.glob('**/*.yml'): | ||
try: | ||
self.add_file(f) | ||
except Exception as e: | ||
log.error(e) | ||
HABApp.core.WrappedFunction(load_all_files, logger=log, name='Load all parameter files').run() | ||
|
||
def reload_file(self, path: Path): | ||
self.add_file(path) | ||
|
||
def remove_file(self, path: Path): | ||
try: | ||
with self.__lock: | ||
self.params.pop(path.stem) | ||
except Exception: | ||
log.error(f"Could not remove parameters from {path.name}!") | ||
for l in traceback.format_exc().splitlines(): | ||
log.error(l) | ||
return None | ||
|
||
log.debug(f'Removed params from {path.name}!') | ||
|
||
def add_file(self, path: Path): | ||
try: | ||
with self.__lock: | ||
with path.open( mode='r', encoding='utf-8') as file: | ||
data = _yml_setup.load(file) | ||
if data is None: | ||
data = {} | ||
self.params[path.stem] = data | ||
except Exception: | ||
log.error(f"Could not load params from {path.name}!") | ||
for l in traceback.format_exc().splitlines(): | ||
log.error(l) | ||
return None | ||
|
||
log.debug(f'Loaded params from {path.name}!') | ||
|
||
def save_file(self, file: str): | ||
assert isinstance(file, str), type(file) | ||
|
||
if RuleParameters.UNITTEST: | ||
return None | ||
|
||
filename = self.config.directories.param / (file + '.yml') | ||
with self.__lock: | ||
log.info(f'Updated {filename}') | ||
with filename.open('w', encoding='utf-8') as outfile: | ||
_yml_setup.dump(self.params[file], outfile) | ||
|
||
def add_param(self, file, *keys, default_value): | ||
save = False | ||
|
||
if file not in self.params: | ||
save = True | ||
self.params[file] = param = {} | ||
else: | ||
param = self.params[file] | ||
|
||
if keys: | ||
# Create structure | ||
for key in keys[:-1]: | ||
if key not in param: | ||
param[key] = {} | ||
save = True | ||
param = param[key] | ||
|
||
# Create value | ||
if keys[-1] not in param: | ||
param[keys[-1]] = default_value | ||
save = True | ||
|
||
if save: | ||
self.save_file(file) | ||
return None | ||
|
||
def get_param(self, file, *keys): | ||
try: | ||
param = self.params[file] | ||
except KeyError: | ||
raise FileNotFoundError(f'File {file}.yml not found in params folder!') | ||
|
||
# lookup parameter | ||
for key in keys: | ||
param = param[key] | ||
return param | ||
import logging | ||
import threading | ||
import traceback | ||
from pathlib import Path | ||
|
||
import HABApp | ||
import ruamel.yaml | ||
from HABApp.runtime import FileEventTarget | ||
from HABApp.util import PrintException | ||
|
||
from .parameters import get_parameter_file, remove_parameter_file, set_parameter_file | ||
|
||
log = logging.getLogger('HABApp.RuleParameters') | ||
|
||
_yml_setup = ruamel.yaml.YAML() | ||
_yml_setup.default_flow_style = False | ||
_yml_setup.default_style = False | ||
_yml_setup.width = 1000000 | ||
_yml_setup.allow_unicode = True | ||
_yml_setup.sort_base_mapping_type_on_output = False | ||
|
||
|
||
class ParameterFileWatcher(FileEventTarget): | ||
UNITTEST = False | ||
|
||
@PrintException | ||
def __init__(self, config, folder_watcher): | ||
|
||
# serialize loading | ||
self.__lock: threading.Lock = threading.Lock() | ||
|
||
if ParameterFileWatcher.UNITTEST: | ||
return | ||
assert isinstance(config, HABApp.config.Config) | ||
assert isinstance(folder_watcher, HABApp.runtime.folder_watcher.FolderWatcher) | ||
self.config = config | ||
|
||
if not self.config.directories.param.is_dir(): | ||
log.info(f'Parameter files disabled. Folder {self.config.directories.param} does not exist!') | ||
return | ||
|
||
# folder watcher | ||
folder_watcher.watch_folder( | ||
folder=self.config.directories.param, | ||
file_ending='.yml', | ||
event_target=self, | ||
worker_factory=lambda x : HABApp.core.WrappedFunction(x, logger=log).run, | ||
watch_subfolders=False | ||
) | ||
|
||
# load all parameter files | ||
def load_all_files(): | ||
for f in self.config.directories.param.glob('**/*.yml'): | ||
try: | ||
self.add_file(f) | ||
except Exception as e: | ||
log.error(e) | ||
HABApp.core.WrappedFunction(load_all_files, logger=log, name='Load all parameter files').run() | ||
|
||
def reload_file(self, path: Path): | ||
self.add_file(path) | ||
|
||
def remove_file(self, path: Path): | ||
try: | ||
with self.__lock: | ||
remove_parameter_file(path.stem) | ||
except Exception: | ||
log.error(f"Could not remove parameters from {path.name}!") | ||
for l in traceback.format_exc().splitlines(): | ||
log.error(l) | ||
return None | ||
|
||
log.debug(f'Removed params from {path.name}!') | ||
|
||
def add_file(self, path: Path): | ||
try: | ||
with self.__lock: | ||
with path.open( mode='r', encoding='utf-8') as file: | ||
data = _yml_setup.load(file) | ||
if data is None: | ||
data = {} | ||
set_parameter_file(path.stem, data) | ||
except Exception: | ||
log.error(f"Could not load params from {path.name}!") | ||
for l in traceback.format_exc().splitlines(): | ||
log.error(l) | ||
return None | ||
|
||
log.debug(f'Loaded params from {path.name}!') | ||
|
||
def save_file(self, file: str): | ||
assert isinstance(file, str), type(file) | ||
|
||
if ParameterFileWatcher.UNITTEST: | ||
return None | ||
|
||
filename = self.config.directories.param / (file + '.yml') | ||
with self.__lock: | ||
log.info(f'Updated {filename}') | ||
with filename.open('w', encoding='utf-8') as outfile: | ||
_yml_setup.dump(get_parameter_file(file), outfile) |
Oops, something went wrong.