Scopo | Fornisce avvertimenti non fatali all'utente circa problemi incontrati durante l'esecuzione di un programma. |
Versione Python | 2.1 e successive |
A partire dal 1 gennaio 2021 le versioni 2.x di Python non sono piu' supportate. Ti invito a consultare la corrispondente versione 3.x dell'articolo per il modulo warnings
Il modulo warnings era stato introdotto in PEP 230 come modo per avvertire i programmatori di cambiamenti nel linguaggio o di caratteristiche delle librerie come anticipazione di modifiche ad incompatibilità retroattiva che sarebbero intervenute con Python 3.0. Visto che gli avvertimenti non sono bloccanti, un programma potrebbe incorrere in questi avvertimenti molte volte nel corso della sua esecuzione. Il modulo warnings sopprime gli avvertimenti ripetuti generati dalla stessa fonte per ridurre il disturbo causato dal vedere lo stesso messaggio più e più volte. E' possibile controllare i messaggi stampati a livello di caso per caso usando l'opzione
-Wdell'interprete oppure chiamando funzioni del modulo warnings dal proprio codice.
Gli avvertimenti sono categorizzati usando sotto classi della classe di eccezione built-in Warning . Diversi valori standard sono descritti nella documentazione , e se ne possono aggiungere di propri subclassando da Warning per creare una nuova classe
I messaggi sono filtrati utilizzando impostazioni controllate tramite l'opzione
-Wdell'interprete. Un filtro consiste di 5 parti, azione , messaggio , categoria , modulo e numero di riga . Quando un messaggio viene generato, viene confrontato con tutti i filtri registrati. Il primo filtro che corrisponde controlla l'azione intrapresa per l'avvertimento. Se non ci sono corripondenze, viene intrapresa l'azione predefinita.
Le azioni comprese dal meccanismo di filtro sono:
La parte message del filtro è una espressione regolare utilizzate per confrontare il testo dell'avvertimento.
cateogory è il nome di una classe di eccezione, come sopra descritto
module contiene una espressione regolare per il confronto con il nome del modulo che genera l'avvertimento
line number può essere usato per cambiare la gestione di occorrenze specifiche di un avvertimento. Si usa
0per applicare il filtro su tutte le occorrenze.
Il modo più semplice di emettere un avvertimento nel proprio codice è di chiamare
warn()
passando il messaggio come argomento:
import warnings
print "Prima dell'avvertimento"
warnings.warn('Questo è un messaggio di avvertimento')
print "Dopo l'avvertimento"
Successivamente, quando il programma viene eseguito, viene stampato
python warnings_warn.py Prima dell'avvertimento warnings_warn.py:7: UserWarning: Questo è un messaggio di avvertimento warnings.warn('Questo è un messaggio di avvertimento') Dopo l'avvertimento
Sebbene il messaggio sia stato stampato, il comportamento predefinito è quello di procedere oltre l'avvertimento ed eseguire il resto del programma. Si può modificare questo comportamento con un filtro:
import warnings
warnings.simplefilter('error', UserWarning)
print "Prima dell'avvertimento"
warnings.warn('Questo è un messaggio di avvertimento')
print "Dopo l'avvertimento"
Il filtro dice al modulo warnings di sollevare una eccezione quando l'avvertimento viene rilasciato
$ python warnings_warn_raise.py Prima dell'avvertimento Traceback (most recent call last): File "warnings_warn_raise.py", line 9, inwarnings.warn('Questo è un messaggio di avvertimento') UserWarning: Questo è un messaggio di avvertimento
Si può anche controllare il comportamento del filtro da riga di comando. Ad esempio se si riesamina
warnings_warn.pye si imposta il filtro per sollevare un errore su UserWarning , si vede l'eccezione:
$ python -W "error::UserWarning::0" warnings_warn.py Prima dell'avvertimento Traceback (most recent call last): File "warnings_warn.py", line 7, inwarnings.warn('Questo è un messaggio di avvertimento') UserWarning: Questo è un messaggio di avvertimento
Visto che i campi per message e module sono vuoti, sono interpretati per trovare corrispondenza con qualsiasi cosa.
Per filtrare da programma su regole più complesse si usa
filterwarnings()
. Ad esempio per filtrare in base al contenuto del testo del messaggio:
import warnings
warnings.filterwarnings('ignore', '.*non mostrare-*',)
warnings.warn('Mostra questo messaggio')
warnings.warn('Non mostrare questo messaggio')
Il modello contiene
"non mostrare", ma il vero messaggio usa
"Non mostrare". La corrispondenza viene trovata perchè l'espressione regolare è sempre compilata per ignorare le maiuscole/minuscole.
$ python warnings_filterwarnings_message.py warnings_filterwarnings_message.py:8: UserWarning: Mostra questo messaggio warnings.warn('Mostra questo messaggio')
Eseguendo questo script dalla riga di comando
import warnings
warnings.warn('Mostra questo messaggio')
warnings.warn('Non mostrare questo messaggio')
si ottiene:
$ python -W "ignore:non mostrare:UserWarning::0" warnings_filtering.py warnings_filtering.py:6: UserWarning: Mostra questo messaggio warnings.warn('Mostra questo messaggio')
Le stesse regole per la corrispondenza del modello si applicano al nome del modulo sorgente che contiene la chiamata di avvertimento. Per eliminare tutti gli avvertimenti dal modulo
warnings_filtering:
import warnings
warnings.warn('Mostra questo messaggio')
warnings.warn('Non mostrare questo messaggio')
warnings.filterwarnings(
'ignore',
'.*',
UserWarning,
'warnings_filtering'
)
import warnings_filtering
Visto che è attivo il filtro, nessun avvertimento viene emesso, all'importazione di
warnings_filtering
$ python warnings_filterwarnings_module.py
Per sopprimere solo l'avvertimento nella riga 14 di
warnings_filtering:
import warnings
warnings.filterwarnings(
'ignore',
'.*',
UserWarning,
'warnings_filtering',
14
)
import warnings_filtering
$ python warnings_filterwarnings_lineno.py /home/robby/Dropbox/Code/python/pymotw-it/dumpscripts/warnings_filtering.py:6: UserWarning: Mostra questo messaggio warnings.warn('Mostra questo messaggio') /home/robby/Dropbox/Code/python/pymotw-it/dumpscripts/warnings_filtering.py:7: UserWarning: Non mostrare questo messaggio warnings.warn('Non mostrare questo messaggio')
Nella modalità predefinita la maggior parte degli avvertimenti sono stampati solo la prima volta che vengono rilevati in una certa locazione, laddove per locazione si intende la combinazione modulo/numero riga.
import warnings
def function_with_warning():
warnings.warn('Questo è un avvertimento!')
function_with_warning()
function_with_warning()
function_with_warning()
$ python warnings_repeated.py warnings_repeated.py:7: UserWarning: Questo è un avvertimento! warnings.warn('Questo è un avvertimento!')
L'azione " once " (una volta) può essere usata per sopprimere istanze dello stesso messaggio da locazioni diverse.
import warnings
warnings.simplefilter('once', UserWarning)
warnings.warn('Questo è un avvertimento!')
warnings.warn('Questo è un avvertimento!')
warnings.warn('Questo è un avvertimento!')
$ python warnings_once.py warnings_once.py:8: UserWarning: Questo è un avvertimento! warnings.warn('Questo è un avvertimento!')
Similarmente, " module " sopprimerà messaggi ripetuti dallo stesso modulo, non importa in quale numero di riga.
Normalmente i messaggi vengono stampati verso
sys.stderr
. E' possibilie modificare questo comportamento sostituiendo la funzione
showwarning()
all'interno del modulo
warnings
. Ad esempio se si vuole che i messaggi siano destinati ad un file di log invece che a
stderr
, si potrebbe rimpiazzare
showwarnings()
con una funzione tipo questa:
import warnings
import logging
logging.basicConfig(level=logging.INFO)
def send_warnings_to_log(message, category, filename, lineno, file=None):
logging.warning(
'%s:%s: %s:%s' %
(filename, lineno, category.__name__, message))
return
old_showwarning = warnings.showwarning
warnings.showwarning = send_warnings_to_log
warnings.warn('Questo è un messaggio di avvertimento!')
Quando viene chiamato
warn()
, gli avvertimenti sono emessi assieme al resto dei messaggi di log.
$ python warnings_showwarning.py WARNING:root:warnings_showwarning.py:18: UserWarning:Questo è un messaggio di avvertimento!
Se sta bene che gli avvertimenti vadano verso
stderr
, ma non piace la formattazione, è possibile sostituire
formatwarning()
.
import warnings
def warning_on_one_line(message, category, filename, lineno, file=None, line=None):
return ' %s:%s: %s:%s' % (filename, lineno, category.__name__, message)
warnings.warn('Messaggio di avvertimento, prima')
warnings.formatwarning = warning_on_one_line
warnings.warn('Messaggio di avvertimento, dopo')
$ python warnings_formatwarning.py warnings_formatwarning.py:9: UserWarning: Messaggio di avvertimento, prima warnings.warn('Messaggio di avvertimento, prima') warnings_formatwarning.py:11: UserWarning:Messaggio di avvertimento, dopo
Si noterà che nella modalità predefinita il messaggio di avvertimento include la riga sorgente che lo ha generato, quando disponibile. Non è sempre così utile vedere la riga di codice assieme all'effettivo messaggio di avvertimento. Si può dire a
warn()
di quando deve risalire lo stack per trovare la riga che ha chiamato la funzione contenente l'avvertimento. In questo modo gli utilizzatori di funzioni deprecate vedono dove è stata chiamata la funzione e non l'implementazione della funzione.
import warnings
def old_function():
warnings.warn(
'old_function() è deprecata, utilizzare new_function() al suo posto',
stacklevel=2)
def caller_of_old_function():
old_function()
caller_of_old_function()
Si noti che nell'esempio
warn()
deve risalire 2 livelli di stack, uno per se stesso ed un altro per
old_function()
-
$ python warnings_warn_stacklevel.py warnings_warn_stacklevel.py:12: UserWarning: old_function() è deprecata, utilizzare new_function() al suo posto old_function()