warnings — Control de advertencias

Código fuente: Lib/warnings.py


Los mensajes de advertencia suelen emitirse en situaciones en las que es útil alertar al usuario de alguna condición en un programa, cuando esa condición (normalmente) no justifica que se haga una excepción y se termine el programa. Por ejemplo, se puede emitir una advertencia cuando un programa utiliza un módulo obsoleto.

Los programadores de Python emiten advertencias llamando a la función warn() definida en este módulo. (Los programadores de C usan PyErr_WarnEx(); ver Manejo de excepciones para más detalles)

Los mensajes de advertencia se escriben normalmente en sys.stderr, pero su disposición puede cambiarse de manera flexible, desde ignorar todas las advertencias hasta convertirlas en excepciones. La disposición de las advertencias puede variar en función de la warning category, el texto del mensaje de advertencia y la ubicación de la fuente donde se emite. Las repeticiones de una advertencia particular para la misma ubicación de la fuente son típicamente suprimidas.

El control de las advertencias consta de dos etapas: en primer lugar, cada vez que se emite una advertencia, se determina si se debe emitir un mensaje o no; en segundo lugar, si se debe emitir un mensaje, se le da formato y se imprime utilizando un gancho establecido por el usuario.

La determinación de si se debe emitir un mensaje de advertencia está controlada por el warning filter, que es una secuencia de reglas y acciones que coinciden. Se pueden añadir reglas al filtro llamando a filterwarnings() y restablecer su estado por defecto llamando a resetwarnings().

La impresión de los mensajes de advertencia se realiza llamando a showwarning(), que puede ser anulado; la implementación por defecto de esta función da formato al mensaje llamando a formatwarning(), que también está disponible para su uso en implementaciones personalizadas.

Ver también

logging.captureWarnings() permite manejar todas las advertencias con la infraestructura de registro estándar.

Categorías de advertencia

Hay una serie de excepciones incorporadas que representan categorías de advertencia. Esta categorización es útil para poder filtrar grupos de advertencias.

Aunque técnicamente se trata de built-in exceptions, están documentadas aquí, porque pertenecen al mecanismo de advertencias.

El código de usuario puede definir categorías de advertencia adicionales mediante la subclasificación de una de las categorías de advertencia estándar. Una categoría de advertencia siempre debe ser una subclase de la clase Warning class.

Actualmente están definidas las siguientes clases de categorías de advertencias:

Clase

Descripción

Warning

Esta es la clase principal para todas las clases que pertenecen a la categoría de advertencia. Es una subclase de Exception.

UserWarning

La categoría por defecto para warn().

DeprecationWarning

Categoría principal para advertencias sobre características obsoletas cuando esas advertencias están destinadas a otros desarrolladores de Python (ignoradas por defecto, a menos que sean activadas por el código en __main__).

SyntaxWarning

Categoría principal para las advertencias sobre características sintácticas dudosas.

RuntimeWarning

Categoría principal para las advertencias sobre características dudosas de tiempo de ejecución.

FutureWarning

Categoría principal para las advertencias sobre características obsoletas cuando esas advertencias están destinadas a los usuarios finales de aplicaciones escritas en Python.

PendingDeprecationWarning

Categoría principal para las advertencias sobre las características que serán desaprobadas en el futuro (ignoradas por defecto).

ImportWarning

Categoría principal para las advertencias que se activan durante el proceso de importación de un módulo (se ignoran por defecto).

UnicodeWarning

Categoría principal para las advertencias relacionadas con Unicode.

BytesWarning

Categoría principal para las advertencias relacionadas con bytes y bytearray.

ResourceWarning

Base category for warnings related to resource usage (ignored by default).

Distinto en la versión 3.7: Anteriormente DeprecationWarning y FutureWarning se distinguían en función de si una característica se eliminaba por completo o se cambiaba su comportamiento. Ahora se distinguen en función de su público objetivo y de la forma en que son manejados por los filtros de advertencia predeterminados.

El filtro de advertencias

El filtro de advertencias controla si las advertencias se ignoran, se muestran o se convierten en errores (planteando una excepción).

