Документация Python неофициальный перевод
Содержание страницы

27.4. warnings – Управление предупреждениямиwarnings – Warning control

Предупреждения обычно выводятся в ситуациях, когда полезно предупредить пользователя о некотором условии в программе, когда это условие (обычно) не требует возбуждения исключения и завершения программы. Например, может потребоваться выдать предупреждение, если программа использует устаревший модуль.

Программисты на Python вызывают предупреждения с помощью функции warn(), определённой в этом модуле. (Программисты на C используют PyErr_WarnEx(); подробнее см. Обработка исключений).

Предупреждения обычно выводятся в sys.stderr, но их обработку можно гибко настраивать – от полного игнорирования до превращения в исключения. Обработка предупреждений может различаться в зависимости от категории предупреждения (см. ниже), текста сообщения и места в исходном коде, где оно было вызвано. Повторные предупреждения из одного и того же места обычно подавляются.

Управление предупреждениями состоит из двух этапов: сначала каждый раз при выдаче предупреждения определяется, должно ли сообщение быть выдано; затем, если сообщение должно быть выдано, оно форматируется и выводится с помощью настраиваемого пользователем перехватчика.

Решение о том, нужно ли выдавать предупреждение, определяется фильтром предупреждений – последовательностью правил сопоставления и действий. Правила можно добавлять в фильтр, вызывая filterwarnings(), и сбрасывать в исходное состояние вызовом resetwarnings().

Вывод предупреждений на экран осуществляется вызовом showwarning(), который можно переопределить; стандартная реализация этой функции форматирует сообщение, вызывая formatwarning(), которая также доступна для использования в пользовательских реализациях.

27.4.1. Категории предупрежденийWarning Categories

Существует ряд встроенных исключений, представляющих категории предупреждений. Такая категоризация полезна для фильтрации групп предупреждений. В настоящее время определены следующие классы категорий предупреждений:

Класс Описание
Warning Это базовый класс всех категорий предупреждений. Он наследуется от исключения.
UserWarning Категория по умолчанию для warn().
DeprecationWarning Базовая категория для предупреждений об устаревших функциях.
SyntaxWarning Базовый тип для предупреждений о сомнительных синтаксических особенностях.
RuntimeWarning Базовая категория для предупреждений о сомнительных возможностях времени выполнения.
FutureWarning Базовая категория для предупреждений о конструкциях, которые в будущем изменят свою семантику.
PendingDeprecationWarning Базовая категория для предупреждений о возможностях, которые будут объявлены устаревшими в будущем (игнорируется по умолчанию).
ImportWarning Базовая категория для предупреждений, возникающих в процессе импорта модуля (игнорируется по умолчанию).
UnicodeWarning Базовая категория для предупреждений, связанных с Юникодом.
BytesWarning Базовая категория для предупреждений, связанных с bytes и buffer.

Хотя технически это встроенные исключения, они документируются здесь, поскольку концептуально относятся к механизму предупреждений.

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

27.4.2. Фильтр предупрежденийThe Warnings Filter

Фильтр предупреждений управляет тем, игнорируются ли предупреждения, выводятся на экран или превращаются в ошибки (вызывают исключение).

Концептуально фильтр предупреждений поддерживает упорядоченный список спецификаций фильтра; каждое конкретное предупреждение последовательно сопоставляется с каждой спецификацией фильтра в списке, пока не будет найдено совпадение; совпадение определяет дальнейшие действия. Каждая запись представляет собой кортеж вида (action, message, category, module, lineno), где:

  • Действие – одна из следующих строк:

    Значение

    Действие

    "error"

    превращать подходящие предупреждения в исключения

    "ignore"

    никогда не выводить подходящие предупреждения

    "always"

    всегда выводить подходящие предупреждения

    "default"

    выводит первое появление соответствующих предупреждений для каждого места, где было выдано предупреждение

    "module"

    выводит первое появление соответствующих предупреждений для каждого модуля, где было выдано предупреждение

    "once"

    выводить только первое вхождение подходящих предупреждений, независимо от местоположения

  • message – строка, содержащая регулярное выражение, которому должно соответствовать сообщение предупреждения (сопоставление компилируется так, чтобы игнорировать регистр).

  • category – класс (подкласс Warning), подклассом которого должна быть категория предупреждения для совпадения.

  • module – строка, содержащая регулярное выражение, которому должно соответствовать имя модуля (сопоставление компилируется с учётом регистра).

  • lineno – это целое число, которому должен соответствовать номер строки, где произошло предупреждение, или 0 для соответствия всем номерам строк.

