Содержание страницы
29.5. warnings – Warning control¶
Исходный код: Lib/warnings.py
Предупреждения обычно выводятся в ситуациях, когда полезно предупредить пользователя о некотором условии в программе, когда это условие (обычно) не требует возбуждения исключения и завершения программы. Например, может потребоваться выдать предупреждение, если программа использует устаревший модуль.
Программисты на Python вызывают предупреждения с помощью функции warn(), определённой в этом модуле. (Программисты на C используют PyErr_WarnEx(); подробнее см. Обработка исключений).
Предупреждения обычно выводятся в sys.stderr, но их обработку можно гибко настраивать – от полного игнорирования до превращения в исключения. Обработка предупреждений может различаться в зависимости от категории предупреждения (см. ниже), текста сообщения и места в исходном коде, где оно было вызвано. Повторные предупреждения из одного и того же места обычно подавляются.
Управление предупреждениями состоит из двух этапов: сначала каждый раз при выдаче предупреждения определяется, должно ли сообщение быть выдано; затем, если сообщение должно быть выдано, оно форматируется и выводится с помощью настраиваемого пользователем перехватчика.
Решение о том, нужно ли выдавать предупреждение, определяется фильтром предупреждений – последовательностью правил сопоставления и действий. Правила можно добавлять в фильтр, вызывая filterwarnings(), и сбрасывать в исходное состояние вызовом resetwarnings().
Вывод предупреждений на экран осуществляется вызовом showwarning(), который можно переопределить; стандартная реализация этой функции форматирует сообщение, вызывая formatwarning(), которая также доступна для использования в пользовательских реализациях.
См. также
logging.captureWarnings() позволяет обрабатывать все предупреждения с помощью стандартной инфраструктуры логирования.
29.5.1. Категории предупреждений¶Warning Categories
Существует ряд встроенных исключений, представляющих категории предупреждений. Такая категоризация полезна для фильтрации групп предупреждений. В настоящее время определены следующие классы категорий предупреждений:
| Класс | Описание |
|---|---|
| Warning | Это базовый класс всех категорий предупреждений. Он наследуется от исключения. |
| UserWarning | Категория по умолчанию для warn(). |
| DeprecationWarning | Базовая категория для предупреждений об устаревших возможностях (по умолчанию игнорируется). |
| SyntaxWarning | Базовый тип для предупреждений о сомнительных синтаксических особенностях. |
| RuntimeWarning | Базовая категория для предупреждений о сомнительных возможностях времени выполнения. |
| FutureWarning | Базовая категория для предупреждений о конструкциях, которые в будущем изменят свою семантику. |
| PendingDeprecationWarning | Базовая категория для предупреждений о возможностях, которые будут объявлены устаревшими в будущем (игнорируется по умолчанию). |
| ImportWarning | Базовая категория для предупреждений, возникающих в процессе импорта модуля (игнорируется по умолчанию). |
| UnicodeWarning | Базовая категория для предупреждений, связанных с Юникодом. |
| BytesWarning | Базовая категория для предупреждений, связанных с bytes и bytearray. |
| ResourceWarning | Базовый класс для предупреждений, связанных с использованием ресурсов. |
Хотя технически это встроенные исключения, они документируются здесь, поскольку концептуально относятся к механизму предупреждений.
Пользовательский код может определять дополнительные категории предупреждений, наследуя одну из стандартных категорий. Категория предупреждения всегда должна быть подклассом класса Warning.
29.5.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).
29.5.2.1. Фильтры предупреждений по умолчанию¶Default Warning Filters
По умолчанию Python устанавливает несколько фильтров предупреждений, которые можно переопределить с помощью опций командной строки, переданных в -W, и вызовов filterwarnings().
- DeprecationWarning, PendingDeprecationWarning и ImportWarning игнорируются.
- BytesWarning игнорируется, если опция -b не указана один или два раза; в этом случае предупреждение либо выводится (-b), либо превращается в исключение (-bb).
- ResourceWarning игнорируется, если только Python не был собран в режиме отладки.
Изменено в версии 3.2: DeprecationWarning теперь по умолчанию игнорируется в дополнение к PendingDeprecationWarning.
29.5.3. Временное подавление предупреждений¶Temporarily Suppressing Warnings
Если используется код, который, как известно, вызовет предупреждение (например, устаревшая функция), но выводить предупреждение не требуется, то его можно подавить с помощью менеджера контекста catch_warnings:
import warnings
def fxn():
warnings.warn("deprecated", DeprecationWarning)
with warnings.catch_warnings():
warnings.simplefilter("ignore")
fxn()
Внутри менеджера контекста все предупреждения просто игнорируются. Это позволяет использовать заведомо устаревший код без необходимости видеть предупреждение, при этом предупреждения не подавляются для другого кода, который может не знать об использовании устаревшего кода. Примечание: это гарантируется только в однопоточном приложении. Если два или более потока используют менеджер контекста catch_warnings одновременно, поведение не определено.
29.5.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() в модуле также восстанавливается до исходного значения. Примечание: это гарантируется только в однопоточном приложении. Если два или более потока используют менеджер контекста catch_warnings одновременно, поведение не определено.
При тестировании нескольких операций, вызывающих один и тот же тип предупреждения, важно тестировать их так, чтобы подтвердить, что каждая операция вызывает новое предупреждение (например, настроить предупреждения как исключения и проверить, что операции вызывают исключения, проверить, что длина списка предупреждений увеличивается после каждой операции, или удалять предыдущие записи из списка предупреждений перед каждой новой операцией).
29.5.5. Обновление кода для новых версий Python¶Updating Code For New Versions of Python
Предупреждения, представляющие интерес только для разработчика, по умолчанию игнорируются. Поэтому следует убедиться, что ваш код тестируется с видимыми предупреждениями, которые обычно игнорируются. Это можно сделать из командной строки, передав интерпретатору флаг -Wd (сокращение от -W default). Это включает стандартную обработку для всех предупреждений, в том числе тех, которые по умолчанию игнорируются. Чтобы изменить действие для встреченных предупреждений, достаточно изменить аргумент, передаваемый в -W, например -W error. Подробнее о возможностях см. флаг -W.
Чтобы программно сделать то же самое, что и -Wd, используйте:
warnings.simplefilter('default')
Убедитесь, что этот код выполняется как можно раньше. Это предотвратит неожиданное влияние регистрации возникших предупреждений на то, как будут обрабатываться будущие предупреждения.
Игнорирование некоторых предупреждений по умолчанию сделано для того, чтобы предотвратить отображение пользователю предупреждений, представляющих интерес только для разработчика. Поскольку вы не обязательно контролируете, какой интерпретатор пользователь использует для запуска кода, между вашими циклами выпуска может выйти новая версия Python. Новый выпуск интерпретатора может вызвать новые предупреждения в вашем коде, которых не было в более старом интерпретаторе, например DeprecationWarning для используемого вами модуля. В то время как вы как разработчик хотите получать уведомления о том, что ваш код использует устаревший модуль, для пользователя эта информация, по сути, является шумом и не приносит пользы.
Модуль unittest также был обновлён для использования фильтра 'default' при выполнении тестов.
29.5.6. Доступные функции¶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().
29.5.7. Доступные менеджеры контекста¶Available Context Managers
- class warnings.catch_warnings(*, record=False, module=None)¶
Менеджер контекста, который копирует и при выходе восстанавливает фильтр предупреждений и функцию showwarning(). Если аргумент record равен False (по умолчанию), менеджер контекста возвращает None при входе. Если record равен True, возвращается список, который постепенно заполняется объектами, наблюдаемыми пользовательской функцией showwarning() (которая также подавляет вывод в sys.stdout). Каждый объект в списке имеет атрибуты с теми же именами, что и аргументы showwarning().
Аргумент module принимает модуль, который будет использоваться вместо модуля, возвращаемого при импорте warnings, чей фильтр будет защищён. Этот аргумент существует в основном для тестирования самого модуля warnings.
Примечание
Менеджер catch_warnings работает, заменяя, а затем восстанавливая функцию showwarning() модуля и внутренний список спецификаций фильтров. Это означает, что контекстный менеджер изменяет глобальное состояние и поэтому не является потокобезопасным.