warnings — Предупреждающий контроль

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


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

Программисты на Python выдают предупреждения, вызывая функцию warn(), определенную в этом модуле. (Программисты на C используют 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

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

BytesWarning

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

ResourceWarning

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

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

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

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

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

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

    Ценность

    Расположение

    "default"

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

    "error"

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

    "ignore"

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

    "always"

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

    "module"

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

    "once"

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

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

  • категория - это класс (подкласс 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).

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

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

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

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

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

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

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

Изменено в версии 3.6: Добавлен параметр «Источник».

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

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

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

источником, если он указан, является уничтоженный объект, который выдал 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. строка - это строка исходного кода, которая должна быть включена в предупреждающее сообщение; если строка не указана, showwarning() попытается прочитать строку, указанную с помощью filename и lineno.

warnings.formatwarning(message, category, filename, lineno, line=None)

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

warnings.filterwarnings(action, message='', category=Warning, module='', lineno=0, append=False)

Вставьте запись в список warnings filter specifications. По умолчанию запись вставляется в начале; если значение добавить равно 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, action=None, category=Warning, lineno=0, append=False)

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

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

Если аргумент action не равен None, остальные аргументы передаются в simplefilter(), как если бы он был вызван сразу после входа в контекст.

Примечание

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

Изменено в версии 3.11: Добавлены параметры action, category, lineno и append.

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