warnings — Advertencias no fatales¶
Propósito: | Entrega advertencias no fatales al usuario sobre los problemas encontrados al ejecutar un programa. |
---|
El módulo warnings
fue introducido por PEP 230 como una forma de
advertir a los programadores sobre los cambios en el lenguaje o las
características de la biblioteca en previsión de cambios incompatibles con
Python 3.0. También se puede usar para informar errores de configuración
recuperables o degradación de características de bibliotecas faltantes. Sin
embargo, es mejor entregar mensajes orientados al usuario a través del módulo
logging
, ya que las advertencias enviadas a la consola pueden perderse.
Como las advertencias no son fatales, un programa puede encontrar la misma
situación de advertencia muchas veces en el transcurso de la ejecución. El
módulo warnings
suprime los mensajes repetidos de la misma fuente para
reducir la molestia de ver la misma advertencia una y otra vez. La salida se
puede controlar caso por caso, utilizando las opciones de línea de comando para
el intérprete o llamando a las funciones que se encuentran en warnings
.
Categorías y Filtrado¶
Las advertencias se clasifican usando subclases de la clase de excepción
incorporada Warning
. Se describen varios valores estándar en la
documentación en línea para el módulo exceptions
, y se pueden agregar
advertencias personalizadas subclasificando Warning
.
Las advertencias se procesan según la configuración de filtros. Un filtro
consta de cinco partes: la acción, el mensaje
, la categoría
, el
módulo
y el número de línea
. La parte mensaje
del filtro es una
expresión regular que se utiliza para coincidir con el texto de advertencia.
La categoría
es un nombre de una clase de excepción. El módulo
contiene una expresión regular que se compara con el nombre del módulo que
genera la advertencia. El número de línea
se puede usar para cambiar el
manejo en casos específicos de una advertencia.
Cuando se genera una advertencia, se compara con todos los filtros registrados. El primer filtro que coincide controla la acción tomada para la advertencia. Si ningún filtro coincide, se toma la acción predeterminada. Las acciones comprendidas por el mecanismo de filtrado se enumeran en la tabla a continuación.
Acción | Significado |
---|---|
error | Convierte la advertencia en una excepción. |
ignore | Descarta la advertencia. |
always | Siempre emite una advertencia. |
default | Imprime la advertencia la primera vez que se genera desde cada ubicación. |
module | Imprime la advertencia la primera vez que se genera cada módulo. |
once | Imprime la advertencia la primera vez que se genera. |
Generando advertencias¶
La forma más simple de emitir una advertencia es llamar a warn()
con el
mensaje como argumento.
import warnings
print('Before the warning')
warnings.warn('This is a warning message')
print('After the warning')
Luego, cuando se ejecuta el programa, se imprime el mensaje.
$ python3 -u warnings_warn.py
Before the warning
warnings_warn.py:13: UserWarning: This is a warning message
warnings.warn('This is a warning message')
After the warning
Aunque se imprime la advertencia, el comportamiento predeterminado es continuar más allá de ese punto y ejecutar el resto del programa. Ese comportamiento se puede cambiar con un filtro.
import warnings
warnings.simplefilter('error', UserWarning)
print('Before the warning')
warnings.warn('This is a warning message')
print('After the warning')
En este ejemplo, la función simplefilter()
agrega una entrada a la lista de
filtros internos para indicarle al módulo warnings
que genere una
excepción cuando se emite una advertencia de UserWarning
.
$ python3 -u warnings_warn_raise.py
Before the warning
Traceback (most recent call last):
File "warnings_warn_raise.py", line 15, in <module>
warnings.warn('This is a warning message')
UserWarning: This is a warning message
El comportamiento del filtro también se puede controlar desde la línea de
comando utilizando la opción -W
para el intérprete. Especifique las
propiedades del filtro como una cadena con las cinco partes (acción, mensaje,
categoría, módulo y número de línea) separadas por dos puntos (:
). Por
ejemplo, si warnings_warn.py
se ejecuta con un filtro configurado para
generar un error en UserWarning
, se genera una excepción.
$ python3 -u -W "error::UserWarning::0" warnings_warn.py
Before the warning
Traceback (most recent call last):
File "warnings_warn.py", line 13, in <module>
warnings.warn('This is a warning message')
UserWarning: This is a warning message
Dado que los campos para mensaje
y módulo
se dejaron en blanco, se
interpretaron como coincidentes con cualquier cosa.
Filtrado con patrones¶
Para filtrar mediante reglas más complejas mediante programación, usa
filterwarnings()
. Por ejemplo, para filtrar según el contenido del texto
del mensaje, proporciona un patrón de expresión regular como argumento
mensaje
.
import warnings
warnings.filterwarnings('ignore', '.*do not.*',)
warnings.warn('Show this message')
warnings.warn('Do not show this message')
El patrón contiene «do not
», pero el mensaje real usa «Do no
». El
patrón coincide porque la expresión regular siempre se compila para buscar
coincidencias entre mayúsculas y minúsculas.
$ python3 warnings_filterwarnings_message.py
warnings_filterwarnings_message.py:14: UserWarning: Show this
message
warnings.warn('Show this message')
El siguiente programa de ejemplo genera dos advertencias.
import warnings
warnings.warn('Show this message')
warnings.warn('Do not show this message')
Se puede ignorar una de las advertencias utilizando el argumento de filtro en la línea de comando.
$ python3 -W "ignore:do not:UserWarning::0" warnings_filter.py
warnings_filter.py:12: UserWarning: Show this message
warnings.warn('Show this message')
Las mismas reglas de coincidencia de patrones se aplican al nombre del módulo
fuente que contiene la llamada que genera la advertencia. Suprime todos los
mensajes del módulo warnings_filter
pasando el nombre del módulo como
patrón al argumento módulo
.
import warnings
warnings.filterwarnings(
'ignore',
'.*',
UserWarning,
'warnings_filter',
)
import warnings_filter
Dado que el filtro está en su lugar, no se emiten advertencias cuando se
importa warnings_filter
.
$ python3 warnings_filterwarnings_module.py
Para suprimir solo el mensaje en la línea 13 de warnings_filter
, incluya el
número de línea como último argumento para filterwarnings()
. Usa el número
de línea real del archivo fuente para limitar el filtro o 0
para que el
filtro se aplique a todas las apariciones del mensaje.
import warnings
warnings.filterwarnings(
'ignore',
'.*',
UserWarning,
'warnings_filter',
13,
)
import warnings_filter
El patrón coincide con cualquier mensaje, por lo que los argumentos importantes son el nombre del módulo y el número de línea.
$ python3 warnings_filterwarnings_lineno.py
.../warnings_filter.py:12: UserWarning: Show this message
warnings.warn('Show this message')
Advertencias repetidas¶
Por defecto, la mayoría de los tipos de advertencias solo se imprimen la primera vez que ocurren en una ubicación determinada, con la «ubicación» definida por la combinación del módulo y el número de línea donde se genera la advertencia.
import warnings
def function_with_warning():
warnings.warn('This is a warning!')
function_with_warning()
function_with_warning()
function_with_warning()
Este ejemplo llama a la misma función varias veces, pero produce una sola advertencia.
$ python3 warnings_repeated.py
warnings_repeated.py:14: UserWarning: This is a warning!
warnings.warn('This is a warning!')
La acción "once"
se puede usar para suprimir instancias del mismo mensaje
desde diferentes ubicaciones.
import warnings
warnings.simplefilter('once', UserWarning)
warnings.warn('This is a warning!')
warnings.warn('This is a warning!')
warnings.warn('This is a warning!')
El texto del mensaje para todas las advertencias se guarda y solo se imprimen mensajes únicos.
$ python3 warnings_once.py
warnings_once.py:14: UserWarning: This is a warning!
warnings.warn('This is a warning!')
Del mismo modo, "module"
suprimirá los mensajes repetidos del mismo módulo,
sin importar el número de línea.
Funciones alternativas de entrega de mensajes¶
Normalmente, las advertencias se imprimen en sys.stderr
. Cambie ese
comportamiento reemplazando la función showwarning()
dentro del módulo
warnings
. Por ejemplo, para enviar advertencias a un archivo de registro
en lugar de un error estándar, reemplaza showwarning()
con una función que
registre la advertencia.
import warnings
import logging
def send_warnings_to_log(message, category, filename, lineno,
file=None, line=None):
logging.warning(
'%s:%s: %s:%s',
filename, lineno,
category.__name__, message,
)
logging.basicConfig(level=logging.INFO)
old_showwarning = warnings.showwarning
warnings.showwarning = send_warnings_to_log
warnings.warn('message')
Las advertencias se emiten con el resto de los mensajes de registro cuando se
llama a warn()
.
$ python3 warnings_showwarning.py
WARNING:root:warnings_showwarning.py:28: UserWarning:message
Formateo¶
Si las advertencias deben ir al error estándar, pero deben reformatearse,
reemplaza formatwarning()
.
import warnings
def warning_on_one_line(message, category, filename, lineno,
file=None, line=None):
return '-> {}:{}: {}:{}'.format(
filename, lineno, category.__name__, message)
warnings.warn('Warning message, before')
warnings.formatwarning = warning_on_one_line
warnings.warn('Warning message, after')
La función de formato debe devolver una sola cadena que contenga la representación de la advertencia que se mostrará al usuario.
$ python3 -u warnings_formatwarning.py
warnings_formatwarning.py:19: UserWarning: Warning message,
before
warnings.warn('Warning message, before')
-> warnings_formatwarning.py:21: UserWarning:Warning message,
after
Nivel de pila en advertencias¶
Por defecto, el mensaje de advertencia incluye la línea de origen que lo
generó, cuando está disponible. Sin embargo, no siempre es útil ver la línea
de código con el mensaje de advertencia real. En cambio, a warn()
se le
puede decir qué tan alto tiene que ir la pila para encontrar la línea que llamó
a la función que contiene la advertencia. De esa manera, los usuarios de una
función obsoleta pueden ver dónde se llama la función, en lugar de la
implementación de la función.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/usr/bin/env python3
# encoding: utf-8
import warnings
def old_function():
warnings.warn(
'old_function() is deprecated, use new_function()',
stacklevel=2)
def caller_of_old_function():
old_function()
caller_of_old_function()
|
En este ejemplo, warn()
necesita subir la pila dos niveles, uno para sí
mismo y otro para old_function()
.
$ python3 warnings_warn_stacklevel.py
warnings_warn_stacklevel.py:14: UserWarning: old_function() is deprecated,
use new_function()
old_function()
Ver también
- Documentación de la biblioteca estándar para warnings
- PEP 230 – Marco de advertencias
exceptions
– Clases base para excepciones y advertencias.logging
– Un mecanismo alternativo para entregar advertencias es escribir en el registro.