Coverage for src/package/settings.py: 100.00%
66 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 21:20 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 21:20 +0000
1#!/usr/bin/env python3
3# Standard libraries
4from configparser import ConfigParser
5from pathlib import Path
6from sys import stdout
7from typing import Optional
9# Components
10from ..prints.colors import Colors
11from ..system.platform import Platform
13# Settings class
14class Settings:
16 # Types
17 Value = int | str
19 # Constants
20 SETTINGS_FILE: str = 'settings.ini'
22 # Members
23 __folder: Path
24 __persistent: bool
25 __path: Path
26 __settings: ConfigParser
28 # Constructor
29 def __init__(self, name: str) -> None:
31 # Prepare paths
32 self.__folder = Platform.userspace(name)
33 self.__path = self.__folder / Settings.SETTINGS_FILE
34 self.__persistent = False
36 # Parse settings
37 self.__settings = ConfigParser()
38 self.__settings.read(self.__path)
40 # Prepare missing settings
41 try:
42 if self.get('package', 'name') != name:
43 raise ValueError('Missing settings files')
44 self.__persistent = True
45 except ValueError:
46 try:
47 self.__prepare()
48 self.__reset(name)
49 self.__persistent = True
50 except PermissionError: # pragma: no cover
51 self.__persistent = False
53 # Initialize settings
54 if not Path(self.__path).is_file():
55 self.__write()
57 # Prepare
58 def __prepare(self) -> None:
60 # Prepare folder path
61 if not Platform.IS_SIMULATED:
62 self.__folder.mkdir(parents=True, exist_ok=True)
64 # Reset
65 def __reset(self, name: str) -> None:
67 # Prepare barebone settings
68 self.__settings = ConfigParser()
69 self.set('package', 'name', name)
71 # Writer
72 def __write(self) -> None:
74 # Write initial settings
75 if self.__persistent and not Platform.IS_SIMULATED:
76 with open(self.__path, encoding='utf8', mode='w') as output:
77 self.__settings.write(output)
79 # Has
80 def has(self, group: str, key: str) -> bool:
82 # Check settings key in group
83 return group in self.__settings and key in self.__settings[group]
85 # Get
86 def get(self, group: str, key: str) -> Optional[Value]:
88 # Get settings key in group
89 if group in self.__settings and key in self.__settings[group]:
90 return self.__settings[group][key]
92 # Default fallback
93 return None
95 # Get bool
96 def get_bool(self, group: str, key: str) -> Optional[bool]:
98 # Get settings key as boolean
99 try:
100 value: str = str(self.get(group, key))
101 return value.lower() == 'true' or int(value) == 1
102 except (TypeError, ValueError):
103 return False
105 # Set
106 def set(self, group: str, key: str, value: Value) -> None:
108 # Prepare group
109 if group not in self.__settings:
110 self.__settings[group] = {}
112 # Unset key
113 if str(value) == 'UNSET':
114 del self.__settings[group][key]
116 # Set key
117 else:
118 self.__settings[group][key] = str(value)
120 # Write updated settings
121 self.__write()
123 # Set bool
124 def set_bool(self, group: str, key: str, value: Value) -> None:
126 # Set settings key as boolean
127 self.set(group, key, 1 if value else 0)
129 # Show
130 def show(self) -> None:
132 # Settings file path
133 print(' ')
134 print(
135 f' {Colors.GREEN}===[ {Colors.YELLOW}Settings:' \
136 f' {Colors.BOLD}{self.__path} {Colors.GREEN}]==={Colors.RESET}'
137 )
138 print(' ')
140 # Settings simulated contents
141 if Platform.IS_SIMULATED:
142 self.__settings.write(stdout)
144 # Settings file contents
145 else:
146 with open(self.__path, encoding='utf8', mode='r') as data:
147 print(data.read())
148 Platform.flush()