67 lines
2.1 KiB
Python
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
|