En Python, comment intercepter les avertissements comme s'il s'agissait d'exceptions?

103

Une bibliothèque tierce (écrite en C) que j'utilise dans mon code python émet des avertissements. Je veux pouvoir utiliser la try exceptsyntaxe pour gérer correctement ces avertissements. Y a-t-il un moyen de faire cela?

Boris Gorelik
la source
2
Ces avertissements ne sont-ils que des messages texte écrits par stderr?
Fenikso
1
Fenikso: Je ne sais pas avec certitude, cela semble être de vrais avertissements
Boris Gorelik
1
Comment reconnaissez-vous le «vrai avertissement»? Je pensais qu'en C, vous obtenez un véritable avertissement lors de la compilation.
Fenikso
warnings.filterwarningsfait exactement ce que vous voulez, je ne comprends pas quel est votre problème?
Rosh Oxymoron
4
@Fenikso, @Rosh Oxymoron vous aviez raison. Mon erreur. warnings.filterwarnigns('error')Fait le travail. Je ne trouve pas la réponse originale qui proposait cette solution
Boris Gorelik

Réponses:

51

Pour citer le manuel python ( 27.6.4. Testing Warnings ):

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)
Pouvoirs de Bobby
la source
6
Voici une réponse, qui vous indique comment utiliser la try exceptsyntaxe.
Unapiedra
Cela a l'avantage, par rapport à la réponse de niekas, que si vous fnxretournez quelque chose, vous gardez ce résultat (et pouvez toujours gérer l'avertissement).
Pietro Battiston
Cela ne répond pas à la question du PO, qui portait sur la gestion des wanrings, pas sur les tester. Cependant, la réponse de niekas ci-dessous montre comment gérer les avertissements.
Biggsy
Notez simplement que la fonction ci-dessus ne fonctionnera pas si votre fonction ne renvoie qu'un avertissement par intermittence, car dans le cas où fxn()cela ne renvoie pas d'avertissement, il y waura une liste vide. Si west une liste vide ( par exemple []), en cours d' exécution alors le code vous donnera l'erreur suivante: IndexError: list index out of range. Si vous cherchez simplement à formater ou vérifier les propriétés des avertissements capturés, il est préférable d'utiliser une boucle for:for x in w: print(f'{x.category.__name__}: {str(x.message)}')
Steven M. Mortimer le
130

Pour gérer les avertissements comme des erreurs, utilisez simplement ceci:

import warnings
warnings.filterwarnings("error")

Après cela, vous serez en mesure de détecter les avertissements comme les erreurs, par exemple cela fonctionnera:

try:
    some_heavy_calculations()
except RuntimeWarning:
    import ipdb; ipdb.set_trace()

PS a ajouté cette réponse car la meilleure réponse dans les commentaires contient une faute d'orthographe: filterwarnignsau lieu de filterwarnings.

niekas
la source
8
Et si vous voulez juste voir une trace de pile, les deux premières lignes sont tout ce dont vous avez besoin.
z0r
5
C'est parfait. Je voulais juste que mon script arrête l'exécution dès que l'avertissement a été émis, afin que je puisse imprimer les informations de débogage pertinentes et résoudre le problème.
Praveen
1
Vous n'avez pas besoin de l' filterwarningsappel pour attraper Warnings, du moins en python 3. cela fonctionne.
naught101
1
La réponse acceptée ne répond pas à la question du PO. Cette réponse le fait. C'est la réponse que je recherchais lorsque ma recherche a trouvé cette question.
Biggsy
15

Voici une variante qui explique plus clairement comment utiliser uniquement vos avertissements personnalisés.

import warnings
with warnings.catch_warnings(record=True) as w:
    # Cause all warnings to always be triggered.
    warnings.simplefilter("always")

    # Call some code that triggers a custom warning.
    functionThatRaisesWarning()

    # ignore any non-custom warnings that may be in the list
    w = filter(lambda i: issubclass(i.category, UserWarning), w)

    if len(w):
        # do something with the first warning
        email_admins(w[0].message)
mcqwerty
la source
4

Dans certains cas, vous devez utiliser des ctypes pour transformer les avertissements en erreurs. Par exemple:

str(b'test')  # no error
import warnings
warnings.simplefilter('error', BytesWarning)
str(b'test')  # still no error
import ctypes
ctypes.c_int.in_dll(ctypes.pythonapi, 'Py_BytesWarningFlag').value = 2
str(b'test')  # this raises an error
Collin Anderson
la source
Cette réponse est constructive simplement pour montrer comment faire des erreurs uniquement dans certains types d'avertissement. Pour presque tous les grands projets logiciels, si vous le faites, warnings.simplefilter('error')vous n'obtiendrez pas la trace de l'avertissement que vous avez vu dans les journaux, mais à la place, vous obtiendrez des traces d'avertissements précédemment filtrés. L'utilisation simplefilterest également le moyen le plus rapide d'arriver à votre réponse si vous avez une invocation CLI.
AlanSE