smal: add config class

This commit is contained in:
saces 2026-04-13 22:08:56 +02:00
parent 4c9ba7dcdb
commit 285bb0647f
2 changed files with 159 additions and 1 deletions

View file

@ -3,6 +3,7 @@
import logging
from .app import SMALApp
from .config import SMALConfig
logger = logging.getLogger(__name__)
@ -14,9 +15,10 @@ logger = logging.getLogger(__name__)
class SMALBot(SMALApp):
""" """
def __init__(self, sigil):
def __init__(self, sigil, config=None):
super().__init__()
self._sigil = sigil
self._config = config if config is not None else SMALConfig.create_new()
async def sendmessage(self, roomid, text):
data = {}

156
mxsmal/src/mxsmal/config.py Normal file
View file

@ -0,0 +1,156 @@
# Copyright (C) 2026 saces@c-base.org
# SPDX-License-Identifier: AGPL-3.0-only
import os
import pathlib
import sys
from dataclasses import asdict, dataclass
from enum import Enum
import dacite
class ConfigError(Exception):
pass
class ConfigFileType(Enum):
YAML = "yaml"
TOML = "toml"
JSON = "json"
@dataclass
class ConfigFile:
@classmethod
def create_new(cls):
return cls()
@classmethod
def load_file(cls, file_path=None, file_type=None, cast=None):
"""
automagic config file loader
Args:
file_path: fd, filename or "-"
type: type of config file, required for fd and "-"
cast: type hint decite/decoder
Returns: config object
"""
if file_path is None:
return cls()
if file_type is None:
# try to guess the type from name extension
ext = pathlib.Path(file_path).suffix
if ext in [".yaml", ".yml"]:
file_type = ConfigFileType.YAML
elif ext in [".toml", ".tml"]:
file_type = ConfigFileType.TOML
elif ext == ".json":
file_type = ConfigFileType.JSON
else:
raise ConfigError("Unable to guess the file type from name.")
# try to load the data
data = None
if file_path == "-":
file_path = sys.stdin
if file_type == ConfigFileType.TOML:
import toml
data = toml.load(file_path)
elif file_type == ConfigFileType.YAML:
import yaml
data = yaml.safe_load(file_path)
elif file_type == ConfigFileType.JSON:
import json
with open(file_path) as f:
data = json.load(f)
else:
# after adding new file types you may need to update the logic
raise RuntimeError()
return dacite.from_dict(
data_class=cls,
data=data,
# tell dacite to cast some types while deserializing
config=dacite.Config(cast=cast) if cast else None,
)
@classmethod
def load_file_auto(cls, file_path: str, file_type=None, cast=None):
if os.path.exists(file_path):
return cls.load_file(file_path, file_type, cast)
else:
return cls()
def save_file(self, file_path: str, file_type=None):
if file_path == "-" and file_type is None:
raise ConfigError("file_type required if printing to stdout")
if file_type is None:
# try to guess the type from name extension
ext = pathlib.Path(file_path).suffix
if ext in [".yaml", ".yml"]:
file_type = ConfigFileType.YAML
elif ext in [".toml", ".tml"]:
file_type = ConfigFileType.TOML
elif ext == ".json":
file_type = ConfigFileType.JSON
else:
raise ConfigError("Unable to guess the file type from name.")
def my_factory(data):
def convert_value(obj):
if isinstance(obj, Enum):
return obj.value
return obj
return dict((k, convert_value(v)) for k, v in data)
d = asdict(self, dict_factory=my_factory)
if file_path == "-":
file_path = sys.stdout
if file_type == ConfigFileType.TOML:
import toml
toml.dump(d, file_path)
elif file_type == ConfigFileType.YAML:
import yaml
yaml.dump(d, file_path)
elif file_type == ConfigFileType.JSON:
import json
json.dump(d, file_path)
else:
# after adding new file types you may need to update the logic
raise RuntimeError()
else:
if file_type == ConfigFileType.TOML:
import toml
with open(file_path, "w") as file:
toml.dump(d, file)
elif file_type == ConfigFileType.YAML:
import yaml
with open(file_path, "w") as file:
yaml.dump(d, file)
elif file_type == ConfigFileType.JSON:
import json
with open(file_path, "w") as file:
json.dump(d, file)
@dataclass
class SMALConfig(ConfigFile):
pass