Python e gestione delle eccezioni: come usare <strong>context manager</strong> per evitare errori e pulire le risors

by theArchitect
SHARE
Python e gestione delle eccezioni: come usare <strong>context manager</strong> per evitare errori e pulire le risors
© Guida-HTML5.it

Introduzione

Quando si parla di gestione delle eccezioni in Python, spesso si pensa subito a try, except e finally. Sono strumenti fondamentali, ma in molti casi esiste un approccio più elegante e sicuro: usare i context manager.

Un context manager è un oggetto che gestisce automaticamente l’apertura e la chiusura di una risorsa, anche se durante l’esecuzione si verifica un’eccezione. Il caso più comune è l’uso di with per lavorare con file, connessioni a database, lock di threading o risorse di rete.

Questo tutorial si concentra su un sotto-argomento molto pratico: come usare i context manager per gestire correttamente le eccezioni e prevenire problemi di risorse non rilasciate. È un tema utile perché riduce il codice boilerplate, rende il programma più leggibile e diminuisce il rischio di bug difficili da individuare.

Codice completo

from pathlib import Path


class FileLogger:
    """
    Context manager personalizzato per scrivere log su file.
    Garantisce che il file venga sempre chiuso, anche in caso di errore.
    """

    def __init__(self, filename):
        self.filename = filename
        self.file = None

    def __enter__(self):
        # Apertura della risorsa
        self.file = open(self.filename, "a", encoding="utf-8")
        return self

    def write(self, message):
        if self.file is None:
            raise RuntimeError("Il file non è aperto.")
        self.file.write(message + "n")

    def __exit__(self, exc_type, exc_value, traceback):
        # Pulizia garantita della risorsa
        if self.file:
            self.file.close()

        # Se è avvenuta un´eccezione, la registriamo in modo semplice
        if exc_type is not None:
            print(f"Errore intercettato: {exc_type.__name__}: {exc_value}")

        # False = non sopprimere l´eccezione, lasciala propagare
        return False


def process_data(input_path, log_path):
    with FileLogger(log_path) as logger:
        logger.write("Inizio elaborazione dati")

        with open(input_path, "r", encoding="utf-8") as f:
            for line_number, line in enumerate(f, start=1):
                line = line.strip()

                # Simuliamo una validazione semplice
                if not line:
                    raise ValueError(f"Riga vuota trovata alla linea {line_number}")

                number = int(line)
                result = 100 / number

                logger.write(f"Linea {line_number}: {number} -

Spiegazione

Vediamo il funzionamento del codice passo per passo.

1. La classe FileLogger

Questa classe implementa il protocollo del context manager tramite due metodi speciali:

  • __enter__: viene eseguito quando si entra nel blocco with. Qui apriamo il file in append.
  • __exit__: viene eseguito quando si esce dal blocco, sia in caso di esecuzione normale sia in caso di eccezione. Qui chiudiamo sempre il file.

Questo è il punto chiave: la chiusura della risorsa è garantita, anche se il codice interno fallisce.

2. Il parametro exc_type in __exit__

Quando nel blocco with si verifica un’eccezione, Python passa a __exit__ informazioni utili:

  • exc_type: il tipo dell’eccezione
  • exc_value: il messaggio o valore associato
  • traceback: lo stack trace

Nel nostro esempio stampiamo un messaggio semplice con il tipo di errore. È utile per il debug o per registrare eventi in un log.

3. Perché return False?

Restituire False da __exit__ significa che non vogliamo sopprimere l’eccezione. In altre parole, dopo aver fatto la pulizia, lasciamo che l’errore continui a propagarsi e venga gestito più in alto, nel blocco try/except del chiamante.

Se invece si restituisse True, l’eccezione verrebbe silenziata. Questo comportamento va usato con molta cautela, perché può nascondere bug importanti.

4. La funzione process_data

Questa funzione legge un file contenente numeri, uno per riga, e calcola 100 / numero. Durante l’elaborazione possono verificarsi più problemi:

  • una riga vuota genera ValueError
  • una stringa non convertibile in intero genererebbe ValueError
  • un numero pari a zero causerebbe ZeroDivisionError

Il context manager del logger assicura che il file di log venga chiuso correttamente anche se una di queste eccezioni interrompe il flusso.

5. Il vantaggio rispetto a try/finally

Senza context manager, dovresti scrivere codice simile a questo:

file = open("app.log", "a", encoding="utf-8")
try:
    file.write("Messaggion")
    # operazioni...
finally:
    file.close()

Funziona, ma è più verboso e più facile da sbagliare. Con with, invece, il codice è più compatto e comunica chiaramente l’intenzione: “questa risorsa ha un ciclo di vita limitato e va gestita automaticamente”.

Best practice

  • Usa sempre with per le risorse esterne: file, socket, lock, connessioni, stream.
  • Non sopprimere eccezioni senza motivo: restituisci False in __exit__ se vuoi farle propagare.
  • Tieni separata la logica di business dalla gestione della risorsa: il context manager deve occuparsi soprattutto di apertura e chiusura.
  • Preferisci context manager già pronti quando possibile, invece di reinventare la ruota.
  • Registra gli errori in modo utile: se intercetti un’eccezione, fornisci un messaggio chiaro o un log strutturato.
  • Evita di fare troppo lavoro in __exit__: deve essere rapido e affidabile, soprattutto in caso di errore.

Riepilogo

I context manager sono uno strumento essenziale nella gestione delle eccezioni in Python. Ti permettono di scrivere codice più pulito, sicuro e leggibile, soprattutto quando lavori con risorse che devono essere sempre rilasciate.

In questo tutorial hai visto come creare un context manager personalizzato con __enter__ e __exit__, come intercettare le eccezioni durante l’uso di with e perché è spesso meglio di un semplice try/finally.

Se vuoi migliorare la qualità del tuo codice Python, questo è un passo molto importante: imparare a gestire bene le eccezioni non significa solo “catturare gli errori”, ma anche progettare il flusso del programma in modo robusto.

Approfondisci con risorse ufficiali

  • Python documentation - context manager: documentazione ufficiale sul protocollo with e sui metodi __enter__ / __exit__.
  • Python documentation - built-in exceptions: elenco completo delle eccezioni integrate in Python.
  • PEP 343: introduzione formale all’istruzione with e ai context manager.
  • contextlib: modulo standard con strumenti utili come contextmanager, ExitStack e altri helper avanzati.

SHARE