PyMOTW: warnings
4 Julio 2008
Traducción de PyMOTW: warnings, (Python Module of the Week, el Módulo Python de la Semana), la columna semanal de Doug Hellmann.
Gestionar alertas que no son errores a través del módulo warnings.
| Módulo: | warnings |
|---|---|
| Propósito: | Mostrar alertas al usuario sobre problemas encontrados durante la ejecución de un programa. |
| Versión de Python | 2.1 y posterior |
Descripción
El módulo warnings fue presentado en el PEP 230 como una manera de advertir a
los programadores sobre cambios en el lenguaje o características en
anticipación a cambios incompatibles que llegarían con Python 3.0. Siendo que
las advertencias no son fatales, un programa puede encontrarse con la misma
situación digna de advertencia durante la ejecución. El módulo warnings
suprime advertencias que se repitan en el mismo código fuente para reducir la
molestia de mostrar el mismo mensaje una y otra vez. Puedes controlar el
mensaje mostrado en cada caso usando la opción -W del intérprete o
llamando funciones de warnings desde tu código.
Categorías y filtros
Las advertencias están categorizadas usando subclases de la excepción
incorporada Warning. Varios valores estándar están descritos en la
documentación, y puedes añadir tus propios derivando de Warning para crear
una nueva clase.
Los mensajes se filtran usando configuraciones que se controlan a través de la
opción -W del intérprete. Un filtro consiste en 5 partes, la acción, el
mensaje, la categoría, el módulo y el número de línea. Cuando una advertencia
es generada, es comparada con todos los filtros registrados. El primer filtro
que coincida controla la acción tomada para la advertencia. Si ningún filtro
coincide, la acción por defecto es tomada.
Las acciones que entiende el mecanismo de filtros son:
| error | Convierte la advertencia en una excepción. |
|---|---|
| ignore | Descarta la advertencia. |
| always | Siempre emite la advertencia. |
| default | Muestra la advertencia la primera vez que es generada desde cada lugar. |
| module | Muestra la advertencia la primera vez que es generada desde cada módulo. |
| once | Muestra la advertencia la primera vez que es generada. |
La parte del mensaje del filtro es una expresión regular que es usada para comparar el texto de la advertencia.
La categoría es un nombre de una clase de excepción, como se ha descrito antes.
El módulo contiene una expresión regular para ser comparada con el nombre del módulo que genera la advertencia.
Y el número de línea puede ser usado para cambiar el manejo en ocurrencias específicas de una advertencia. Usa 0 como número de línea para que el filtro se aplique a todas las ocurrencias.
Generando advertencias
La manera más simple de emitir una advertencia desde tu propio código es tan
sólo invocar warnings.warn() con el mensaje como argumento:
import warnings
print 'Antes de la advertencia'
warnings.warn('Este es un mensaje de advertencia')
print 'Después de la advertencia'
Entonces, cuando tu programa ejecute, el mensaje es mostrado:
$ python warnings_warn.py
Antes de la advertencia
warnings_warn.py:14: UserWarning: Este es un mensaje de advertencia
warnings.warn('Este es un mensaje de advertencia')
Después de la advertencia
A pesar de que la advertencia es mostrada, el comportamiento por defecto es continuar después de la advertencia y ejecutar el resto del programa. Podemos cambiar este comportamiento con un filtro:
import warnings
warnings.simplefilter('error', UserWarning)
print 'Antes de la advertencia'
warnings.warn('Este es un mensaje de advertencia')
print 'Después de la advertencia'
Este filtro le dice al módulo warnings que eleve una excepción cuando se
emite la advertencia.
$ python warnings_warn_raise.py
Antes de la advertencia
Traceback (most recent call last):
File "warnings_warn_raise.py", line 16, in
warnings.warn('Este es un mensaje de advertencia')
File "/usr/lib/python2.5/warnings.py", line 62, in warn
globals)
File "/usr/lib/python2.5/warnings.py", line 102, in warn_explicit
raise message
UserWarning: Este es un mensaje de advertencia
Podemos, obviamente, también controlar el comportamiento del filtro desde la
línea de comando. Por ejemplo, si retornamos a warnings_warn.py y
configuramos el filtro para elevar un error en UserWarning, vemos la
excepción:
$ python -W 'error::UserWarning::0' warnings_warn.py
Antes de la advertencia
Traceback (most recent call last):
File "warnings_warn.py", line 14, in
warnings.warn('Este es un mensaje de advertencia')
File "/usr/lib/python2.5/warnings.py", line 62, in warn
globals)
File "/usr/lib/python2.5/warnings.py", line 102, in warn_explicit
raise message
UserWarning: Este es un mensaje de advertencia
Como dejé los campos para mensaje y módulo vacíos, fueron interpretados como si coincidiese todo.
Filtrando con patrones
Para filtrar en base a reglas más complejas desde el programa, usa
filterwarnings(). Por ejemplo, para filtrar en base al contenido del texto
del mensaje:
import warnings
warnings.filterwarnings('ignore', '^no*',)
warnings.warn('Muestra este mensaje')
warnings.warn('No muestres este mensaje')
Nota que uso "no" en el patrón, pero "No" en la advertencia. La expresión regular siempre es compilada para buscar coincidencias independientemente de las mayúsculas y minúsculas.
$ python warnings_filterwarnings_message.py
warnings_filterwarnings_message.py:15: UserWarning: Muestra este mensaje
warnings.warn('Muestra este mensaje')
Ejecutando este código desde la línea de comando:
import warnings
warnings.warn('Muestra este mensaje')
warnings.warn('No muestres este mensaje')
resulta:
$ python -W 'ignore:no:UserWarning::0' warnings_filtering.py
warnings_filtering.py:13: UserWarning: Muestra este mensaje
warnings.warn('Muestra este mensaje')
Las mismas reglas de comparación se aplican al nombre del módulo fuente que
contiene la invocación de la advertencia. Para suprimir todas las advertencias del
módulo warnings_filtering:
import warnings
warnings.filterwarnings('ignore',
'.*',
UserWarning,
'warnings_filtering',
)
import warnings_filtering
A partir de que el filtro está en uso, ninguna advertencia es emitida cuando
warnings_filtering es importado:
$ python warnings_filterwarnings_module.py
Para suprimir sólo la advertencia en la línea 14 de warnings_filtering:
import warnings
warnings.filterwarnings('ignore',
'.*',
UserWarning,
'warnings_filtering',
14
)
import warnings_filtering
$ python warnings_filterwarnings_lineno.py
/home/ers/Projects/PyMOTW-es/warnings/warnings_filtering.py:13: UserWarning: Muestra este mensaje
warnings.warn('Muestra este mensaje')
Advertencias repetidas
Por defecto, la mayoría de las advertencias sólo se muestran la primera vez que ocurren en un lugar definido, donde lugar es definido como la combinación de módulo y número de línea.
import warnings
def function_with_warning():
warnings.warn('Esta es una advertencia!')
function_with_warning()
function_with_warning()
function_with_warning()
$ python warnings_repeated.py
warnings_repeated.py:14: UserWarning: Esta es una advertencia!
warnings.warn('Esta es una advertencia!')
La acción "once" puede ser usada para suprimir instancias del mismo mensaje desde diferentes lugares.
import warnings
warnings.simplefilter('once', UserWarning)
warnings.warn('Esta es una advertencia!')
warnings.warn('Esta es una advertencia!')
warnings.warn('Esta es una advertencia!')
$ python warnings_once.py
warnings_once.py:15: UserWarning: Esta es una advertencia!
warnings.warn('Esta es una advertencia!')
De manera similar, "module" suprimirá mensajes repetidos desde el mismo módulo, sin importar el número de línea.
Funciones alternativas de entrega de mensajes
Normalmente, las advertencias se muestran en sys.stderr. Puedes cambiar ese
comportamiento reemplazando la función showwarning() dentro del módulo
warnings. Por ejemplo, si quieres que las advertencias vayan un archivo de registro en lugar de stderr, podrías reemplazar showwarning() con una función como ésta:
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('Este es un mensaje de advertencia')
Así, cuando warnings.warn() es invocada, las advertencias son emitidas con el resto de los mensajes de log.
$ python warnings_showwarning.py
WARNING:root:warnings_showwarning.py:25: UserWarning:Este es un mensaje de advertencia
Formato
Si está bien que las advertencias vayan a stderr, pero no te gusta el formato, puedes reemplazar formatwarning().
import warnings
def warning_on_one_line(message, category, filename, lineno):
return '%s:%s: %s:%s' % (filename, lineno, category.__name__, message)
warnings.warn('Este es un mensaje de advertencia, antes')
warnings.formatwarning = warning_on_one_line
warnings.warn('Este es un mensaje de advertencia, después')
$ python warnings_formatwarning.py
warnings_formatwarning.py:16: UserWarning: Este es un mensaje de advertencia, antes
warnings.warn('Este es un mensaje de advertencia, antes')
warnings_formatwarning.py:18: UserWarning:Este es un mensaje de advertencia, después
Niveles de pila de adventencias
Notarás que por defecto los mensajes de advertencia incluyen la línea de código
que las generaron, siempre que estén disponibles. No es tan útil ver la línea de código con el mensaje de advertencia presente. En cambio, puedes decirle a warnings.warn() cuan lejos tiene que ir en la pila para encontrar la línea que llamó la función que contenía la advertencia. Así, usuarios de una función obsoleta ven dónde se invoca la función, en lugar de la implementación de la función.
import warnings
def old_function():
warnings.warn(
'old_function() es obsoleta, usa new_function() en su lugar',
stacklevel=2)
def caller_of_old_function():
old_function()
caller_of_old_function()
Notarás que en este ejemplo warnings.warn() necesita subir dos niveles en la pila, uno para si misma y uno para old_function().
$ python warnings_warn_stacklevel.py
warnings_warn_stacklevel.py:19: UserWarning: old_function() es obsoleta, usa new_function() en su lugar
old_function()
Referencias
PEP 230 -- Warning Framework
Python Module of the Week Home
Descarga el código de prueba
Copyright 2008 Doug Hellmann
blog comments powered by Disqus