Поскольку класс Warning наследуется от встроенного класса исключения, чтобы превратить предупреждение в ошибку, достаточно возбудить category(message).

Фильтр предупреждений инициализируется параметрами -W, переданными в командную строку интерпретатора Python. Интерпретатор сохраняет аргументы всех параметров -W без интерпретации в sys.warnoptions; модуль warnings разбирает их при первом импорте (недопустимые параметры игнорируются после вывода сообщения в sys.stderr).

Предупреждения, которые по умолчанию игнорируются, можно включить, передав -Wd интерпретатору. Это включает обработку по умолчанию для всех предупреждений, включая те, которые обычно игнорируются. Это особенно полезно для включения ImportWarning при отладке проблем с импортом разрабатываемого пакета. ImportWarning также можно явно включить в коде Python с помощью:

warnings.simplefilter('default', ImportWarning)

27.4.2.1. Фильтры предупреждений по умолчаниюDefault Warning Filters

По умолчанию Python устанавливает несколько фильтров предупреждений, которые можно переопределить с помощью опций командной строки, переданных в -W, и вызовов filterwarnings().

  • PendingDeprecationWarning и ImportWarning игнорируются.
  • BytesWarning игнорируется, если опция -b не указана один или два раза; в этом случае предупреждение либо выводится (-b), либо превращается в исключение (-bb).

27.4.3. Временное подавление предупрежденийTemporarily Suppressing Warnings

Если используется код, который, как известно, вызовет предупреждение (например, устаревшая функция), но выводить предупреждение не требуется, то его можно подавить с помощью менеджера контекста catch_warnings:

import warnings

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

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

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

27.4.4. Тестирование предупрежденийTesting Warnings

Для тестирования предупреждений, вызываемых кодом, используйте менеджер контекста catch_warnings. С его помощью можно временно изменить фильтр предупреждений для упрощения тестирования. Например, сделайте следующее, чтобы перехватить все возникшие предупреждения и проверить их:

import warnings

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

with warnings.catch_warnings(record=True) as w:
    # Привести к тому, что все предупреждения всегда будут срабатывать.
    warnings.simplefilter("always")
    # Вызвать предупреждение.
    fxn()
    # Проверить некоторые вещи
    assert len(w) == 1
    assert issubclass(w[-1].category, DeprecationWarning)
    assert "deprecated" in str(w[-1].message)

Можно также сделать все предупреждения исключениями, используя error вместо always. Следует учитывать, что если предупреждение уже было вызвано из-за правила once/default, то, независимо от установленных фильтров, предупреждение не будет показано снова, пока не будет очищен реестр предупреждений, связанный с этим предупреждением.

Когда менеджер контекста завершает работу, фильтр предупреждений восстанавливается в состояние, которое было при входе в контекст. Это предотвращает изменение фильтра предупреждений тестами неожиданным образом между тестами и, как следствие, получение неопределённых результатов. Функция showwarning() в модуле также восстанавливается до своего исходного значения.

При тестировании нескольких операций, вызывающих один и тот же тип предупреждения, важно тестировать их так, чтобы подтвердить, что каждая операция вызывает новое предупреждение (например, настроить предупреждения как исключения и проверить, что операции вызывают исключения, проверить, что длина списка предупреждений увеличивается после каждой операции, или удалять предыдущие записи из списка предупреждений перед каждой новой операцией).

27.4.5. Доступные функцииAvailable Functions

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

