diff --git a/API/Classes/Base/FileClass.py b/API/Classes/Base/FileClass.py index a4f07c8c4..a83f7a0ef 100644 --- a/API/Classes/Base/FileClass.py +++ b/API/Classes/Base/FileClass.py @@ -1,49 +1,76 @@ #import ujson as json import json +import logging +from pathlib import Path +from typing import Any, Dict + +logger = logging.getLogger(__name__) + class File: + """ + Utility class for safe and consistent file I/O operations. + Provides JSON read/write helpers with proper error propagation + and improved reliability. + """ + @staticmethod - def readFile(path): - try: - with open(path, mode="r") as f: - data = json.loads(f.read()) - return data - except IndexError: - raise IndexError - except IOError: - raise IOError - except OSError: - raise OSError + def _validate_path(path: str) -> Path: + """Validate and return a Path object. Ensures parent directory exists.""" + p = Path(path) + + if not p.parent.exists(): + raise FileNotFoundError(f"Directory does not exist: {p.parent}") + + return p @staticmethod - def writeFile(data, path): - try: - with open(path, mode="w") as f: - f.write(json.dumps(data, ensure_ascii=True, indent=4, sort_keys=False)) - except (IOError, IndexError): - raise IndexError - except OSError: - raise OSError + def readFile(path: str) -> Dict[str, Any]: + """ + Read JSON file and return parsed data. + Raises standard exceptions (FileNotFoundError, PermissionError, JSONDecodeError). + """ + p = File._validate_path(path) + + logger.debug(f"Reading file from: {p}") + + with p.open(mode="r", encoding="utf-8") as f: + return json.load(f) @staticmethod - def writeFileUJson(data, path): - try: - with open(path, mode="w") as f: - f.write(json.dumps(data)) - except (IOError, IndexError): - raise IndexError - except OSError: - raise OSError + def writeFile(data: Dict[str, Any], path: str) -> None: + """ + Write data to file in formatted JSON using atomic write. + """ + p = File._validate_path(path) + temp_path = p.with_suffix(p.suffix + ".tmp") + + logger.debug(f"Writing formatted JSON to: {p} (atomic)") + + with temp_path.open(mode="w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=True, indent=4, sort_keys=False) + + temp_path.replace(p) + + @staticmethod + def writeFileUJson(data: Dict[str, Any], path: str) -> None: + """ + Write data to file in compact JSON format using atomic write. + """ + p = File._validate_path(path) + temp_path = p.with_suffix(p.suffix + ".tmp") + + logger.debug(f"Writing compact JSON to: {p} (atomic)") + + with temp_path.open(mode="w", encoding="utf-8") as f: + json.dump(data, f, separators=(",", ":")) + + temp_path.replace(p) @staticmethod - def readParamFile(path): - try: - with open(path, mode="r") as f: - data = json.loads(f.read()) - return data - except IndexError: - raise IndexError - except IOError: - raise IOError - except OSError: - raise OSError \ No newline at end of file + def readParamFile(path: str) -> Dict[str, Any]: + """ + Read parameter JSON file. + Alias for readFile for semantic clarity. + """ + return File.readFile(path) \ No newline at end of file