Conceptualmente, el filtro de advertencias mantiene una lista ordenada de especificaciones de filtros; cualquier advertencia específica se compara con cada especificación de filtro de la lista por turno hasta que se encuentra una coincidencia; el filtro determina la disposición de la coincidencia. Cada entrada es una tupla de la forma (action, message, category, module, lineno), donde:

  • action es una de las siguientes cadenas:

    Valor

    Disposición

    "default"

    imprime la primera ocurrencia de advertencias coincidentes para cada lugar (módulo + número de línea) donde se emite la advertencia

    "error"

    convertir las advertencias de coincidencia en excepciones

    "ignore"

    nunca imprime advertencias que coincidan

    "always"

    siempre imprime advertencias que coinciden

    "module"

    imprime la primera ocurrencia de advertencias coincidentes para cada módulo en el que se emite la advertencia (independientemente del número de línea)

    "once"

    imprime sólo la primera aparición de advertencias coincidentes, independientemente de la ubicación

  • message is a string containing a regular expression that the start of the warning message must match, case-insensitively. In -W and PYTHONWARNINGS, message is a literal string that the start of the warning message must contain (case-insensitively), ignoring any whitespace at the start or end of message.

  • category es una clase (una subclase de Warning) de la cual la categoría de advertencia debe ser una subclase para poder coincidir.

  • module is a string containing a regular expression that the start of the fully qualified module name must match, case-sensitively. In -W and PYTHONWARNINGS, module is a literal string that the fully qualified module name must be equal to (case-sensitively), ignoring any whitespace at the start or end of module.

  • lineno es un número entero que el número de línea donde ocurrió la advertencia debe coincidir, o 0 para coincidir con todos los números de línea.

Como la clase Warning se deriva de la clase Excepción incorporada, para convertir una advertencia en un error simplemente lanzamos category(message).

Si se informa de una advertencia y no coincide con ningún filtro registrado, se aplica la acción «por defecto» (de ahí su nombre).

Descripción de los filtros de advertencia

El filtro de advertencias se inicializa con -W options pasado a la línea de comandos del intérprete de Python y la variable de entorno PYTHONWARNINGS. El intérprete guarda los argumentos de todas las entradas suministradas sin interpretación en sys.warnoptions; el módulo warnings los analiza cuando se importa por primera vez (las opciones no válidas se ignoran, después de imprimir un mensaje a sys.stderr).

Los filtros de advertencia individuales se especifican como una secuencia de campos separados por dos puntos:

action:message:category:module:line

El significado de cada uno de estos campos es como se describe en El filtro de advertencias. Cuando se enumeran varios filtros en una sola línea (como en PYTHONWARNINGS), los filtros individuales se separan por comas y los filtros que se enumeran más tarde tienen prioridad sobre los que se enumeran antes de ellos (ya que se aplican de izquierda a derecha, y los filtros aplicados más recientemente tienen prioridad sobre los anteriores).

Los filtros de advertencia comúnmente utilizados se aplican a todas las advertencias, a las advertencias de una categoría particular o a las advertencias planteadas por módulos o paquetes particulares. Algunos ejemplos:

default                      # Show all warnings (even those ignored by default)
ignore                       # Ignore all warnings
error                        # Convert all warnings to errors
error::ResourceWarning       # Treat ResourceWarning messages as errors
default::DeprecationWarning  # Show DeprecationWarning messages
ignore,default:::mymodule    # Only report warnings triggered by "mymodule"
error:::mymodule             # Convert warnings to errors in "mymodule"

Filtro de advertencia predeterminado

Por defecto, Python instala varios filtros de advertencia, que pueden ser anulados por la opción de línea de comandos -W, la variable de entorno PYTHONWARNINGS y llamadas a filterwarnings().

En versiones regulares, el filtro de advertencia por defecto tiene las siguientes entradas (en orden de precedencia):

default::DeprecationWarning:__main__
ignore::DeprecationWarning
ignore::PendingDeprecationWarning
ignore::ImportWarning
ignore::ResourceWarning

En una compilación de depuración, la lista de filtros de advertencia predeterminados está vacía.

Distinto en la versión 3.2: DeprecationWarning es ahora ignorado por defecto además de PendingDeprecationWarning.

Distinto en la versión 3.7: DeprecationWarning se muestra de nuevo por defecto cuando se activa directamente por código en __main__.

Distinto en la versión 3.7: BytesWarning ya no aparece en la lista de filtros por defecto y en su lugar se configura a través de sys.warnoptions cuando -b se especifica dos veces.

Anulando el filtro por defecto

Los desarrolladores de aplicaciones escritas en Python pueden desear ocultar todas las advertencias de nivel de Python a sus usuarios por defecto, y sólo mostrarlas cuando se realizan pruebas o se trabaja de otra manera en la aplicación. El atributo sys.warnoptions utilizado para pasar las configuraciones de los filtros al intérprete puede utilizarse como marcador para indicar si las advertencias deben ser desactivadas o no:

import sys

if not sys.warnoptions:
    import warnings
    warnings.simplefilter("ignore")

Se aconseja a los desarrolladores de pruebas de código Python que se aseguren de que todas las advertencias se muestren por defecto para el código que se está probando, usando un código como:

import sys

if not sys.warnoptions:
    import os, warnings
    warnings.simplefilter("default") # Change the filter in this process
    os.environ["PYTHONWARNINGS"] = "default" # Also affect subprocesses

