Change json and toml file API

This commit is contained in:
Joscha 2025-11-05 02:07:43 +01:00
parent 862a9c1683
commit c7f7697f66
2 changed files with 64 additions and 30 deletions

View file

@ -1,12 +1,28 @@
from typing import Self
import json import json
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any from typing import Any, Self
from .file import TAG, File from .file import TAG, File
from .text import TextFile from .text import TextFile
def _merge_values(a: Any, b: Any) -> Any:
if not isinstance(a, dict) or not isinstance(b, dict):
return b
result = {}
for k, v_a in a.items():
result[k] = v_a
for k, v_b in b.items():
v_a = a.get(k)
if v_a is None:
result[k] = v_b
else:
result[k] = _merge_values(v_a, v_b)
return result
@dataclass @dataclass
class JsonFileProxy: class JsonFileProxy:
file: "JsonFile" file: "JsonFile"
@ -15,18 +31,14 @@ class JsonFileProxy:
def at(self, *path: str) -> Self: def at(self, *path: str) -> Self:
return JsonFileProxy(self.file, self.path + path) return JsonFileProxy(self.file, self.path + path)
def set(self, value: Any) -> None: def set(self, path: str | tuple[str, ...], value: Any) -> None:
if not self.path: if isinstance(path, str):
self.file.set(value) path = (path,)
data = self.file.data self.file.set(self.path + path, value)
*parts, last = self.path
for part in parts:
data = data.setdefault(part, {})
data[last] = value
def tag_here(self, tag: str = TAG) -> None: def tag(self, path: str | tuple[str, ...] = "_tag") -> None:
self.set(tag) self.set(path, TAG)
class JsonFile(File): class JsonFile(File):
@ -36,13 +48,32 @@ class JsonFile(File):
def at(self, *path: str) -> JsonFileProxy: def at(self, *path: str) -> JsonFileProxy:
return JsonFileProxy(self, path) return JsonFileProxy(self, path)
def set(self, value: Any) -> None: def get(self, path: str | tuple[str, ...]) -> Any:
self.data = value data = self.data
for part in path:
data = data[part]
return data
def tag(self, tag: str = TAG, key: str | list[str] = "_tag") -> None: def set(self, path: str | tuple[str, ...], value: Any) -> None:
if isinstance(key, str): if isinstance(path, str):
self.at(key).tag_here(tag=tag) path = (path,)
self.at(*key).tag_here(tag=tag)
if not path:
self.data = value
return
*parts, last = path
data = self.data
for part in parts:
data = data.setdefault(part, {})
data[last] = value
def merge(self, path: str | tuple[str, ...], value: Any) -> None:
self.set(path, _merge_values(self.get(path), value))
def tag(self, path: str | tuple[str, ...] = "_tag") -> None:
self.set(path, TAG)
def to_text(self) -> TextFile: def to_text(self) -> TextFile:
return TextFile(json.dumps(self.data)) return TextFile(json.dumps(self.data))

View file

@ -4,6 +4,7 @@ from typing import Any, Self
import toml import toml
from .file import File from .file import File
from .json import JsonFile
from .text import TextFile from .text import TextFile
@ -15,31 +16,33 @@ class TomlFileProxy:
def at(self, *path: str) -> Self: def at(self, *path: str) -> Self:
return TomlFileProxy(self.file, self.path + path) return TomlFileProxy(self.file, self.path + path)
def set(self, value: Any) -> None: def set(self, path: str | tuple[str, ...], value: Any) -> None:
if not self.path: if isinstance(path, str):
self.file.set(value) path = (path,)
data = self.file.data self.file.set(self.path + path, value)
*parts, last = self.path
for part in parts:
data = data.setdefault(part, {})
data[last] = value
class TomlFile(File): class TomlFile(File):
def __init__(self, data: Any = {}) -> None: def __init__(self, data: Any = {}) -> None:
self.data = data self.json = JsonFile(data)
def at(self, *path: str) -> TomlFileProxy: def at(self, *path: str) -> TomlFileProxy:
return TomlFileProxy(self, path) return TomlFileProxy(self, path)
def set(self, value: Any) -> None: def get(self, path: str | tuple[str, ...]) -> Any:
self.data = value return self.json.get(path)
def set(self, path: str | tuple[str, ...], value: Any) -> None:
self.json.set(path, value)
def merge(self, path: str | tuple[str, ...], value: Any) -> None:
self.json.merge(path, value)
def to_text(self) -> TextFile: def to_text(self) -> TextFile:
file = TextFile() file = TextFile()
file.tag(comment="#") file.tag(comment="#")
file.append(toml.dumps(self.data), newline=False) file.append(toml.dumps(self.json.data), newline=False)
return file return file
def to_bytes(self) -> bytes: def to_bytes(self) -> bytes: