warnings — Warning control

Вихідний код: 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

Основна категорія для попереджень, що викликаються під час процесу імпорту модуля (ігнорується за умовчанням).

Попередження Unicode

Базова категорія для попереджень, пов’язаних із Unicode.

BytesWarning

Базова категорія для попереджень, пов’язаних із bytes і bytearray.

ResourceWarning

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

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

Фільтр попереджень

Фільтр попереджень контролює, чи попередження ігноруються, відображаються чи перетворюються на помилки (створення винятку).

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

  • action є одним із таких рядків:

    Значення

    Розпорядження

    "за умовчанням"

    надрукувати перше повторення відповідних попереджень для кожного місця (модуль + номер рядка), де видається попередження

    "помилка"

    перетворити відповідні попередження на винятки

    "ігнорувати"

    ніколи не друкувати відповідні попередження

    "завжди"

    завжди друкувати відповідні попередження

    "модуль"

    надрукувати перше повторення відповідних попереджень для кожного модуля, де видається попередження (незалежно від номера рядка)

    "один раз"

    друкувати лише перше повторення відповідних попереджень, незалежно від розташування

  • message — це рядок, що містить регулярний вираз, якому має відповідати початок попереджувального повідомлення без урахування регістру. У -W і PYTHONWARNINGS message — це літеральний рядок, який має містити початок попереджувального повідомлення (незалежно від регістру), ігноруючи пробіли на початку або в кінці message .

  • category — це клас (підклас Warning), підкласом якого має бути категорія попередження, щоб відповідати.

  • module is a string containing a regular expression that the start of the fully qualified module name must match, case-sensitively. In -W and PYTHONWARNINGS, module is a literal string that the fully qualified module name must be equal to (case-sensitively), ignoring any whitespace at the start or end of 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, *, skip_file_prefixes=None)

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

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

This makes the warning refer to deprecated_api’s caller, rather than to the source of deprecated_api itself (since the latter would defeat the purpose of the warning message).

The skip_file_prefixes keyword argument can be used to indicate which stack frames are ignored when counting stack levels. This can be useful when you want the warning to always appear at call sites outside of a package when a constant stacklevel does not fit all call paths or is otherwise challenging to maintain. If supplied, it must be a tuple of strings. When prefixes are supplied, stacklevel is implicitly overridden to be max(2, stacklevel). To cause a warning to be attributed to the caller from outside of the current package you might write:

# example/lower.py
_warn_skips = (os.path.dirname(__file__),)

def one_way(r_luxury_yacht=None, t_wobbler_mangrove=None):
    if r_luxury_yacht:
        warnings.warn("Please migrate to t_wobbler_mangrove=.",
                      skip_file_prefixes=_warn_skips)

# example/higher.py
from . import lower

def another_way(**kw):
    lower.one_way(**kw)

This makes the warning refer to both the example.lower.one_way() and package.higher.another_way() call sites only from calling code living outside of example package.

джерело, якщо вказано, це знищений об’єкт, який випустив ResourceWarning.

Змінено в версії 3.6: Додано параметр джерело.

Змінено в версії 3.12: Added skip_file_prefixes.

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-файлах або інших джерелах імпорту, які не належать до файлової системи).

джерело, якщо вказано, це знищений об’єкт, який випустив ResourceWarning.

Змінено в версії 3.6: Додайте параметр source.

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

Записати попередження у файл. Стандартна реалізація викликає formatwarning(message, category, filename, lineno, line) і записує результуючий рядок у file, який за замовчуванням має значення sys.stderr. Ви можете замінити цю функцію будь-якою викликаною, призначивши warning.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)

Вставте запис у список специфікацій фільтра попереджень. Запис вставляється спереду за замовчуванням; якщо 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, action=None, category=Warning, lineno=0, append=False)

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

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

If the action argument is not None, the remaining arguments are passed to simplefilter() as if it were called immediately on entering the context.

Примітка

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

Змінено в версії 3.11: Added the action, category, lineno, and append parameters.