Gestione Errori
Scrivi codice robusto validando importi, codici fiscali e date nel sistema di fatturazione dello studio legale.
Gestione Errori
Un sistema di fatturazione che accetta qualsiasi dato senza controlli è un problema in attesa di esplodere. Importi negativi, codici fiscali malformati, file mancanti: tutto può succedere. Python gestisce queste situazioni con le eccezioni, un meccanismo che separa il flusso normale da quello di errore senza riempire il codice di if annidati.
Try/Except: validare importi fattura
Il blocco try/except cattura gli errori e permette al programma di reagire invece di crashare. Specificare il tipo di eccezione è fondamentale: un generico except: nasconde bug reali.
def valida_importo(valore):
"""Converte e valida un importo fattura."""
try:
importo = float(valore)
except ValueError:
print(f"Errore: '{valore}' non è un numero valido")
return None
if importo <= 0:
print(f"Errore: l'importo deve essere positivo, ricevuto {importo}")
return None
if importo > 1_000_000:
print(f"Attenzione: importo molto elevato ({importo:.2f} EUR), verificare")
return importo
# Test con diversi input
valida_importo("1500.50") # 1500.5
valida_importo("abc") # Errore: 'abc' non è un numero valido
valida_importo("-200") # Errore: l'importo deve essere positivo, ricevuto -200.0Gestire file e rete: CSV mancante, API irraggiungibile
Nel mondo reale, i file possono mancare e le API possono non rispondere. Gestire questi casi è obbligatorio per un software professionale.
import csv
def carica_clienti(percorso_file):
"""Carica la lista clienti da CSV, gestendo file mancante o malformato."""
try:
with open(percorso_file, "r", encoding="utf-8") as f:
lettore = csv.DictReader(f)
lista_clienti = list(lettore)
except FileNotFoundError:
print(f"File non trovato: {percorso_file}")
print("Creo una lista clienti vuota")
return []
except PermissionError:
print(f"Permessi insufficienti per leggere {percorso_file}")
return []
print(f"Caricati {len(lista_clienti)} clienti")
return lista_clienti
clienti = carica_clienti("clienti_studio.csv")Finally e else: chiusura risorse
Il blocco else si esegue solo quando non ci sono errori. Il blocco finally si esegue sempre, perfetto per operazioni di pulizia come chiudere connessioni o scrivere log.
import json
def carica_configurazione(percorso):
"""Carica la configurazione dello studio con gestione completa degli errori."""
try:
f = open(percorso, "r", encoding="utf-8")
configurazione = json.load(f)
except FileNotFoundError:
print("File di configurazione mancante, uso valori predefiniti")
return {"tariffa_oraria": 150.0, "aliquota_iva": 22}
except json.JSONDecodeError as e:
print(f"File di configurazione corrotto: {e}")
return {"tariffa_oraria": 150.0, "aliquota_iva": 22}
else:
print(f"Configurazione caricata: {len(configurazione)} parametri")
return configurazione
finally:
print("Operazione di caricamento configurazione completata")
config = carica_configurazione("config_studio.json")
print(f"Tariffa oraria: {config['tariffa_oraria']} EUR")Eccezioni personalizzate: FatturaInvalida, ClienteNonTrovato
Quando le eccezioni built-in non bastano, puoi creare le tue. Questo rende il codice più espressivo e il debug più rapido.
class FatturaInvalida(Exception):
"""Errore per fatture con dati non conformi."""
def __init__(self, numero_fattura, motivo):
self.numero_fattura = numero_fattura
self.motivo = motivo
super().__init__(f"Fattura {numero_fattura} non valida: {motivo}")
class ClienteNonTrovato(Exception):
"""Errore quando un cliente non esiste nell'archivio."""
def __init__(self, codice_fiscale):
self.codice_fiscale = codice_fiscale
super().__init__(f"Nessun cliente con CF: {codice_fiscale}")
def cerca_cliente(lista_clienti, codice_fiscale):
for cliente in lista_clienti:
if cliente["codice_fiscale"] == codice_fiscale:
return cliente
raise ClienteNonTrovato(codice_fiscale)
# Uso pratico
lista_clienti = [
{"nome": "Laura", "cognome": "Verdi", "codice_fiscale": "VRDLRA85M41H501Z"},
]
try:
cliente = cerca_cliente(lista_clienti, "XXXXXX00A00X000X")
except ClienteNonTrovato as e:
print(f"Errore: {e}")
# Errore: Nessun cliente con CF: XXXXXX00A00X000XValidazione a catena: pipeline di controlli
In un sistema reale, i dati passano attraverso più controlli prima di essere accettati. Ecco come strutturare una pipeline di validazione per le fatture.
def valida_codice_fiscale(cf):
if len(cf) != 16:
raise ValueError(f"Codice fiscale deve avere 16 caratteri, ricevuti {len(cf)}")
if not cf.isalnum():
raise ValueError("Codice fiscale deve contenere solo lettere e numeri")
return cf.upper()
def valida_fattura(dati):
"""Pipeline di validazione completa per una fattura."""
errori = []
# Controllo importo
try:
importo = float(dati.get("importo", 0))
if importo <= 0:
errori.append("Importo deve essere positivo")
except (ValueError, TypeError):
errori.append("Importo non è un numero valido")
# Controllo codice fiscale
try:
valida_codice_fiscale(dati.get("codice_fiscale", ""))
except ValueError as e:
errori.append(str(e))
# Controllo descrizione
if not dati.get("descrizione", "").strip():
errori.append("Descrizione obbligatoria")
if errori:
raise FatturaInvalida(
dati.get("numero", "SCONOSCIUTO"),
"; ".join(errori)
)
return True
# Test della pipeline
try:
valida_fattura({
"numero": "FT-0042",
"importo": -500,
"codice_fiscale": "ABC",
"descrizione": "",
})
except FatturaInvalida as e:
print(f"Validazione fallita: {e}")
# Validazione fallita: Fattura FT-0042 non valida: Importo deve essere positivo;
# Codice fiscale deve avere 16 caratteri, ricevuti 3; Descrizione obbligatoriaDa ricordare
- ●Cattura sempre eccezioni specifiche:
except ValueErroranziché un genericoexcept: - ●Usa
finallyper operazioni di pulizia che devono avvenire sempre (chiudere file, liberare risorse) - ●Crea eccezioni personalizzate quando quelle built-in non descrivono bene il problema
- ●Una pipeline di validazione raccoglie tutti gli errori prima di rifiutare i dati, così l'utente corregge tutto in una volta
- ●Non usare le eccezioni per il flusso di controllo normale: sono per situazioni eccezionali