29.5. warnings – Управление предупреждениями
Исходный код: Lib/warnings.py
Предупреждения обычно выводятся в ситуациях, когда полезно предупредить пользователя о некотором условии в программе, которое (как правило) не требует возбуждения исключения и завершения программы. Например, можно выдать предупреждение, когда программа использует устаревший модуль.
Программисты на Python выводят предупреждения, вызывая функцию warn(), определённую в этом модуле. (Программисты на C используют PyErr_WarnEx(); подробнее см. Обработка исключений).
Предупреждения обычно записываются в sys.stderr, но их обработку можно гибко изменять: от игнорирования всех предупреждений до превращения их в исключения. Обработка предупреждений может зависеть от категории предупреждения (см. ниже), текста предупреждающего сообщения и исходного расположения, где оно выдаётся. Повторения одного и того же предупреждения для одного и того же исходного расположения обычно подавляются.
В управлении предупреждениями есть два этапа: сначала каждый раз, когда выдается предупреждение, принимается решение, должно ли сообщение быть выдано; затем, если сообщение должно быть выдано, оно форматируется и выводится с помощью задаваемого пользователем перехватчика.
Решение о выдаче предупреждающего сообщения управляется фильтром предупреждений, который представляет собой последовательность правил сопоставления и действий. Правила можно добавить в фильтр вызовом filterwarnings() и сбросить в состояние по умолчанию вызовом resetwarnings().
Вывод предупреждений выполняется вызовом showwarning(), который может быть переопределён; реализация по умолчанию этой функции форматирует сообщение, вызывая formatwarning(), который также доступен для использования пользовательскими реализациями.
Смотрите также
logging.captureWarnings() позволяет обрабатывать все предупреждения с помощью стандартной инфраструктуры логирования.
29.5.1. Категории предупреждений
Существует ряд встроенных исключений, представляющих категории предупреждений. Такая категоризация полезна для фильтрации групп предупреждений. Ниже перечислены классы категорий предупреждений, определённые в настоящее время:
Класс |
Описание |
|---|---|
Это базовый класс всех классов категорий предупреждений. Он является подклассом |
|
Категория по умолчанию для |
|
Базовый класс для предупреждений об устаревших функциях (игнорируется по умолчанию). |
|
Базовый класс для предупреждений о сомнительных синтаксических особенностях. |
|
Базовый класс для предупреждений о сомнительных особенностях выполнения. |
|
Базовый класс для предупреждений о конструкциях, которые изменят свою семантику в будущем. |
|
Базовый класс для предупреждений о возможностях, которые будут объявлены устаревшими в будущем (по умолчанию игнорируются). |
|
Базовый класс для предупреждений, возникающих в процессе импорта модуля (по умолчанию игнорируются). |
|
Базовый класс для предупреждений, связанных с Unicode. |
|
Базовый класс для предупреждений, связанных с
|
|
Базовый класс для предупреждений, связанных с использованием ресурсов. |
Хотя технически это встроенные исключения, они документированы здесь, поскольку концептуально относятся к механизму предупреждений.
Пользовательский код может определять дополнительные категории предупреждений, создавая подклассы одной из стандартных категорий предупреждений. Категория предупреждений всегда должна быть подклассом класса Warning.
29.5.2. Фильтр предупреждений
Фильтр предупреждений определяет, будут ли предупреждения игнорироваться, отображаться или превращаться в ошибки (вызывать исключение).
Концептуально фильтр предупреждений поддерживает упорядоченный список спецификаций фильтра; каждое конкретное предупреждение поочерёдно сопоставляется с каждой спецификацией фильтра в списке, пока не будет найдено совпадение; совпадение определяет действие для предупреждения. Каждая запись представляет собой кортеж вида (action, message, category, module, lineno), где:
action – одна из следующих строк:
Значение
Действие
"error"превращать соответствующие предупреждения в исключения
"ignore"никогда не выводить соответствующие предупреждения
"always"всегда выводить соответствующие предупреждения
"default"выводит первое совпадение предупреждений для каждой позиции, где выдаётся предупреждение
"module"выводит первое совпадение предупреждений для каждого модуля, где выдаётся предупреждение
"once"выводить только первое вхождение соответствующих предупреждений, независимо от местоположения
message – это строка, содержащая регулярное выражение, которому должно соответствовать начало предупреждающего сообщения. Выражение компилируется так, чтобы всегда не учитывать регистр.
category – класс (подкласс
Warning), подклассом которого должна быть категория предупреждения для совпадения.module – это строка, содержащая регулярное выражение, которому должно соответствовать имя модуля. Выражение компилируется с учетом регистра.
lineno – целое число, которому должен соответствовать номер строки, где возникло предупреждение, или
0для соответствия всем номерам строк.
Поскольку класс Warning является подклассом встроенного класса Exception, чтобы превратить предупреждение в ошибку, мы просто возбуждаем category(message).
Фильтр предупреждений инициализируется параметрами -W, переданными в командную строку интерпретатора Python. Интерпретатор сохраняет аргументы для всех параметров -W без интерпретации в sys.warnoptions; модуль warnings разбирает их при первом импорте (недопустимые параметры игнорируются после вывода сообщения в sys.stderr).
29.5.2.1. Фильтры предупреждений по умолчанию
По умолчанию Python устанавливает несколько фильтров предупреждений, которые можно переопределить с помощью параметров командной строки, переданных в -W, и вызовов filterwarnings().
DeprecationWarning,PendingDeprecationWarningиImportWarningигнорируются.BytesWarningигнорируется, если параметр-bне указан один или два раза; в этом случае предупреждение либо выводится (-b), либо превращается в исключение (-bb).ResourceWarningигнорируется, если Python не был собран в режиме отладки.
Изменено в версии 3.2: DeprecationWarning теперь игнорируется по умолчанию в дополнение к PendingDeprecationWarning.
29.5.3. Временное подавление предупреждений
Если вы используете код, который, как вы знаете, вызовет предупреждение (например, устаревшую функцию), но не хотите видеть это предупреждение, то можно подавить его с помощью менеджера контекста catch_warnings:
import warnings
def fxn():
warnings.warn("deprecated", DeprecationWarning)
with warnings.catch_warnings():
warnings.simplefilter("ignore")
fxn()
Пока находитесь внутри менеджера контекста, все предупреждения просто игнорируются. Это позволяет использовать заведомо устаревший код, не видя предупреждения, при этом не подавляя предупреждения для другого кода, который может не знать об использовании устаревшего кода. Примечание: это гарантируется только в однопоточном приложении. Если два или более потока одновременно используют менеджер контекста catch_warnings, поведение не определено.
29.5.4. Тестирование предупреждений
Для тестирования предупреждений, вызываемых кодом, используйте контекстный менеджер 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, поведение не определено.
При тестировании нескольких операций, вызывающих один и тот же тип предупреждения, важно проверять их таким образом, чтобы убедиться, что каждая операция вызывает новое предупреждение (например, настроить возбуждение исключений для предупреждений и проверять, что операции вызывают исключения, проверять, что длина списка предупреждений продолжает увеличиваться после каждой операции, или удалять предыдущие записи из списка предупреждений перед каждой новой операцией).
29.5.5. Обновление кода для новых версий Python
Предупреждения, представляющие интерес только для разработчика, по умолчанию игнорируются. Поэтому следует убедиться, что ваш код тестируется с отображением обычно игнорируемых предупреждений. Это можно сделать из командной строки, передав интерпретатору -Wd (сокращённая форма для -W default). Это включает стандартную обработку всех предупреждений, включая те, которые игнорируются по умолчанию. Чтобы изменить действие для встреченных предупреждений, достаточно изменить аргумент, передаваемый в -W, например -W error. Подробнее о возможностях см. флаг -W.
Чтобы сделать то же самое программно, как -Wd, используйте:
warnings.simplefilter('default')
Обязательно выполняйте этот код как можно раньше. Это предотвращает неожиданное влияние регистрации уже возникших предупреждений на обработку будущих.
Игнорирование некоторых предупреждений по умолчанию предназначено для того, чтобы пользователь не видел предупреждения, представляющие интерес только для разработчика. Поскольку вы не обязательно контролируете, какой интерпретатор использует пользователь для запуска своего кода, между вашими циклами выпуска может выйти новая версия Python. Новый выпуск интерпретатора может вызвать новые предупреждения в вашем коде, которых не было в более старом интерпретаторе, например DeprecationWarning для используемого вами модуля. В то время как вы как разработчик хотите получать уведомления о том, что ваш код использует устаревший модуль, для пользователя эта информация является по сути шумом и не приносит ему пользы.
Модуль unittest также был обновлён для использования фильтра 'default' во время выполнения тестов.
29.5.6. Доступные функции
-
warnings.warn(message, category=None, stacklevel=1, source=None)¶ Выдаёт предупреждение, либо игнорирует его, либо возбуждает исключение. Аргумент category, если задан, должен быть классом категории предупреждения (см. выше); по умолчанию он равен
UserWarning. В качестве альтернативы message может быть экземпляромWarning, и в этом случае category будет проигнорирован, а будет использованоmessage.__class__. В этом случае текст сообщения будетstr(message). Эта функция возбуждает исключение, если выданное предупреждение превращается в ошибку фильтром предупреждений (см. выше). Аргумент stacklevel может использоваться функциями-обёртками, написанными на Python, например:pythondef 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, если задан, должен быть глобальным пространством имен, используемым кодом, для которого выдается предупреждение. (Этот аргумент используется для поддержки отображения исходного кода модулей, найденных в zip-файлах или других не файловых источниках импорта).
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().
29.5.7. Доступные менеджеры контекста
-
class
warnings.catch_warnings(*, record=False, module=None)¶ Менеджер контекста, который копирует и при выходе восстанавливает фильтр предупреждений и функцию
showwarning(). Если аргумент record равенFalse(по умолчанию), менеджер контекста возвращаетNoneпри входе. Если record равенTrue, возвращается список, который постепенно заполняется объектами, которые видит пользовательская функцияshowwarning()(которая также подавляет вывод вsys.stdout). Каждый объект в списке имеет атрибуты с теми же именами, что и аргументыshowwarning().Аргумент module принимает модуль, который будет использоваться вместо модуля, возвращаемого при импорте
warnings, чей фильтр будет защищён. Этот аргумент существует в основном для тестирования самого модуляwarnings.Примечание
Менеджер
catch_warningsработает путём замены, а затем восстановления функцииshowwarning()модуля и внутреннего списка спецификаций фильтров. Это означает, что менеджер контекста изменяет глобальное состояние и поэтому не является потокобезопасным.