warnings - Avvisi non fatali

Scopo Fornisce avvertimenti non fatali all'utente circa problemi incontrati durante l'esecuzione di un programma.
Versione Python 2.1 e successive

Descrizione

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

-W
dell'interprete oppure chiamando funzioni del modulo warnings dal proprio codice.

Categorizzare e Filtrare

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

-W
dell'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:

  • error: Trasforma l'avvertimento in una eccezione
  • ignore: Ignora l'avvertimento
  • always: Emette sempre l'avvertimento
  • default: Stampa l'avvertimento la prima volta che viene generato da ciascuna locazione
  • module: Stampa l'avvertimento la prima volta che viene generato da ogni modulo
  • once: Stampa l'avvertimento solo la prima volta che viene generato

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

0
per applicare il filtro su tutte le occorrenze.

Generare Avvertimenti

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, in 
    warnings.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.py
e 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, in 
    warnings.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.

Filtrare tramite Modelli

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')

Avvertimenti Ripetuti

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.

Funzioni di Consegna Messaggio Alternative

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!

Formattazione

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

Livello di Stack negli Avvertimenti

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()

Vedere anche:

warnings
La documentazione della libreria standard per questo modulo
PEP 230
Warning Framework
eccezioni
Classi base per eccezioni ed avvertimenti