118 lines
3.6 KiB
Python
118 lines
3.6 KiB
Python
"""Data parsing and serialization module for JSON, YAML, and TOML formats."""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any, Optional
|
|
|
|
import yaml
|
|
|
|
try:
|
|
import tomli
|
|
except ImportError:
|
|
tomli = None
|
|
|
|
try:
|
|
import tomllib
|
|
except ImportError:
|
|
tomllib = None
|
|
|
|
SUPPORTED_FORMATS = ["json", "yaml", "toml"]
|
|
|
|
|
|
def detect_format(file_path: str) -> str:
|
|
"""Detect file format from extension."""
|
|
ext = Path(file_path).suffix.lower()
|
|
format_map = {
|
|
".json": "json",
|
|
".yaml": "yaml",
|
|
".yml": "yaml",
|
|
".toml": "toml",
|
|
}
|
|
format_name = format_map.get(ext)
|
|
if format_name is None:
|
|
raise ValueError(f"Unsupported file extension: {ext}. Supported formats: {', '.join(SUPPORTED_FORMATS)}")
|
|
return format_name
|
|
|
|
|
|
def detect_format_from_content(content: str) -> Optional[str]:
|
|
"""Detect format from content (try parsing)."""
|
|
if content.strip().startswith("{") or content.strip().startswith("["):
|
|
try:
|
|
json.loads(content)
|
|
return "json"
|
|
except json.JSONDecodeError:
|
|
pass
|
|
if "=" in content and ":" not in content.split("=")[0]:
|
|
return "toml"
|
|
return "yaml"
|
|
|
|
|
|
def load_data(source: str, format: Optional[str] = None) -> Any:
|
|
"""Load data from a file path or string content."""
|
|
path = Path(source)
|
|
if path.exists() and path.is_file():
|
|
file_format = format or detect_format(source)
|
|
with open(source, "r", encoding="utf-8") as f:
|
|
content = f.read()
|
|
return parse_content(content, file_format)
|
|
else:
|
|
detected = format or detect_format_from_content(source)
|
|
if detected is None:
|
|
detected = "json"
|
|
return parse_content(source, detected)
|
|
|
|
|
|
def parse_content(content: str, format: str) -> Any:
|
|
"""Parse content string based on format."""
|
|
if format == "json":
|
|
return json.loads(content)
|
|
elif format == "yaml":
|
|
return yaml.safe_load(content)
|
|
elif format == "toml":
|
|
if tomli is not None:
|
|
return tomli.loads(content)
|
|
elif tomllib is not None:
|
|
return tomllib.loads(content)
|
|
else:
|
|
raise ImportError("Neither tomli nor tomllib is available for TOML parsing")
|
|
else:
|
|
raise ValueError(f"Unsupported format: {format}. Supported formats: {', '.join(SUPPORTED_FORMATS)}")
|
|
|
|
|
|
def dump_data(data: Any, format: str, output: Optional[str] = None, indent: int = 2) -> str:
|
|
"""Dump data to string or file based on format."""
|
|
if format == "json":
|
|
result = json.dumps(data, indent=indent, ensure_ascii=False)
|
|
elif format == "yaml":
|
|
result = yaml.dump(data, indent=indent, allow_unicode=True, sort_keys=False)
|
|
elif format == "toml":
|
|
try:
|
|
import tomli_w
|
|
result = tomli_w.dumps(data)
|
|
except ImportError:
|
|
try:
|
|
import tomllib
|
|
result = tomllib.dumps(data)
|
|
except ImportError:
|
|
raise ImportError("tomli_w or tomllib required for TOML output")
|
|
else:
|
|
raise ValueError(f"Unsupported format: {format}. Supported formats: {', '.join(SUPPORTED_FORMATS)}")
|
|
|
|
if output:
|
|
with open(output, "w", encoding="utf-8") as f:
|
|
f.write(result)
|
|
return ""
|
|
return result
|
|
|
|
|
|
def read_file(file_path: str) -> str:
|
|
"""Read file content."""
|
|
with open(file_path, "r", encoding="utf-8") as f:
|
|
return f.read()
|
|
|
|
|
|
def write_file(content: str, file_path: str) -> None:
|
|
"""Write content to file."""
|
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
f.write(content)
|