Rename config_files module → user_files + document
This commit is contained in:
parent
23be12fb60
commit
61cd3b2f55
|
@ -28,11 +28,11 @@ class Backend:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.appdirs = AppDirs(appname=__app_name__, roaming=True)
|
self.appdirs = AppDirs(appname=__app_name__, roaming=True)
|
||||||
|
|
||||||
from . import config_files
|
from . import user_files
|
||||||
self.saved_accounts = config_files.Accounts(self)
|
self.saved_accounts = user_files.Accounts(self)
|
||||||
self.ui_settings = config_files.UISettings(self)
|
self.ui_settings = user_files.UISettings(self)
|
||||||
self.ui_state = config_files.UIState(self)
|
self.ui_state = user_files.UIState(self)
|
||||||
self.history = config_files.History(self)
|
self.history = user_files.History(self)
|
||||||
|
|
||||||
self.models = ModelStore(allowed_key_types={
|
self.models = ModelStore(allowed_key_types={
|
||||||
Account, # Logged-in accounts
|
Account, # Logged-in accounts
|
||||||
|
@ -232,7 +232,7 @@ class Backend:
|
||||||
async def load_settings(self) -> tuple:
|
async def load_settings(self) -> tuple:
|
||||||
"""Return parsed user config files."""
|
"""Return parsed user config files."""
|
||||||
|
|
||||||
from .config_files import Theme
|
from .user_files import Theme
|
||||||
settings = await self.ui_settings.read()
|
settings = await self.ui_settings.read()
|
||||||
ui_state = await self.ui_state.read()
|
ui_state = await self.ui_state.read()
|
||||||
history = await self.history.read()
|
history = await self.history.read()
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
"""User data and configuration files definitions."""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import logging as log
|
import logging as log
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, ClassVar, Dict, Optional
|
||||||
|
|
||||||
import aiofiles
|
import aiofiles
|
||||||
|
|
||||||
|
@ -17,7 +19,11 @@ WRITE_LOCK = asyncio.Lock()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ConfigFile:
|
class DataFile:
|
||||||
|
"""Base class representing a user data file."""
|
||||||
|
|
||||||
|
is_config: ClassVar[bool] = False
|
||||||
|
|
||||||
backend: Backend = field(repr=False)
|
backend: Backend = field(repr=False)
|
||||||
filename: str = field()
|
filename: str = field()
|
||||||
|
|
||||||
|
@ -30,23 +36,36 @@ class ConfigFile:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self) -> Path:
|
def path(self) -> Path:
|
||||||
|
"""Full path of the file, even if it doesn't exist yet."""
|
||||||
|
|
||||||
|
if self.is_config:
|
||||||
return Path(self.backend.appdirs.user_config_dir) / self.filename
|
return Path(self.backend.appdirs.user_config_dir) / self.filename
|
||||||
|
|
||||||
|
return Path(self.backend.appdirs.user_data_dir) / self.filename
|
||||||
|
|
||||||
|
|
||||||
async def default_data(self):
|
async def default_data(self):
|
||||||
|
"""Default content if the file doesn't exist."""
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
async def read(self):
|
async def read(self):
|
||||||
|
"""Return the content of the existing file on disk."""
|
||||||
|
|
||||||
log.debug("Reading config %s at %s", type(self).__name__, self.path)
|
log.debug("Reading config %s at %s", type(self).__name__, self.path)
|
||||||
return self.path.read_text()
|
return self.path.read_text()
|
||||||
|
|
||||||
|
|
||||||
async def write(self, data) -> None:
|
async def write(self, data) -> None:
|
||||||
|
"""Request for the file to be written/updated with data."""
|
||||||
|
|
||||||
self._to_write = data
|
self._to_write = data
|
||||||
|
|
||||||
|
|
||||||
async def _write_loop(self) -> None:
|
async def _write_loop(self) -> None:
|
||||||
|
"""Write/update file to on disk with a 1 second cooldown."""
|
||||||
|
|
||||||
self.path.parent.mkdir(parents=True, exist_ok=True)
|
self.path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
@ -59,13 +78,21 @@ class ConfigFile:
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class JSONConfigFile(ConfigFile):
|
class JSONDataFile(DataFile):
|
||||||
|
"""Represent a user data file in the JSON format."""
|
||||||
|
|
||||||
async def default_data(self) -> JsonData:
|
async def default_data(self) -> JsonData:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
async def read(self) -> JsonData:
|
async def read(self) -> JsonData:
|
||||||
|
"""Return the content of the existing file on disk.
|
||||||
|
|
||||||
|
If the file doesn't exist on disk or it has missing keys, the missing
|
||||||
|
data will be merged and written to disk before returning.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
data = json.loads(await super().read())
|
data = json.loads(await super().read())
|
||||||
except (FileNotFoundError, json.JSONDecodeError):
|
except (FileNotFoundError, json.JSONDecodeError):
|
||||||
|
@ -86,15 +113,26 @@ class JSONConfigFile(ConfigFile):
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Accounts(JSONConfigFile):
|
class Accounts(JSONDataFile):
|
||||||
|
"""Config file for saved matrix accounts: user ID, access tokens, etc."""
|
||||||
|
|
||||||
|
is_config = True
|
||||||
|
|
||||||
filename: str = "accounts.json"
|
filename: str = "accounts.json"
|
||||||
|
|
||||||
|
|
||||||
async def any_saved(self) -> bool:
|
async def any_saved(self) -> bool:
|
||||||
|
"""Return whether there are any accounts saved on disk."""
|
||||||
return bool(await self.read())
|
return bool(await self.read())
|
||||||
|
|
||||||
|
|
||||||
async def add(self, user_id: str) -> None:
|
async def add(self, user_id: str) -> None:
|
||||||
|
"""Add an account to the config and write it on disk.
|
||||||
|
|
||||||
|
The account's details such as its access token are retrieved from
|
||||||
|
the corresponding `MatrixClient` in `backend.clients`.
|
||||||
|
"""
|
||||||
|
|
||||||
client = self.backend.clients[user_id]
|
client = self.backend.clients[user_id]
|
||||||
|
|
||||||
await self.write({
|
await self.write({
|
||||||
|
@ -108,6 +146,8 @@ class Accounts(JSONConfigFile):
|
||||||
|
|
||||||
|
|
||||||
async def delete(self, user_id: str) -> None:
|
async def delete(self, user_id: str) -> None:
|
||||||
|
"""Delete an account from the config and write it on disk."""
|
||||||
|
|
||||||
await self.write({
|
await self.write({
|
||||||
uid: info
|
uid: info
|
||||||
for uid, info in (await self.read()).items() if uid != user_id
|
for uid, info in (await self.read()).items() if uid != user_id
|
||||||
|
@ -115,7 +155,11 @@ class Accounts(JSONConfigFile):
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class UISettings(JSONConfigFile):
|
class UISettings(JSONDataFile):
|
||||||
|
"""Config file for QML interface settings and keybindings."""
|
||||||
|
|
||||||
|
is_config = True
|
||||||
|
|
||||||
filename: str = "settings.json"
|
filename: str = "settings.json"
|
||||||
|
|
||||||
|
|
||||||
|
@ -174,15 +218,12 @@ class UISettings(JSONConfigFile):
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class UIState(JSONConfigFile):
|
class UIState(JSONDataFile):
|
||||||
|
"""File to save and restore the state of the QML interface."""
|
||||||
|
|
||||||
filename: str = "state.json"
|
filename: str = "state.json"
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def path(self) -> Path:
|
|
||||||
return Path(self.backend.appdirs.user_data_dir) / self.filename
|
|
||||||
|
|
||||||
|
|
||||||
async def default_data(self) -> JsonData:
|
async def default_data(self) -> JsonData:
|
||||||
return {
|
return {
|
||||||
"collapseAccounts": {},
|
"collapseAccounts": {},
|
||||||
|
@ -192,21 +233,21 @@ class UIState(JSONConfigFile):
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class History(JSONConfigFile):
|
class History(JSONDataFile):
|
||||||
|
"""File to save and restore lines typed by the user in QML components."""
|
||||||
|
|
||||||
filename: str = "history.json"
|
filename: str = "history.json"
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def path(self) -> Path:
|
|
||||||
return Path(self.backend.appdirs.user_data_dir) / self.filename
|
|
||||||
|
|
||||||
|
|
||||||
async def default_data(self) -> JsonData:
|
async def default_data(self) -> JsonData:
|
||||||
return {"console": []}
|
return {"console": []}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Theme(ConfigFile):
|
class Theme(DataFile):
|
||||||
|
"""A theme file defining the look of QML components."""
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self) -> Path:
|
def path(self) -> Path:
|
||||||
data_dir = Path(self.backend.appdirs.user_data_dir)
|
data_dir = Path(self.backend.appdirs.user_data_dir)
|
Loading…
Reference in New Issue
Block a user