4.10. Gestire le eccezioni

Come molti linguaggi orientati agli oggetti, Python consente la manipolazione delle eccezioni tramite i blocchi try...except.

Nota
Python usa try...except per gestire le eccezioni e raise per generarle. Java e C++ usano try...catch per gestirle e throw per generarle.

Se conoscete già tutto riguardo le eccezioni, potete saltare questa sezione. Se siete rimasti bloccati programmando in un linguaggio minore che non ha la gestione delle eccezioni, o avete usato un linguaggio vero, ma senza usare le eccezioni, questa sezione è molto importante.

Le eccezioni sono ovunque in Python; virtualmente ogni modulo nella libreria standard di Python le usa e Python stesso le solleva in molte differenti circostanze. Le avete già viste spesso in questo libro.

In ognuno di questi casi, stavamo semplicemente giocando intorno all'IDE di Python: si incappa in un errore, l'eccezione viene stampata (a seconda del vostro IDE, in un intenzionale e stonante ombra di rosso) e niente più. Questa viene chiamata eccezione non gestita; quando l'eccezione veniva sollevata, non c'era codice che la notasse esplicitamente e se ne occupasse, perciò tutto proseguiva secondo il comportamento predefinito di Python, stampando alcune informazioni di debug e terminando il programma. Nell'IDE questo non è un grosso problema, ma se accade mentre il vostro programma Python reale è in esecuzione, l'intero programma va incontro ad un'improvvisa e poco piacevole interruzione. [6]

