-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathjson_file_functions.py
More file actions
90 lines (68 loc) · 2.86 KB
/
json_file_functions.py
File metadata and controls
90 lines (68 loc) · 2.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
"""
Functions for reading and writing json files
"""
import json
import logging
import os
import tempfile
from pathlib import Path
logger = logging.getLogger(__name__)
def read_json_file(file_path: Path) -> dict | list | None:
"""
Safely reads and parses a JSON file.
"""
if not file_path.exists():
logger.warning("File not found: %s", json.dumps(str(file_path)))
return None
try:
data = json.loads(file_path.read_text(encoding='utf-8'))
logger.info("Successfully read data from %s", json.dumps(str(file_path)))
return data
except json.JSONDecodeError as e:
logger.error("Invalid JSON format in %s: %s", json.dumps(str(file_path)), e)
return None
except Exception as e:
logger.error("Unexpected error reading %s: %s", json.dumps(str(file_path)), e)
return None
def write_json_file(file_path: Path, data: dict | list) -> bool:
"""
Writes data to a JSON file atomically.
"""
file_path = Path(file_path).absolute()
if not file_path.parent.exists():
file_path.parent.mkdir(parents=True, exist_ok=True)
logger.debug("Created %s", json.dumps(str(file_path.parent.as_posix())))
temp_file_path: Path | None = None
try:
with tempfile.NamedTemporaryFile(mode='w', dir=str(file_path.parent), encoding='utf-8', suffix=".tmp", delete=False) as tf:
# Get file path from tempfile instance
temp_file_path = Path(tf.name)
json.dump(data, tf, indent=4)
tf.flush()
os.fsync(tf.fileno())
# Atomic swap
temp_file_path.replace(file_path)
logger.info("Successfully saved to %s", json.dumps(str(file_path)))
return True
except (KeyboardInterrupt, SystemExit):
logger.error("Write interrupted for %s. Cleaning up.", json.dumps(str(file_path)))
if temp_file_path and temp_file_path.exists():
temp_file_path.unlink()
raise
except Exception as e:
logger.error("Failed to write to %s: %s", json.dumps(str(file_path)), e)
if temp_file_path and temp_file_path.exists():
temp_file_path.unlink()
return False
def load_config(file_path: Path) -> dict | list | None:
"""Alias for read_json_file, specifically for configuration files."""
return read_json_file(file_path)
def save_config(file_path: Path, config_data: dict | list) -> bool:
"""Alias for write_json_file, specifically for configuration files."""
return write_json_file(file_path, config_data)
def load_cache(file_path: Path) -> dict | list | None:
"""Alias for read_json_file, specifically for cache files."""
return read_json_file(file_path)
def save_cache(file_path: Path, cache_data: dict | list) -> bool:
"""Alias for write_json_file, specifically for cache files."""
return write_json_file(file_path, cache_data)