Source code for structum_lab.config.manager
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2025 PythonWoods
"""
Provider di configurazione JSON per Structum.
Questa implementazione rappresenta il fallback minimale basato esclusivamente
sulla standard library Python. È pensata per garantire un funzionamento
immediato del framework in assenza di plugin avanzati.
Per casi d’uso complessi (multi-source, validazione, override per ambiente)
si raccomanda l’uso di provider esterni come Dynaconf.
"""
import json
from pathlib import Path
from typing import Any
CONFIG_DIR = Path.home() / ".structum"
CONFIG_FILE = CONFIG_DIR / "config.json"
[docs]
class JSONConfigProvider:
"""
Provider di configurazione basato su file JSON.
Caratteristiche:
- Persistenza automatica su filesystem
- Supporto a chiavi annidate tramite dot notation
- Nessuna dipendenza esterna
"""
[docs]
def __init__(self) -> None:
self._data: dict[str, Any] = {}
self._ensure_config_dir()
self.reload()
def _ensure_config_dir(self) -> None:
"""Assicura l’esistenza della directory di configurazione."""
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
[docs]
def get(self, key: str, default: Any = None) -> Any:
"""Restituisce il valore associato a una chiave (dot notation supportata)."""
value: Any = self._data
for part in key.split("."):
if isinstance(value, dict) and part in value:
value = value[part]
else:
return default
return value
[docs]
def set(self, key: str, value: Any) -> None:
"""Imposta una chiave di configurazione e salva immediatamente su disco."""
data = self._data
parts = key.split(".")
for part in parts[:-1]:
if part not in data or not isinstance(data[part], dict):
data[part] = {}
data = data[part]
data[parts[-1]] = value
self.save()
[docs]
def has(self, key: str) -> bool:
"""Verifica l’esistenza di una chiave di configurazione."""
value: Any = self._data
for part in key.split("."):
if isinstance(value, dict) and part in value:
value = value[part]
else:
return False
return True
[docs]
def save(self) -> None:
"""Scrive la configurazione corrente su file JSON."""
self._ensure_config_dir()
CONFIG_FILE.write_text(json.dumps(self._data, indent=2))
[docs]
def reload(self) -> None:
"""Ricarica la configurazione dal file JSON gestendo errori di corruzione."""
if not CONFIG_FILE.exists():
self._data = {"plugins": {}}
self.save()
return
try:
content = CONFIG_FILE.read_text()
# Gestione file vuoto o whitespace
if not content.strip():
self._data = {"plugins": {}}
self.save()
return
self._data = json.loads(content)
except (json.JSONDecodeError, OSError) as e:
# Strategia: Backup del file corrotto e reset
# In un framework reale, qui si loggherebbe un warning
backup_path = CONFIG_FILE.with_suffix(".json.bak")
CONFIG_FILE.rename(backup_path)
print(
f"⚠️ Configurazione corrotta! Backup salvato in {backup_path}. Reset impostazioni."
)
self._data = {"plugins": {}}
self.save()