Parliamo

project-cherry.dev

Intermedio12 min

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.

python
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.0

Gestire 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.

python
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.

python
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.

python
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: XXXXXX00A00X000X

Validazione 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.

python
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 obbligatoria

Da ricordare

  • Cattura sempre eccezioni specifiche: except ValueError anziché un generico except:
  • Usa finally per 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