Предупреждения Python: полное руководство по управлению

Раздел: Основы Python -> Управление предупреждениями

Управление предупреждениями в Python

Модуль warnings предоставляет гибкие инструменты для контроля вывода предупреждений. Это позволяет разработчику решать, какие предупреждения показывать, а какие скрывать, превращать их в исключения или перенаправлять в журнал. Ниже рассмотрены основные подходы с примерами.

Как настроить систему предупреждений для всего приложения?

Наиболее универсальный способ - вызвать warnings.filterwarnings() или warnings.simplefilter() в начале программы. Эти функции задают глобальный фильтр для всех последующих предупреждений.


import warnings

# Игнорировать предупреждения о депрекации
warnings.filterwarnings('ignore', category=DeprecationWarning)

# Пример вызова устаревшей функции
import numpy as np
np.array([1, 2, 3])  # если бы внутри был DeprecationWarning, он бы не отобразился

Warning python (предупреждения в python)

Параметры action могут принимать значения 'ignore', 'error', 'always', 'default', 'module' и 'once'. Категория задаёт тип предупреждения, на который распространяется правило.

Типичные ошибки:
  • Вызов фильтра после того, как предупреждение уже было выдано (фильтр не применяется к уже сгенерированным предупреждениям).
  • Забыть указать категорию - тогда правило применится ко всем предупреждениям, что может скрыть важные сообщения.
  • Фильтры не работают в дочерних потоках, если они не настроены внутри каждого потока отдельно.

Как полностью отключить вывод любых предупреждений?

Используйте warnings.simplefilter('ignore') без указания категории. Это затронет все типы предупреждений.


import warnings
warnings.simplefilter('ignore')

# Теперь даже UserWarning не отобразится
warnings.warn('Тестовое предупреждение')
print('Предупреждение заблокировано')
Предупреждение заблокировано
Проблема: Скрываются все предупреждения, включая потенциально важные (например, о несовместимости версий). Рекомендуется фильтровать по категориям, а не отключать всё подряд.

Как превратить предупреждения в исключения?

Установите action='error' - предупреждение будет выброшено как исключение соответствующего класса (например, DeprecationWarning станет DeprecationWarning исключением).


import warnings
warnings.simplefilter('error', DeprecationWarning)

try:
    # вызов устаревшей функции
    import collections
    collections.Sequence  # в Python 3.9+ DeprecationWarning
except DeprecationWarning as e:
    print(f'Перехвачено как исключение: {e}')
Перехвачено как исключение:  ... (текст предупреждения)
Ошибка: Если код полагается на корректную работу после выдачи предупреждения, превращение его в ошибку может сломать выполнение. Использовать осторожно, обычно для тестирования или строгого контроля версий.

Как игнорировать только определённый тип предупреждений?

Укажите категорию в warnings.filterwarnings. Например, оставить только FutureWarning:


import warnings
warnings.filterwarnings('ignore', category=DeprecationWarning)
warnings.filterwarnings('default', category=FutureWarning)

warnings.warn('Будущее предупреждение', FutureWarning)
warnings.warn('Устаревшее', DeprecationWarning)
.../script.py:5: FutureWarning: Будущее предупреждение
  warnings.warn('Будущее предупреждение', FutureWarning)

Второе предупреждение не выводится, так как оно игнорируется.

Проблема: Правила фильтрации применяются в порядке их добавления. Если одно правило перекрывает другое, может возникнуть путаница. Проверяйте порядок вызовов filterwarnings.

Как временно подавить предупреждения внутри блока кода?

Используйте контекстный менеджер warnings.catch_warnings(). Внутри блока можно задать любые временные фильтры, которые исчезнут после выхода.


import warnings

with warnings.catch_warnings():
    warnings.simplefilter('ignore')
    # Код, в котором не нужны предупреждения
    warnings.warn('Это предупреждение не появится')

# За пределами блока фильтр не действует
warnings.warn('А это появится', UserWarning)
.../script.py:8: UserWarning: А это появится
  warnings.warn('А это появится', UserWarning)
Ошибки:
  • catch_warnings не является потокобезопасным - в многопоточном коде могут возникнуть гонки, если одновременно несколько потоков изменяют фильтры.
  • При входе в контекст сохраняется снимок текущих фильтров, но если внутри блока изменить фильтры из другого потока, они могут испортиться.

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

Интерпретатор Python поддерживает флаги -W. Например, python -W ignore::DeprecationWarning script.py.


# В командной строке (не Python код):
python -W ignore::DeprecationWarning my_script.py

# Или более сложный фильтр:
python -W 'error::UserWarning' my_script.py

Флаг -W можно указывать несколько раз. Он эквивалентен вызову warnings.filterwarnings в начале скрипта.

Проблема: Фильтры из командной строки действуют только для данного запуска. Их сложно применять в средах, где нельзя менять аргументы вызова (например, в IDE или веб-серверах).

Как задать глобальные правила предупреждений без изменения исходного кода?

Используйте переменную окружения PYTHONWARNINGS. Она принимает те же значения, что и -W.


# В bash:
export PYTHONWARNINGS="ignore::DeprecationWarning,error::UserWarning"
python script.py

# Или в Windows (cmd):
set PYTHONWARNINGS=ignore::DeprecationWarning,error::UserWarning
python script.py