Un'eccezione non deve tuttavia comportare il crash dell'intero programma. Le eccezioni, quando sollevate, possono essere gestite. Qualche volta un'eccezione è autentica perché c'è un difetto nel vostro codice (come l'accesso ad una variabile inesistente), spesso però un'eccezione è qualcosa che avevate previsto. Se, ad esempio, state aprendo un file, questi potrebbe non esistere; se vi state connettendo ad un database, potrebbe non essere disponibile o potreste non avere i necessari requisiti di sicurezza per accedervi. Se sapete che una linea di codice potrebbe sollevare un'eccezione, dovreste gestirla usando un blocco try...except.

Esempio 4.21. Aprire un file inesistente

>>> fsock = open("/notthere", "r")      1
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
IOError: [Errno 2] No such file or directory: '/notthere'
>>> try:
...     fsock = open("/notthere")       2
... except IOError:                     3
...     print "The file does not exist, exiting gracefully"
... print "This line will always print" 4
The file does not exist, exiting gracefully
This line will always print
1 Usando la funzione built-in open, possiamo provare ad aprire un file in lettura (molte altre cose su open nella prossima sezione). Il file non esiste, quindi viene sollevata l'eccezione IOError. Dato che non abbiamo provveduto a gestire l'eccezione IOError, Python si limita a stampare alcune informazioni di debug di quello che è accaduto ed a terminare il programma.
2 Stiamo provando ad aprire lo stesso file inesistente ma questa volta lo stiamo facendo all'interno di un blocco try...except.
3 Quando il metodo open solleva un'eccezione IOError, siamo pronti per gestirla. except IOError: questa linea cattura l'eccezione ed esegue il nostro blocco di codice, che in questo caso si limita a stampare un messaggio di errore più cortese.
4 Una volta che un'eccezione è stata gestita, il processo continua con la linea che segue il blocco try...except. Notate che questa linea stamperà sempre se l'eccezione avviene oppure no. Se aveste veramente un file chiamato notthere nella vostra root directory, la chiamata ad open funzionerebbe, la clausola except sarebbe ignorata e questa linea verrebbe ancora eseguita.

Le eccezioni possono sembrare ostiche (dopotutto, se non catturate l'eccezione, il vostro intero programma terminerà), ma considerate l'alternativa. Vorreste veramente ottenere un oggetto file riferito ad un file non esistente? Dovreste in ogni caso controllarne la validità e se ve ne dimenticate, il vostro programma vi darebbe strani errori da qualche parte nelle linee seguenti e voi sareste costretti a ricontrollare l'intero codice, alla ricerca della causa del problema. Sono sicuro che lo avete già fatto; non è divertente. Con le eccezioni, gli errori vengono rilevati immediatamente e potete risolvere il problema alla radice, gestendoli in modo standard.

Ci sono molti altri usi delle eccezioni oltre alla gestione degli errori. Un uso comune nella libreria standard Python consiste nel provare ad importare un modulo e poi provare se funzioni o meno. Importare un modulo che non esiste causa il sollevamento dell'eccezione ImportError. Potete usarla per definire molteplici livelli di funzionalità, basati su quali moduli sono disponibili a run-time, o per supportare molteplici piattaforme (dove il codice specifico per ogni piattaforma è suddiviso in moduli differenti).

Potete inoltre definire le vostre stesse eccezioni, creando una classe che eredita dalla classe built-in Exception e sollevare le vostre eccezioni tramite il comando raise. Questo va al di fuori dallo scopo di questa sezione ma leggete gli ulteriori paragrafi se siete interessati.

Esempio 4.22. Supportare le funzionalità specifiche per piattaforma

Questo codice proviene dal modulo getpass, un modulo wrapper per ottenere password dagli utenti. Ottenere una password è significativamente differente da piattaforme UNIX, Windows e Mac OS, ma questo codice incapsula tutte differenze.

  # Bind the name getpass to the appropriate function
  try:
      import termios, TERMIOS                     1
  except ImportError:
      try:
          import msvcrt                           2
      except ImportError:
          try:
              from EasyDialogs import AskPassword 3
          except ImportError:
              getpass = default_getpass           4
          else:                                   5
              getpass = AskPassword
      else:
          getpass = win_getpass
  else:
      getpass = unix_getpass
1 termios è un modulo specifico UNIX, che rende disponibile un controllo di basso livello sul terminale di input. Se il modulo non è disponibile sul vostro sistema o non è supportato, Python solleverà l'eccezione ImportError, che noi cattureremo.
2 Ok, non abbiamo termios, perciò proviamo msvcrt, un modulo specifico di Windows, che fornisce un API per molte funzioni utili del Microsoft Visual C++ run-time. Se l'import fallisce, Python solleverà ImportError e noi cattureremo l'eccezione.
3 Se i primi due non funzionano, proviamo ad importare una funzione da EasyDialogs, che è un modulo specifico di Mac OS che provvede a funzioni di dialogo popup di vario tipo. Se, ancora una volta, questo import fallisce, Python solleverà l'eccezione ImportError, che noi cattureremo.
4 Nessuno di questi moduli specifici di piattaforma sono disponibili (il che è possibile, visto che Python è stato portato su molte differenti piattaforme), perciò dobbiamo puntare su una funzione standard di input delle password (che è definita da qualche altra parte nel modulo getpass). Notate cosa stiamo facendo: stiamo assegnando la funzione default_getpass alla variabile getpass. Se leggete la documentazione ufficiale di getpass, vi spiegherà che il modulo getpass definisce una funzione getpass. Questo è ciò che fa: passa getpass alla giusta funzione per la vostra piattaforma. Quindi, quando chiamate la funzione getpass, state realmente chiamando una funzione specifica per la vostra piattaforma, che questo codice ha fatto per voi. Non avete bisogno di conoscere o di far caso alla piattaforma su cui gira il vostro programma; limitatevi a chiamare getpass e questi farà sempre la cosa giusta.
5 Un blocco try...except può avere una clausola else, come l'istruzione if. Se nessuna eccezione è sollevata durante il blocco try, la clausola else viene eseguita di seguito. In questo caso, questo significa che from EasyDialogs import AskPassword funziona, per cui noi dovremmo legare getpass alla funzione AskPassword. Ognuno degli altri blocchi try...except hanno clausole else simili, per legare getpass alla funzione appropriata, quando troviamo un import che funzioni.

Ulteriori letture

Footnotes

[6] O, come alcuni pignoli farebbero notare, il vostro programma effettuerebbe un'azione illegale. Qualunque.