Por último, se aconseja a los desarrolladores de shells interactivos que ejecuten el código de usuario en un espacio de nombres distinto de __main__ que se aseguren de que los mensajes DeprecationWarning se hagan visibles por defecto, utilizando un código como el siguiente (donde user_ns es el módulo utilizado para ejecutar el código introducido interactivamente):

import warnings
warnings.filterwarnings("default", category=DeprecationWarning,
                                   module=user_ns.get("__name__"))

Eliminación temporal de las advertencias

Si está utilizando un código que sabe que va a provocar una advertencia, como una función obsoleta, pero no quiere ver la advertencia (incluso cuando las advertencias se han configurado explícitamente a través de la línea de comandos), entonces es posible suprimir la advertencia utilizando el gestor de contexto catch_warnings:

import warnings

def fxn():
    warnings.warn("deprecated", DeprecationWarning)

with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    fxn()

Mientras que dentro del gestor de contexto todas las advertencias serán simplemente ignoradas. Esto le permite utilizar el código conocido como obsoleto sin tener que ver la advertencia, mientras que no suprime la advertencia para otro código que podría no ser consciente de su uso de código obsoleto. Nota: esto sólo puede garantizarse en una aplicación de un solo hilo. Si dos o más hilos utilizan el gestor de contexto catch_warnings al mismo tiempo, el comportamiento es indefinido.

Advertencias de prueba

Para probar las advertencias planteadas por el código, use el administrador de contexto catch_warnings. Con él puedes mutar temporalmente el filtro de advertencias para facilitar tus pruebas. Por ejemplo, haz lo siguiente para capturar todas las advertencias levantadas para comprobar:

import warnings

def fxn():
    warnings.warn("deprecated", DeprecationWarning)

with warnings.catch_warnings(record=True) as w:
    # Cause all warnings to always be triggered.
    warnings.simplefilter("always")
    # Trigger a warning.
    fxn()
    # Verify some things
    assert len(w) == 1
    assert issubclass(w[-1].category, DeprecationWarning)
    assert "deprecated" in str(w[-1].message)

También se puede hacer que todas las advertencias sean excepciones usando error en lugar de always. Una cosa que hay que tener en cuenta es que si una advertencia ya se ha planteado debido a una regla de once o default, entonces no importa qué filtros estén establecidos, la advertencia no se verá de nuevo a menos que el registro de advertencias relacionadas con la advertencia se haya borrado.

Una vez que se cierra el gestor de contexto, el filtro de advertencias se restablece al estado en que se encontraba al entrar en el contexto. Esto evita que las pruebas cambien el filtro de advertencias de forma inesperada entre una prueba y otra, y que se produzcan resultados indeterminados en las pruebas. La función showwarning() del módulo también se restaura a su valor original. Nota: esto sólo puede garantizarse en una aplicación de un solo hilo. Si dos o más hilos utilizan el gestor de contexto catch_warnings al mismo tiempo, el comportamiento es indefinido.

Cuando se prueban múltiples operaciones que plantean el mismo tipo de advertencia, es importante probarlas de manera que se confirme que cada operación plantea una nueva advertencia (por ejemplo, establecer advertencias que se planteen como excepciones y comprobar que las operaciones plantean excepciones, comprobar que la longitud de la lista de advertencias siga aumentando después de cada operación, o bien suprimir las entradas anteriores de la lista de advertencias antes de cada nueva operación).

Actualización del código para las nuevas versiones de las dependencias

Las categorías de advertencia que interesan principalmente a los desarrolladores de Python (más que a los usuarios finales de aplicaciones escritas en Python) se ignoran por defecto.

En particular, esta lista de «ignorados por defecto» incluye DeprecationWarning (para cada módulo excepto``__main__``), lo que significa que los desarrolladores deben asegurarse de probar su código con advertencias típicamente ignoradas hechas visibles para recibir notificaciones oportunas de futuros cambios del API a última hora(ya sea en la biblioteca estándar o en paquetes de terceros).

En el caso ideal, el código tendrá un conjunto de pruebas adecuado, y el corredor de pruebas se encargará de habilitar implícitamente todas las advertencias cuando se ejecuten las pruebas (el corredor de pruebas proporcionado por el módulo unittest hace esto).

En casos menos ideales, las aplicaciones pueden ser revisadas por el uso de interfaces desaprobadas pasando -Wd al intérprete de Python (esto es la abreviatura de -W default) o ajustando PYTHONWARNINGS=default en el entorno. Esto permite el manejo por defecto de todas las advertencias, incluyendo aquellas que son ignoradas por defecto. Para cambiar la acción que se lleva a cabo para las advertencias encontradas puede cambiar el argumento que se pasa a -W (por ejemplo -W error). Consulte el indicador -W para obtener más detalles sobre lo que es posible.

Funciones Disponibles

warnings.warn(message, category=None, stacklevel=1, source=None)

