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

Исходный код: Lib/warnings.py


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

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

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

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

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

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

См.также

logging.captureWarnings() позволяет обрабатывать все предупреждения со стандартной инфраструктурой логирование.

Категории предупреждений

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

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

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

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

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

Изменено в версии 3.7: Ранее DeprecationWarning и FutureWarning различались исходя из того, полностью ли удаляется та или иная функция или меняется ее поведение. Теперь они различаются в зависимости от целевой аудитории и способа обработки с помощью фильтров предупреждений по умолчанию.

Фильтр предупреждений

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

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

  • action является одной из следующих строк:

    Значение Расположение
    "default" распечатать первое появление соответствующих предупреждений для каждого местоположения (модуль + номер строки), где выдается предупреждение
    "error" превратить соответствующие предупреждения в исключения
    "ignore" никогда не печатать соответствующие предупреждения
    "always" всегда печатать соответствующие предупреждения
    "module" распечатать первое появление соответствующих предупреждений для каждого модуля, в котором выдается предупреждение (независимо от номера строки)
    "once" напечатать только первое появление соответствующих предупреждений, независимо от местоположения
  • message - строка, содержащая регулярное выражение, которое должно соответствовать началу предупреждающего сообщения. Выражение компилируется, чтобы всегда учитывать регистр.

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

  • module - это строка, содержащий регулярное выражение, которому должно соответствовать имя модуля. Выражение скомпилировано с учетом регистра.

  • lineno - это целое число, которое должно совпадать с номером строки, в которой появилось предупреждение, или 0, чтобы соответствовать всем номерам строк.

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

Если предупреждение сообщается и не соответствует ни одному зарегистрированному фильтру, применяется действие «по умолчанию» (отсюда и его имя).

Описание фильтров предупреждений

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

Отдельные фильтры предупреждений задаются как последовательность полей, разделенных двоеточиями:

action:message:category:module:line

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

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

default                      # Показать все предупреждения (даже те, которые по умолчанию игнорируются)
ignore                       # Игнорировать все предупреждения
error                        # Преобразовать все предупреждения в ошибки
error::ResourceWarning       # Считать сообщения ResourceWarning ошибками
default::DeprecationWarning  # Показать сообщения DeprecationWarning
ignore,default:::mymodule    # Сообщать только о предупреждениях, вызванных "mymodule"
error:::mymodule[.*]         # Преобразование предупреждений в ошибки в "mymodule"
                             # и любые подпакеты "mymodule"

Фильтр предупреждений по умолчанию

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

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

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

В отладочных компоновках список фильтров предупреждений по умолчанию пуст.

Изменено в версии 3.2: Теперь DeprecationWarning игнорируется по умолчанию в дополнение к PendingDeprecationWarning.

Изменено в версии 3.7: DeprecationWarning снова отображается по умолчанию при запуске непосредственно код в __main__.

Изменено в версии 3.7: BytesWarning больше не отображается в списке фильтров по умолчанию и вместо этого конфигурируется с помощью sys.warnoptions, когда -b указывается дважды.

Переопределение фильтра по умолчанию

Разработчики приложений, написанных в Python, могут хотеть скрыть предупреждения уровня все Python от своих пользователей по умолчанию и только показать их, запуская тесты или в других отношениях рабочий на приложении. Чтобы указать, следует ли отключать предупреждения, в качестве маркера можно sys.warnoptions атрибут используемый интерпретатор для передачи конфигураций в используемые фильтры:

import sys

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

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

import sys

if not sys.warnoptions:
    import os, warnings
    warnings.simplefilter("default") # Изменить фильтр в этом процессе
    os.environ["PYTHONWARNINGS"] = "default" # Также влияют на подпроцессы

Наконец, разработчикам интерактивных оболочек, которые запускают пользовательские код в пространстве имен, отличном от __main__, рекомендуется гарантировать, что DeprecationWarning сообщения становятся видимыми по умолчанию, используя код, подобные следующему (где user_ns - модуль, используемый для выполнения код, введенных в интерактивном режиме):

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

Временное подавление предупреждений

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

import warnings

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

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

В то время как в менеджере контекста все предупреждения будут просто игнорироваться. Это позволяет использовать известные устаревшие код без необходимости просмотра предупреждения, при этом не подавляя предупреждение для других код, которые могут не знать об использовании устаревших код. Примечание: это может быть гарантировано только в однопоточном приложении. Если два или более потоки используют диспетчер catch_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() в модуле также восстанавливается до исходного значение. Примечание: это может быть гарантировано только в однопоточном приложении. Если два или более потоки используют диспетчер catch_warnings контекст одновременно, поведение не определено.

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

Обновление кода для новых версий зависимостей

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

Примечательно, что этот «игнорируемый по умолчанию» список включает в себя DeprecationWarning (для каждого модуля, кроме __main__), что означает, что разработчики должны убедиться, что их код протестированы с обычно игнорируемыми предупреждениями, сделанными видимыми, чтобы получать своевременные уведомления о будущих изменениях API (будь то в стандартной библиотеке или сторонних пакетах).

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

В менее идеальных случаях приложения можно проверить на использование устаревших интерфейсов, передав -Wd в Python интерпретатор (это сокращенно для -W default) или установив PYTHONWARNINGS=default в среде. Это включает обработку по умолчанию для всех предупреждений, включая те, которые игнорируются по умолчанию. Чтобы изменить действие, выполняемое для обнаруженных предупреждений, можно изменить, какой аргумент передается -W (например, -W error). Для получения дополнительной информации о возможных вариантах см. флаг -W.

Доступные функции

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

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

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

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

source, если он поставляется, является уничтоженным объектом, который испускает ResourceWarning.

Изменено в версии 3.6: Добавлен source параметр.

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

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

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

source, если он поставляется, является уничтоженным объектом, который испускает ResourceWarning.

Изменено в версии 3.6: Добавлен параметр source.

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 true, он вставляется в конце. Он проверяет типы аргументов, компилирует message и module регулярные выражения и вставляет их как кортеж в список фильтров предупреждений. Записи, расположенные ближе к передней стороне списка, переопределяют записи, находящиеся позже в списке, если они совпадают с определенным предупреждением. Пропущенные аргументы по умолчанию для значение, который соответствует всему.

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

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

warnings.resetwarnings()

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

Доступные менеджеры контекста

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

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

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

Примечание

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