|
2 | 2 | import os |
3 | 3 |
|
4 | 4 | class SharedPreferences: |
5 | | - def __init__(self, appname, filename="config.json"): |
6 | | - """Initialize with appname and filename for preferences.""" |
| 5 | + def __init__(self, appname, filename="config.json", defaults=None): |
| 6 | + """Initialize with appname, filename, and optional defaults for preferences.""" |
7 | 7 | self.appname = appname |
8 | 8 | self.filename = filename |
| 9 | + self.defaults = defaults if defaults is not None else {} |
9 | 10 | self.filepath = f"data/{self.appname}/{self.filename}" |
10 | 11 | self.data = {} |
11 | 12 | self.load() |
@@ -36,31 +37,80 @@ def load(self): |
36 | 37 | def get_string(self, key, default=None): |
37 | 38 | """Retrieve a string value for the given key, with a default if not found.""" |
38 | 39 | to_return = self.data.get(key) |
39 | | - if to_return is None and default is not None: |
40 | | - to_return = default |
| 40 | + if to_return is None: |
| 41 | + # Method default takes precedence |
| 42 | + if default is not None: |
| 43 | + to_return = default |
| 44 | + # Fall back to constructor default |
| 45 | + elif key in self.defaults: |
| 46 | + to_return = self.defaults[key] |
41 | 47 | return to_return |
42 | 48 |
|
43 | 49 | def get_int(self, key, default=0): |
44 | 50 | """Retrieve an integer value for the given key, with a default if not found.""" |
45 | | - try: |
46 | | - return int(self.data.get(key, default)) |
47 | | - except (TypeError, ValueError): |
| 51 | + if key in self.data: |
| 52 | + try: |
| 53 | + return int(self.data[key]) |
| 54 | + except (TypeError, ValueError): |
| 55 | + return default |
| 56 | + # Key not in stored data, check defaults |
| 57 | + # Method default takes precedence if explicitly provided (not the hardcoded 0) |
| 58 | + # Otherwise use constructor default if exists |
| 59 | + if default != 0: |
48 | 60 | return default |
| 61 | + if key in self.defaults: |
| 62 | + try: |
| 63 | + return int(self.defaults[key]) |
| 64 | + except (TypeError, ValueError): |
| 65 | + return 0 |
| 66 | + return 0 |
49 | 67 |
|
50 | 68 | def get_bool(self, key, default=False): |
51 | 69 | """Retrieve a boolean value for the given key, with a default if not found.""" |
52 | | - try: |
53 | | - return bool(self.data.get(key, default)) |
54 | | - except (TypeError, ValueError): |
| 70 | + if key in self.data: |
| 71 | + try: |
| 72 | + return bool(self.data[key]) |
| 73 | + except (TypeError, ValueError): |
| 74 | + return default |
| 75 | + # Key not in stored data, check defaults |
| 76 | + # Method default takes precedence if explicitly provided (not the hardcoded False) |
| 77 | + # Otherwise use constructor default if exists |
| 78 | + if default != False: |
55 | 79 | return default |
| 80 | + if key in self.defaults: |
| 81 | + try: |
| 82 | + return bool(self.defaults[key]) |
| 83 | + except (TypeError, ValueError): |
| 84 | + return False |
| 85 | + return False |
56 | 86 |
|
57 | 87 | def get_list(self, key, default=None): |
58 | 88 | """Retrieve a list for the given key, with a default if not found.""" |
59 | | - return self.data.get(key, default if default is not None else []) |
| 89 | + if key in self.data: |
| 90 | + return self.data[key] |
| 91 | + # Key not in stored data, check defaults |
| 92 | + # Method default takes precedence if provided |
| 93 | + if default is not None: |
| 94 | + return default |
| 95 | + # Fall back to constructor default |
| 96 | + if key in self.defaults: |
| 97 | + return self.defaults[key] |
| 98 | + # Return empty list as hardcoded fallback |
| 99 | + return [] |
60 | 100 |
|
61 | 101 | def get_dict(self, key, default=None): |
62 | 102 | """Retrieve a dictionary for the given key, with a default if not found.""" |
63 | | - return self.data.get(key, default if default is not None else {}) |
| 103 | + if key in self.data: |
| 104 | + return self.data[key] |
| 105 | + # Key not in stored data, check defaults |
| 106 | + # Method default takes precedence if provided |
| 107 | + if default is not None: |
| 108 | + return default |
| 109 | + # Fall back to constructor default |
| 110 | + if key in self.defaults: |
| 111 | + return self.defaults[key] |
| 112 | + # Return empty dict as hardcoded fallback |
| 113 | + return {} |
64 | 114 |
|
65 | 115 | def edit(self): |
66 | 116 | """Return an Editor object to modify preferences.""" |
@@ -197,14 +247,31 @@ def remove_all(self): |
197 | 247 | self.temp_data = {} |
198 | 248 | return self |
199 | 249 |
|
| 250 | + def _filter_defaults(self, data): |
| 251 | + """Remove keys from data that match constructor defaults.""" |
| 252 | + if not self.preferences.defaults: |
| 253 | + return data |
| 254 | + |
| 255 | + filtered = {} |
| 256 | + for key, value in data.items(): |
| 257 | + if key in self.preferences.defaults: |
| 258 | + if value != self.preferences.defaults[key]: |
| 259 | + filtered[key] = value |
| 260 | + # else: skip saving, matches default |
| 261 | + else: |
| 262 | + filtered[key] = value # No default, always save |
| 263 | + return filtered |
| 264 | + |
200 | 265 | def apply(self): |
201 | 266 | """Save changes to the file asynchronously (emulated).""" |
202 | | - self.preferences.data = self.temp_data.copy() |
| 267 | + filtered_data = self._filter_defaults(self.temp_data) |
| 268 | + self.preferences.data = filtered_data |
203 | 269 | self.preferences.save_config() |
204 | 270 |
|
205 | 271 | def commit(self): |
206 | 272 | """Save changes to the file synchronously.""" |
207 | | - self.preferences.data = self.temp_data.copy() |
| 273 | + filtered_data = self._filter_defaults(self.temp_data) |
| 274 | + self.preferences.data = filtered_data |
208 | 275 | self.preferences.save_config() |
209 | 276 | return True |
210 | 277 |
|
|
0 commit comments