2025-03-07 08:03:18 +01:00

98 lines
3.0 KiB
Python

# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus <wojtryb@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
from typing import TypeVar, Generic, Callable, List, Optional
from abc import ABC, abstractmethod
from enum import Enum
from .parsers import Parser, BoolParser, EnumParser, BasicParser
from .field import Field
from .save_location import SaveLocation
T = TypeVar('T')
E = TypeVar('E', bound=Enum)
ListT = TypeVar('ListT', bound=List[Enum])
class FieldBase(ABC, Field, Generic[T]):
"""Implementation base of List, and NonList field."""
def __new__(cls, *args, **kwargs) -> 'FieldBase[T]':
obj = object.__new__(cls)
obj.__init__(*args, **kwargs)
return obj
def __init__(
self,
config_group: str,
name: str,
default: T,
parser_type: Optional[type] = None,
local: bool = False,
) -> None:
self.config_group = config_group
self.name = name
self.default = default
self.parser_type = parser_type
self.location = SaveLocation.LOCAL if local else SaveLocation.GLOBAL
self._on_change_callbacks: List[Callable[[], None]] = []
def register_callback(self, callback: Callable[[], None]) -> None:
"""Store callback in internal list."""
self._on_change_callbacks.append(callback)
def write(self, value: T) -> None:
"""Write value to file and run callbacks if it was not redundant."""
if not isinstance(value, type(self.default)):
raise TypeError(f"{value} not of type {type(self.default)}")
if self._is_write_redundant(value):
return
self.location.write(
group=self.config_group,
name=self.name,
value=self._to_string(value))
for callback in self._on_change_callbacks:
callback()
@abstractmethod
def read(self) -> T:
"""Return value from kritarc parsed to field type."""
...
@abstractmethod
def _to_string(self, value: T) -> str:
"""Convert a value of field type to string."""
...
def _is_write_redundant(self, value: T) -> bool:
"""
Return if writing a value is not necessary.
That is when:
- the value is the same as the one stored in file
- value is a default one and it is not present in file
"""
if self.read() == value:
return True
raw = self.location.read(self.config_group, self.name)
return raw is None and value == self.default
def reset_default(self) -> None:
"""Write a default value to kritarc file."""
self.write(self.default)
@staticmethod
def _get_parser(parser_type: type) -> Parser[T]:
"""Return field parser."""
if issubclass(parser_type, Enum):
return EnumParser(parser_type) # type: ignore
return {
int: BasicParser(int),
float: BasicParser(float),
str: BasicParser(str),
bool: BoolParser()
}[parser_type] # type: ignore