3rd/src/Config.py
vlw 310fa9b66a refactor: streamlined config file structure and config value referencing (#1)
This PR refactors the config file structure into a simpler format, as well as making it more capable at the same time. Config values are now accessed as properties instead of referenced on the parsed config JSON list directly. Config files are also loaded with the `-i` argument which can take any compatible JSON file, this replaces the previous `-a` (autorun) argument.

Reviewed-on: https://codeberg.org/vlw/3rd/pulls/1
Co-authored-by: vlw <victor@vlw.se>
Co-committed-by: vlw <victor@vlw.se>
2026-01-01 00:46:25 +01:00

174 lines
4.2 KiB
Python

import json
import tempfile
from pathlib import Path
from typing import Self, Union
from .Enums import ConfigKeys
class Config():
pathnames = set()
@staticmethod
def for_each(items: list) -> Self:
"""
Returns a generator which iterates over each item in a list of item configs
Args:
items (list): The list to iterate over
Returns:
Self: Instance of the Config class
Yields:
Iterator[Self]: Config class for the current item
"""
for item in items:
yield Config(item)
@staticmethod
def from_json_file(pathname: str) -> Self:
"""
Load item configs from a JSON file
Args:
pathname (str): _description_
Returns:
Self: _description_
"""
with open(pathname, "r") as f:
json = json.load(f)
for item in json:
Config.pathnames.add(item[ConfigKeys.ABSPATH_TARGET.value])
Config.for_each(json)
@staticmethod
def __throw_missing_key(key: ConfigKeys) -> None:
"""
Raises a KeyError for an item config key if it does not exist
Args:
key (ConfigKeys): The key to raise an error for
Raises:
KeyError: Raised from an item config key
"""
raise KeyError(f"Expected required item config key '{key.value}' but it was not found")
@staticmethod
def __throw_value_error(key: ConfigKeys, expected_type: str) -> None:
"""
Raise a ValueError for a key with an expected type
Args:
key (ConfigKeys): The item config key to raise an error for
expected_type (str): The expected type
Raises:
ValueError: Raised from an item config key and expected value type
"""
raise ValueError(f"Item config key '{key.value}' expects a value of type {expected_type}")
def __init__(self, item: dict):
"""
Create a new Config instance
"""
self.__item = item
@property
def password(self) -> str|False:
"""
Returns the password for this item, or None if unset
Returns:
str|False: Password or None if no password is set
"""
if not self.__key_exists(ConfigKeys.PASSWORD.value):
return False
return self.__item[ConfigKeys.PASSWORD.value] if isinstance(self.__item[ConfigKeys.PASSWORD.value], str) else None
@property
def compression(self) -> int:
"""
Returns the compression level for this item, or false if STORE mode should be used
Returns:
str|False: Compression level for this item, false if compression is disabled
"""
if not self.__key_exists(ConfigKeys.COMPRESSION.value):
return 0
if not isinstance(self.__item[ConfigKeys.COMPRESSION.value], int) or self.__item[ConfigKeys.COMPRESSION.value] == 0:
return 0
return max(1, min(self.__item[ConfigKeys.COMPRESSION.value], 10))
@property
def abspath_temp(self) -> str:
"""
Returns the path to the directory where the created archive will be stored until it's uploaded
Returns:
str: Absolute path to the destination directory
"""
if not self.__key_exists(ConfigKeys.ABSPATH_TEMP.value):
return tempfile.gettempdir()
return self.__item[ConfigKeys.ABSPATH_TEMP.value] if isinstance(self.__item[ConfigKeys.ABSPATH_TEMP.value], str) else tempfile.gettempdir()
@property
def abspath_target(self) -> str:
"""
Returns an absolute path to the target to be archived
Returns:
str: Absolute path to the target
"""
if not self.__key_exists(ConfigKeys.ABSPATH_TARGET.value):
return Config.__throw_missing_key(ConfigKeys.ABSPATH_TARGET)
if not isinstance(self.__item[ConfigKeys.ABSPATH_TARGET.value], str):
return Config.__throw_value_error(ConfigKeys.ABSPATH_TARGET, str)
return self.__item[ConfigKeys.ABSPATH_TARGET.value]
@property
def abspath_destination(self) -> str:
"""
Returns an absolute path to the target to be archived
Returns:
str: Absolute path to the target
"""
if not self.__key_exists(ConfigKeys.ASBPATH_DESTINATION.value):
return Config.__throw_missing_key(ConfigKeys.ASBPATH_DESTINATION)
if not isinstance(self.__item[ConfigKeys.ASBPATH_DESTINATION.value], str):
return Config.__throw_value_error(ConfigKeys.ASBPATH_DESTINATION, str)
return self.__item[ConfigKeys.ASBPATH_DESTINATION.value]
def __key_exists(self, key: str) -> bool:
"""
Returns true if a property key is defined for the current item
Args:
key (str): The key to test
Returns:
bool: True if key exists
"""
return key in self.__item