Emite una advertencia, o tal vez la ignorar o lanza una excepción. El argumento category, si se da, debe ser un warning category class; por defecto es UserWarning. Alternativamente, message puede ser una instancia Warning, en cuyo caso category será ignorada y se usará message.__class__. En este caso, el texto del mensaje será str(message). Esta función hace una excepción si la advertencia particular emitida es convertida en un error por el warnings filter. El argumento stacklevel puede ser usado por funciones de envoltura escritas en Python, como esta:

def deprecation(message):
    warnings.warn(message, DeprecationWarning, stacklevel=2)

Esto hace que la advertencia se refiera al invocador de deprecation(), en lugar de a la fuente de deprecation() en sí (ya que esta última perdería el propósito del mensaje de advertencia).

source, si se suministra, es el objeto destruido que emitió un ResourceWarning.

Distinto en la versión 3.6: Añadido parámetro source.

warnings.warn_explicit(message, category, filename, lineno, module=None, registry=None, module_globals=None, source=None)

Se trata de una interfaz de bajo nivel para la funcionalidad de warn(), pasando explícitamente el mensaje, la categoría, el nombre de archivo y el número de línea, y opcionalmente el nombre del módulo y el registro (que debería ser el diccionario __warningregistry__ del módulo). El nombre del módulo por defecto es el nombre de archivo con .py desmembrado; si no se pasa el registro, la advertencia nunca se suprime. message debe ser una cadena y category una subclase de Warning o message puede ser una instancia Warning, en cuyo caso category será ignorada.

module_globals, si se suministra, debe ser el espacio de nombres global en uso por el código para el que se emite la advertencia. (Este argumento se utiliza para apoyar la visualización de la fuente de los módulos que se encuentran en los archivos zip o en otras fuentes de importación que no son del sistema de archivos).

source, si se suministra, es el objeto destruido que emitió un ResourceWarning.

Distinto en la versión 3.6: Añade el parámetro source.

warnings.showwarning(message, category, filename, lineno, file=None, line=None)

Escriba una advertencia en un archivo. La implementación por defecto llama formatwarning(message, category, filename, lineno, line) y escribe la cadena resultante a file, que por defecto es sys.stderr. Puede reemplazar esta función con cualquier interlocutor asignando a warnings.showwarning. line es una línea de código fuente que se incluirá en el mensaje de advertencia; si no se proporciona line, showwarning() intentará leer la línea especificada por nombre de archivo y lineno.

warnings.formatwarning(message, category, filename, lineno, line=None)

Formatea una advertencia de la manera estándar. Esto retorna una cadena que puede contener nuevas líneas incrustadas y termina en una nueva línea. line es una línea de código fuente a incluir en el mensaje de advertencia; si line no se suministra, formatwarning() intentará leer la línea especificada por el nombre de fichero filename y lineno.

warnings.filterwarnings(action, message='', category=Warning, module='', lineno=0, append=False)

Inserta una entrada en la lista de warnings filter specifications. La entrada se inserta por defecto en el frente; si append es verdadero, se inserta al final. Esto comprueba los tipos de los argumentos, compila las expresiones regulares message y module, y las inserta como una tupla en la lista de filtros de aviso. Las entradas más cercanas al principio de la lista anulan las entradas posteriores de la lista, si ambas coinciden con una advertencia en particular. Los argumentos omitidos predeterminan un valor que coincide con todo.

warnings.simplefilter(action, category=Warning, lineno=0, append=False)

Inserte una simple entrada en la lista de warnings filter specifications. El significado de los parámetros de la función es el mismo que el de filterwarnings(), pero no se necesitan expresiones regulares ya que el filtro insertado siempre coincide con cualquier mensaje de cualquier módulo siempre que la categoría y el número de línea coincidan.

warnings.resetwarnings()

Reajusta el filtro de advertencias. Esto descarta el efecto de todas las llamadas previas a filterwarnings(), incluyendo la de las opciones de línea de comandos de -W y las llamadas a simplefilter().

Gestores de Contexto disponibles

class warnings.catch_warnings(*, record=False, module=None)

Un gestor de contexto que copia y, al salir, restaura el filtro de advertencias y la función showwarning(). Si el argumento record es False (por defecto) el gestor de contextos retorna None al entrar. Si record es True, se retorna una lista que se va poblando progresivamente de objetos como se ve por una función personalizada de showwarning() (que también suprime la salida a sys.stdout). Cada objeto de la lista tiene atributos con los mismos nombres que los argumentos de showwarning().

El argumento module toma un módulo que será usado en lugar del módulo retornado cuando se importa warnings cuyo filtro será protegido. Este argumento existe principalmente para probar el propio módulo warnings.

Nota

El gestor catch_warnings funciona reemplazando y luego restaurando la función showwarning() del módulo y la lista interna de especificaciones del filtro. Esto significa que el gestor de contexto está modificando el estado global y por lo tanto no es seguro para los hilos.