mirror of
https://github.com/malmeloo/FindMy.py.git
synced 2026-04-22 16:55:37 +02:00
refactor(reports): improve Serializable base class
This commit is contained in:
@@ -5,6 +5,10 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Generic, Self, TypeVar
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
logging.getLogger(__name__)
|
||||
|
||||
@@ -38,16 +42,37 @@ class Closable(ABC):
|
||||
pass
|
||||
|
||||
|
||||
class Serializable(ABC):
|
||||
T = TypeVar("T", bound=dict)
|
||||
|
||||
|
||||
class Serializable(Generic[T], ABC):
|
||||
"""ABC for serializable classes."""
|
||||
|
||||
@abstractmethod
|
||||
def serialize(self) -> dict:
|
||||
"""Serialize the object to a JSON-serializable dictionary."""
|
||||
def to_json(self, dst: str | Path | None = None, /) -> T:
|
||||
"""
|
||||
Export the current state of the object as a JSON-serializable dictionary.
|
||||
|
||||
If an argument is provided, the output will also be written to that file.
|
||||
|
||||
The output of this method is guaranteed to be JSON-serializable, and passing
|
||||
the return value of this function as an argument to `Serializable.from_json`
|
||||
will always result in an exact copy of the internal state as it was when exported.
|
||||
|
||||
You are encouraged to save and load object states to and from disk whenever possible,
|
||||
to prevent unnecessary API calls or otherwise unexpected behavior.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def deserialize(cls, data: dict) -> Serializable:
|
||||
"""Deserialize the object from a JSON-serializable dictionary."""
|
||||
def from_json(cls, val: str | Path | T, /) -> Self:
|
||||
"""
|
||||
Restore state from a previous `Closable.to_json` export.
|
||||
|
||||
If given a str or Path, it must point to a json file from `Serializable.to_json`.
|
||||
Otherwise, it should be the Mapping itself.
|
||||
|
||||
See `Serializable.to_json` for more information.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
34
findmy/util/files.py
Normal file
34
findmy/util/files.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""Utilities to simplify reading and writing data from and to files."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from collections.abc import Mapping
|
||||
from pathlib import Path
|
||||
from typing import TypeVar, cast
|
||||
|
||||
T = TypeVar("T", bound=Mapping)
|
||||
|
||||
|
||||
def save_and_return_json(data: T, dst: str | Path | None) -> T:
|
||||
"""Save and return a JSON-serializable data structure."""
|
||||
if dst is None:
|
||||
return data
|
||||
|
||||
if isinstance(dst, str):
|
||||
dst = Path(dst)
|
||||
|
||||
dst.write_text(json.dumps(data, indent=4))
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def read_data_json(val: str | Path | T) -> T:
|
||||
"""Read JSON data from a file if a path is passed, or return the argument itself."""
|
||||
if isinstance(val, str):
|
||||
val = Path(val)
|
||||
|
||||
if isinstance(val, Path):
|
||||
val = cast("T", json.loads(val.read_text()))
|
||||
|
||||
return val
|
||||
Reference in New Issue
Block a user