import os import json import atexit from threading import Thread, Event from collections.abc import MutableMapping from PySide2 import QtCore data_dir_path = os.path.join(QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppDataLocation), "fimefracking") tasks_path = os.path.join(data_dir_path, "tasks.json") data_path = os.path.join(data_dir_path, "data_{}.json") save_delay = 3 * 60 class Tasks: def __init__(self): if not os.path.exists(data_dir_path): os.mkdir(data_dir_path) if os.path.exists(tasks_path): with open(tasks_path, "r") as f: self._tasks = json.loads(f.read()) else: self._tasks = [] @property def tasks(self): return self._tasks @tasks.setter def tasks(self, tasks): self._tasks = tasks self._save() def _save(self): print("...saving tasks...") with open(tasks_path, "w+") as f: f.write(json.dumps(self._tasks)) class Data(MutableMapping): def __init__(self): if not os.path.exists(data_dir_path): os.mkdir(data_dir_path) self._cache = {} self._hot_keys = [] self._running = False self._tevent = Event() self._thread = None def cleanup(): self._running = False self._tevent.set() if self._thread: self._thread.join() atexit.register(cleanup) def __getitem__(self, key): dpath = data_path.format(key) if key not in self._cache and os.path.exists(dpath): with open(dpath, "r") as f: self._cache[key] = f.read() return self._cache[key] def __setitem__(self, key, value): self._cache[key] = value self._hot_keys.append(key) self._schedule_save() def _schedule_save(self): if self._running: return self._running = True self._thread = Thread(target=self._executor, daemon=True) self._thread.start() def _executor(self): while self._running: self._tevent.wait(save_delay) self._save() def _save(self): for key in self._hot_keys: print(f"... saving dict {key} ...") to_write = self._cache[key] # apparently thread-safe with open(data_path.format(key), "w+") as f: f.write(json.dumps(to_write)) self._hot_keys = [] self._saving = False def __delitem__(self, key): print("WARNING: deletion of items not supported") def __iter__(self): return iter(self._cache) def __len__(self): return len(self._cache) def __repr__(self): return f"{type(self).__name__}({self._cache})"