from __future__ import annotations as _annotations from dataclasses import asdict, dataclass @dataclass class Config: bot_channel: str = 'breadtube-bot' bot_role: str = 'BreadTube' bot_channel_scan_interval: float = 30. bot_channel_init_retries: int = 3 bot_message_duration: float = 150. request_timeout: float = 3. def to_str(self) -> str: return '\n'.join(['config', *[f'{k}={v}' for k, v in asdict(self).items()]]) @staticmethod def from_str(text: str) -> Config: annotations = Config.__annotations__ global_types = globals()['__builtins__'] config = Config() lines = text.strip().splitlines() if not lines: raise RuntimeError('Cannot load config: empty input') if lines[0] != 'config': raise RuntimeError('Cannot load config: first line is not "config"') config_dict = {} for line_number, line in enumerate(lines[1:]): key, value = line.split('=', maxsplit=1) if key not in annotations: raise RuntimeError(f'Invalid config: invalid key {key} at line {line_number + 1}') if key in config_dict: raise RuntimeError(f'Invalid config: duplicated key {key} at line {line_number + 1}') config_dict[key] = value for key, value in config_dict.items(): setattr(config, key, global_types[annotations[key]](value)) return config