warnings — Контроль предупреждения

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


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

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

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

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

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

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

См.также

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

Предупреждающие категории

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

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

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

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

Класс

Описание

Warning

Это базовый класс всех классов категорий предупреждений. Он является подклассом Exception.

UserWarning

Категория по умолчанию для warn().

DeprecationWarning

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

SyntaxWarning

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

RuntimeWarning

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

FutureWarning

Базовая категория для предупреждений об устаревших функциях, когда эти предупреждения предназначены для конечных пользователей приложений, написанных на Python.

PendingDeprecationWarning

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

ImportWarning

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

UnicodeWarning

Базовая категория для предупреждений, связанных с Unicode.

BytesWarning

Базовая категория для предупреждений, связанных с bytes и bytearray.

ResourceWarning

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

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

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

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

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

  • action - это одна из следующих строк:

    Значение

    Утилизация

    "default"

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

    "error"

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

    "ignore"

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

    "always"

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

    "module"

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

    "once"

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

  • message - это строка, содержащая регулярное выражение, которому должно соответствовать начало предупреждающего сообщения (регистронезависимо). В -W и PYTHONWARNINGS, message - это буквальная строка, которую должно содержать начало предупреждающего сообщения (регистронезависимо), игнорируя любые пробелы в начале или конце message.

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

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

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

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

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

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

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

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

action:message:category:module:line

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

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

default                      # Show all warnings (even those ignored by default)
ignore                       # Ignore all warnings
error                        # Convert all warnings to errors
error::ResourceWarning       # Treat ResourceWarning messages as errors
default::DeprecationWarning  # Show DeprecationWarning messages
ignore,default:::mymodule    # Only report warnings triggered by "mymodule"
error:::mymodule             # Convert warnings to errors in "mymodule"

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

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

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

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

При debug build список фильтров предупреждений по умолчанию пуст.

Изменено в версии 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") # Change the filter in this process
    os.environ["PYTHONWARNINGS"] = "default" # Also affect subprocesses

Наконец, разработчикам интерактивных оболочек, выполняющих пользовательский код в пространстве имен, отличном от __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:
    # Cause all warnings to always be triggered.
    warnings.simplefilter("always")
    # Trigger a warning.
    fxn()
    # Verify some things
    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).

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

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

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

Выдать предупреждение, а может проигнорировать его или выдать исключение. Аргумент category, если он задан, должен быть warning category class; по умолчанию он равен UserWarning. Альтернативно, message может быть экземпляром Warning, в этом случае category будет проигнорирована и будет использоваться message.__class__. В этом случае текст сообщения будет str(message). Эта функция вызывает исключение, если конкретное выданное предупреждение меняется на ошибку с помощью warnings filter. Аргумент 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, если указано, должно быть глобальным пространством имен, используемым кодом, для которого выдано предупреждение. (Этот аргумент используется для поддержки отображения источника для модулей, найденных в 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)

Вставляет запись в список warnings filter specifications. По умолчанию запись вставляется спереди; если append равен true, она вставляется в конец. Это проверяет типы аргументов, компилирует регулярные выражения message и module и вставляет их в виде кортежа в список фильтров предупреждений. Записи, расположенные ближе к началу списка, перекрывают записи, расположенные позже в списке, если они обе соответствуют определенному предупреждению. Опущенные аргументы по умолчанию имеют значение, которое соответствует всем.

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

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

warnings.resetwarnings()

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

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

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

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

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

Примечание

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

Вернуться на верх