После этого все скрипты, запущенные в данном сеансе, будут использовать указанные фильтры.

Ошибка: Если переменная окружения уже задана, но вы хотите её отменить, можно установить пустое значение (PYTHONWARNINGS=). Вложенные или конфликтующие правила могут привести к непредсказуемому поведению.

Как перехватить предупреждение и обработать его в своей логике?

Замените функцию warnings.showwarning на собственную. Это позволит, например, записывать предупреждения в лог-файл.


import warnings
import logging

logging.basicConfig(level=logging.DEBUG)

def my_showwarning(message, category, filename, lineno, file=None, line=None):
    logging.warning(f'{category.__name__}: {message} ({filename}:{lineno})')

warnings.showwarning = my_showwarning

warnings.warn('Это будет в логе', UserWarning)
WARNING:root:UserWarning: Это будет в логе (script.py:10)
Проблема: Изменение showwarning глобально влияет на все модули. Не забудьте восстановить оригинальную функцию после окончания работы, если это необходимо. Для временной замены используйте warnings.catch_warnings(record=True) (см. раздел примеров).

Расширенные примеры работы с предупреждениями

Создание пользовательской категории предупреждений

Можно определить свой класс, унаследованный от Warning, и использовать его для специфических предупреждений.

Пример

import warnings

class MySpecialWarning(Warning):
    pass

def risky_function():
    warnings.warn('Это пользовательское предупреждение', MySpecialWarning)

# Фильтруем только наше предупреждение
warnings.filterwarnings('error', category=MySpecialWarning)

try:
    risky_function()
except MySpecialWarning as e:
    print(f'Поймано пользовательское предупреждение: {e}')
Поймано пользовательское предупреждение: Это пользовательское предупреждение

Использование контекстного менеджера для записи предупреждений

warnings.catch_warnings(record=True) перехватывает все предупреждения внутри блока и сохраняет их в список.

Пример

import warnings

with warnings.catch_warnings(record=True) as w:
    warnings.simplefilter('always')  # ловить все
    warnings.warn('Первое', UserWarning)
    warnings.warn('Второе', DeprecationWarning)
    print(f'Перехвачено {len(w)} предупреждений')
    for warning in w:
        print(f'Категория: {warning.category.__name__}, сообщение: {warning.message}')
Перехвачено 2 предупреждений
Категория: UserWarning, сообщение: Первое
Категория: DeprecationWarning, сообщение: Второе

Фильтрация по имени модуля

Можно настроить игнорирование предупреждений только из определённого модуля, используя параметр module.

Пример

import warnings

# Игнорировать DeprecationWarning только из модуля 'numpy'
warnings.filterwarnings('ignore', category=DeprecationWarning, module='numpy')

import numpy as np
# Если numpy когда-нибудь выдаст DeprecationWarning, он будет скрыт

Параметр module принимает регулярное выражение (через re). Например, 'numpy\.*'.

Сброс всех фильтров к состоянию по умолчанию

warnings.resetwarnings() удаляет все заданные программой фильтры и возвращает стандартное поведение.

Пример

import warnings

warnings.simplefilter('ignore')
print('Фильтры изменены')
warnings.resetwarnings()
print('Фильтры сброшены')
warnings.warn('Это снова видно')
Фильтры изменены
Фильтры сброшены
script.py:7: UserWarning: Это снова видно

Предупреждения в многопоточном коде

Каждый поток имеет собственный стек фильтров, но начальные настройки копируются из главного потока. Изменения в одном потоке не влияют на другие, если не использовать специальные механизмы синхронизации.

Пример

import warnings
import threading

def worker():
    # В дочернем потоке другой контекст
    with warnings.catch_warnings():
        warnings.simplefilter('ignore')
        warnings.warn('Из потока не видно')
    # Здесь предупреждение не подавлено, если не настроено глобально
    warnings.warn('Из потока видно')

thread = threading.Thread(target=worker)
thread.start()
thread.join()
# В главном потоке свой контекст
warnings.warn('Главный поток')

Результат зависит от того, какие фильтры установлены в главном потоке. В примере выше в главном потоке никакие фильтры не менялись, поэтому все три предупреждения (кроме первого, которое подавлено внутри catch_warnings) будут выведены.

Логирование предупреждений через модуль logging

Можно перенаправить все предупреждения в систему логирования, используя кастомный обработчик.

Пример

import warnings
import logging

logging.basicConfig(level=logging.WARNING, format='%(levelname)s - %(message)s')

def log_warning(message, category, filename, lineno, file=None, line=None):
    logger = logging.getLogger(category.__name__)
    logger.warning(f'{filename}:{lineno}: {message}')

warnings.showwarning = log_warning

warnings.warn('Тест лога')
WARNING - UserWarning: script.py:10: Тест лога

Тестирование кода с предупреждениями (pytest)

Библиотека pytest предоставляет встроенный контекст pytest.warns для проверки, что код выдает определённое предупреждение.

Пример

# content of test_warnings.py
import warnings
import pytest

def deprecated_function():
    warnings.warn('use new version', DeprecationWarning)

def test_deprecation():
    with pytest.warns(DeprecationWarning):
        deprecated_function()
$ pytest test_warnings.py -v
...
test_warnings.py::test_deprecation PASSED

Если предупреждение не будет выдано, тест упадет.

Предупреждения в Python - comments

En
Warning python (python)