101 lines
3.3 KiB
Python
101 lines
3.3 KiB
Python
import json
|
|
import traceback
|
|
from datetime import datetime, date
|
|
from decimal import Decimal
|
|
from types import SimpleNamespace
|
|
from pathlib import Path
|
|
from typing import Any, Optional, Literal, Union
|
|
from pydantic import BaseModel
|
|
|
|
|
|
def save_to_disk(
|
|
data: Union[str, bytes, dict, list, Any],
|
|
file_path: str,
|
|
as_json: Optional[bool] = None,
|
|
encoding: str = "utf-8",
|
|
indent: int = 4,
|
|
add_timestamp: bool = False,
|
|
mode: Literal["overwrite", "append"] = "overwrite",
|
|
) -> dict:
|
|
"""
|
|
Salva dados em disco local de forma segura, com tratamento automático
|
|
para objetos não serializáveis (ex.: SimpleNamespace, Decimal, datetime, BaseModel).
|
|
"""
|
|
|
|
def convert(obj: Any):
|
|
"""Converte qualquer objeto para formato serializável em JSON."""
|
|
if obj is None:
|
|
return None
|
|
if isinstance(obj, (str, int, float, bool)):
|
|
return obj
|
|
if isinstance(obj, bytes):
|
|
return obj.decode("utf-8", errors="ignore")
|
|
if isinstance(obj, Decimal):
|
|
return float(obj)
|
|
if isinstance(obj, (datetime, date)):
|
|
return obj.isoformat()
|
|
if isinstance(obj, SimpleNamespace):
|
|
return {k: convert(v) for k, v in vars(obj).items()}
|
|
if isinstance(obj, BaseModel):
|
|
return obj.model_dump()
|
|
if isinstance(obj, list):
|
|
return [convert(i) for i in obj]
|
|
if isinstance(obj, dict):
|
|
return {k: convert(v) for k, v in obj.items()}
|
|
# fallback final — stringifica o objeto
|
|
return str(obj)
|
|
|
|
try:
|
|
path = Path(file_path)
|
|
|
|
# Garante que o diretório existe
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Adiciona timestamp ao nome se necessário
|
|
if add_timestamp:
|
|
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
|
stem = path.stem
|
|
suffix = path.suffix or ".json"
|
|
path = path.with_name(f"{stem}_{timestamp}{suffix}")
|
|
|
|
# Detecta automaticamente se deve salvar como JSON
|
|
if as_json is None:
|
|
as_json = isinstance(data, (dict, list, SimpleNamespace, BaseModel))
|
|
|
|
write_mode = (
|
|
"wb" if isinstance(data, bytes) else "a" if mode == "append" else "w"
|
|
)
|
|
|
|
if isinstance(data, bytes):
|
|
with open(path, write_mode) as f:
|
|
f.write(data)
|
|
elif as_json:
|
|
# Converte antes de salvar
|
|
safe_data = convert(data)
|
|
with open(path, write_mode, encoding=encoding) as f:
|
|
json.dump(safe_data, f, ensure_ascii=False, indent=indent)
|
|
else:
|
|
if not isinstance(data, str):
|
|
data = str(data)
|
|
with open(path, write_mode, encoding=encoding) as f:
|
|
f.write(data)
|
|
|
|
file_size = path.stat().st_size if path.exists() else 0
|
|
print(f"[OK] Arquivo salvo: {path} ({file_size} bytes)")
|
|
|
|
return {
|
|
"success": True,
|
|
"path": str(path.resolve()),
|
|
"size": file_size,
|
|
"error": None,
|
|
}
|
|
|
|
except Exception as e:
|
|
error_message = f"[ERRO] Falha ao salvar arquivo '{file_path}': {e}\n{traceback.format_exc()}"
|
|
print(error_message)
|
|
return {
|
|
"success": False,
|
|
"path": str(file_path),
|
|
"size": 0,
|
|
"error": str(e),
|
|
}
|