Выдаёт предупреждение, либо игнорирует его, либо возбуждает исключение. Аргумент category, если задан, должен быть классом категории предупреждения (см. выше); по умолчанию он равен UserWarning. Альтернативно, message может быть экземпляром Warning, и в этом случае category будет проигнорирован, а будет использовано message.__class__. В этом случае текст сообщения будет равен str(message). Эта функция возбуждает исключение, если конкретное выданное предупреждение превращается в ошибку фильтром предупреждений (см. выше). Аргумент stacklevel может использоваться функциями-обёртками, написанными на Python, как показано ниже:

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

Это заставляет предупреждение ссылаться на вызывающий код функции deprecation(), а не на источник самой deprecation() (поскольку последнее сделало бы предупреждение бессмысленным).

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

Это низкоуровневый интерфейс к функциональности warn(), в который явно передаются сообщение, категория, имя файла и номер строки, а также опционально имя модуля и реестр (который должен быть словарём __warningregistry__ модуля). Имя модуля по умолчанию равно имени файла без расширения .py; если реестр не передан, предупреждение никогда не подавляется. message должна быть строкой, а category – подклассом Warning, либо message может быть экземпляром Warning, и в этом случае category будет проигнорирован.

module_globals, если указан, должно быть глобальным пространством имён, используемым кодом, для которого выдаётся предупреждение. (Этот аргумент используется для поддержки отображения исходного кода для модулей, найденных в zip-файлах или других источниках импорта, не основанных на файловой системе).

warnings.showwarning(message, category, filename, lineno, file=None, line=None)
Записывает предупреждение в файл. Реализация по умолчанию вызывает formatwarning(message, category, filename, lineno, line) и записывает полученную строку в file, который по умолчанию равен sys.stderr. Можно заменить эту функцию альтернативной реализацией, присвоив новое значение warnings.showwarning. line – строка исходного кода, которая будет включена в сообщение предупреждения; если line не указан, showwarning() попытается прочитать строку, указанную в filename и lineno.
warnings.formatwarning(message, category, filename, lineno, line=None)
Форматирует предупреждение стандартным способом. Возвращает строку, которая может содержать встроенные символы новой строки и заканчивается символом новой строки. line – это строка исходного кода, которая будет включена в сообщение предупреждения; если line не указана, formatwarning() попытается прочитать строку, указанную в filename и lineno.
warnings.filterwarnings(action, message='', category=Warning, module='', lineno=0, append=False)
Вставляет запись в список спецификаций фильтра предупреждений. По умолчанию запись вставляется в начало; если append равен истине, она вставляется в конец. Эта функция проверяет типы аргументов, компилирует регулярные выражения message и module и вставляет их в виде кортежа в список фильтров предупреждений. Записи, находящиеся ближе к началу списка, имеют приоритет над записями, находящимися ближе к концу, если обе соответствуют конкретному предупреждению. Опущенные аргументы по умолчанию принимают значение, которое соответствует всему.
warnings.simplefilter(action, category=Warning, lineno=0, append=False)
Вставляет простую запись в список спецификаций фильтра предупреждений. Значение параметров функции такое же, как для filterwarnings(), но регулярные выражения не требуются, поскольку вставляемый фильтр всегда соответствует любому сообщению в любом модуле, если совпадают категория и номер строки.
warnings.resetwarnings()
Сбрасывает фильтр предупреждений. Отменяет действие всех предыдущих вызовов filterwarnings(), включая опции командной строки -W и вызовы simplefilter().

27.4.6. Доступные контекстные менеджерыAvailable Context Managers

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

Менеджер контекста, который копирует и при выходе восстанавливает фильтр предупреждений и функцию showwarning(). Если аргумент record равен False (по умолчанию), менеджер контекста возвращает None при входе. Если record равен True, возвращается список, который постепенно заполняется объектами, наблюдаемыми пользовательской функцией showwarning() (которая также подавляет вывод в sys.stdout). Каждый объект в списке имеет атрибуты с теми же именами, что и аргументы showwarning().

Аргумент module принимает модуль, который будет использоваться вместо модуля, возвращаемого при импорте warnings, чей фильтр будет защищён. Этот аргумент существует в основном для тестирования самого модуля warnings.