Как в Python отловить предупреждения, как если бы они были исключениями?


105

Сторонняя библиотека (написанная на C), которую я использую в своем коде на Python, выдает предупреждения. Я хочу иметь возможность использовать try exceptсинтаксис для правильной обработки этих предупреждений. Есть ли способ сделать это?


2
Это предупреждение - это просто текстовые сообщения, написанные на stderr?
Fenikso

1
Фениксо: Точно не знаю, похоже, настоящее предупреждение
Борис Горелик,

1
Как распознать «настоящее предупреждение»? Я думал, что в C вы получаете настоящее предупреждение во время компиляции.
Fenikso

warnings.filterwarningsделает именно то, что вы хотите, я не понимаю, в чем ваша проблема?
Рош Оксюморон

4
@ Фениксо, @ Рош Оксюморон, ты был прав. Моя ошибка. warnings.filterwarnigns('error')делает свою работу. Я не могу найти оригинальный ответ, предлагающий это решение
Борис Горелик

Ответы:


52

Цитата из справочника Python ( 27.6.4. Предупреждения при тестировании ):

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)

6
Вот ответ, в котором рассказывается, как использовать try exceptсинтаксис.
Unapiedra

Это имеет преимущество перед ответом niekas в том, что если fnxчто-то возвращает, вы сохраняете этот результат (и по-прежнему можете управлять предупреждением).
Пьетро Баттистон,

Это не отвечает на вопрос OP, который касался обработки запросов, а не их тестирования. Однако ответ niekas ниже показывает, как обрабатывать предупреждения.
Biggsy

Просто обратите внимание, что указанная выше функция не будет работать, если ваша функция только периодически возвращает предупреждение, потому что в случае, fxn()когда предупреждение не возвращается, тогда wбудет пустой список. Если wэто пустой список (т.е. []), а затем запустить код даст вам следующее сообщение об ошибке: IndexError: list index out of range. Если вы просто хотите отформатировать или проверить свойства зафиксированных предупреждений, то лучше использовать цикл for:for x in w: print(f'{x.category.__name__}: {str(x.message)}')
Стивен М. Мортимер,

132

Чтобы обрабатывать предупреждения как ошибки, просто используйте это:

import warnings
warnings.filterwarnings("error")

После этого вы сможете получать предупреждения так же, как и ошибки, например, это будет работать:

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

PS Добавил этот ответ, потому что лучший ответ в комментариях содержит орфографическую ошибку: filterwarnignsвместо filterwarnings.


8
И если вы просто хотите увидеть трассировку стека, вам достаточно первых двух строк.
z0r

5
Это потрясающе. Я просто хотел, чтобы мой сценарий останавливал выполнение, как только было выдано предупреждение, чтобы я мог распечатать соответствующую отладочную информацию и исправить проблему.
Praveen

1
Вам не нужен filterwarningsвызов, чтобы поймать Warnings, по крайней мере, в python 3. он просто работает.
naught101 03

1
Принятый ответ не отвечает на вопрос ОП. Этот ответ имеет значение. Это ответ, который я искал, когда мой поиск нашел этот вопрос.
Biggsy

16

Вот вариант, который поясняет, как работать только с вашими настраиваемыми предупреждениями.

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)

16

Если вы просто хотите, чтобы сценарий завершался ошибкой при предупреждении, вы можете вызвать его pythonс -Wаргументом :

python -W error foobar.py

4

В некоторых случаях вам нужно использовать ctypes, чтобы превратить предупреждения в ошибки. Например:

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

Этот ответ конструктивен просто для того, чтобы показать, как делать ошибки только при определенных типах предупреждений. Практически для любого крупного программного проекта, если вы это сделаете, warnings.simplefilter('error')вы не получите обратную трассировку для предупреждения, которое вы видели в журналах, а вместо этого получите обратную трассировку из ранее отфильтрованных предупреждений. Использование simplefilterтакже является самым быстрым способом получить ответ, если у вас есть вызов CLI.
AlanSE
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.