StarfallBot/starfall/config.py

67 lines
2.1 KiB
Python

import logging
import os
from shutil import copy
from typing import Any, final
import tomllib
@final
class Config:
"""Provides a wrapper for toml config files."""
def __init__(self, file_name: str, sample_file_name: str):
"""Read in a config file and provide it as a :obj:`Config` object.
Args:
file_name (str): Path to a TOML config file.
sample_file_name (str): Path to a sample TOML config file.
Used if :param:`file_name` does not exist.
"""
self.file_name = file_name
"""str: Path to a TOML config file."""
self.sample_file_name = sample_file_name
"""str: Path to a sample TOML config file. Used if :attr:`file_name` does not exist."""
self._toml_data = {}
"""dict: TOML data, parsed to a nested dict."""
try:
with open(str(self.file_name), "rb") as f:
self._toml_data = tomllib.load(f)
except OSError as error:
file_name = copy(str(self.sample_file_name), str(self.file_name))
logging.getLogger().warning(
"Could not read config file %r, reason: %r",
os.path.realpath(file_name),
error,
)
def get(self, key: str, default: Any = None) -> Any:
"""Recursively calls `get(key, default)` to search nested dictionary.
Examples:
Fetch a nested configuration value.
>>> config = Config("config.toml", "config-sample.toml")
>>> config.get("web.run", False)
True
Returns:
Any: Value at key if found, provided default or None otherwise.
"""
nested_key: list[str] = key.split(".")
current_item: Any = self._toml_data
# Traverse the dict for every depth of the key the user provided.
# If any part of the key does not exist, bail and return default.
for part in nested_key:
if part not in current_item:
return default
current_item = current_item[part]
# No parts left; `current_item` now holds the requested value.
return current_item