Режим разработки Python

Добавлено в версии 3.7.

Режим разработки Python вводит дополнительные проверки времени выполнения, которые слишком дороги, чтобы быть включенными по умолчанию. Он не должен быть более многословным, чем по умолчанию, если код корректен; новые предупреждения выдаются только при обнаружении проблемы.

Его можно включить с помощью опции командной строки -X dev или установив переменную окружения PYTHONDEVMODE в значение 1.

См. также Python debug build.

Эффекты режима разработки Python

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

PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python3 -W default -X faulthandler

Эффекты режима разработки Python:

  • Добавьте default warning filter. Отображаются следующие предупреждения:

    Обычно вышеуказанные предупреждения фильтруются по умолчанию warning filters.

    Он ведет себя так, как если бы использовалась опция командной строки -W default.

    Используйте опцию командной строки -W error или установите переменную окружения PYTHONWARNINGS в значение error, чтобы рассматривать предупреждения как ошибки.

  • Установите отладочные крючки на распределители памяти для проверки:

    • Переполнение буфера

    • Переполнение буфера

    • Нарушение API распределителя памяти

    • Небезопасное использование GIL

    См. функцию PyMem_SetupDebugHooks() C.

    Он ведет себя так, как если бы переменная окружения PYTHONMALLOC была установлена в значение debug.

    Чтобы включить режим разработки Python без установки отладочных крючков на распределители памяти, установите переменную окружения PYTHONMALLOC в значение default.

  • Вызовите faulthandler.enable() при запуске Python для установки обработчиков сигналов SIGSEGV, SIGFPE, SIGABRT, SIGBUS и SIGILL для дампа трассировки Python при сбое.

    Он ведет себя так, как если бы использовалась опция командной строки -X faulthandler или если переменная окружения PYTHONFAULTHANDLER установлена в значение 1.

  • Включите asyncio debug mode. Например, asyncio проверяет короутины, которые не были ожидаемы, и регистрирует их.

    Он ведет себя так, как если бы переменная окружения PYTHONASYNCIODEBUG была установлена в значение 1.

  • Проверьте аргументы encoding и errors для операций кодирования и декодирования строк. Примеры: open(), str.encode() и bytes.decode().

    По умолчанию, для лучшей производительности, аргумент errors проверяется только при первой ошибке кодирования/декодирования, а аргумент encoding иногда игнорируется для пустых строк.

  • Деструктор io.IOBase регистрирует close() исключения.

  • Установите атрибут dev_mode в sys.flags на True.

Режим разработки Python по умолчанию не включает модуль tracemalloc, поскольку накладные расходы (на производительность и память) будут слишком велики. Включение модуля tracemalloc предоставляет дополнительную информацию о происхождении некоторых ошибок. Например, ResourceWarning записывает в журнал обратную трассировку, где был выделен ресурс, а ошибка переполнения буфера записывает в журнал обратную трассировку, где был выделен блок памяти.

Режим разработки Python не мешает опции командной строки -O удалять утверждения assert и устанавливать __debug__ в False.

Режим разработки Python может быть включен только при запуске Python. Его значение можно прочитать из sys.flags.dev_mode.

Изменено в версии 3.8: Деструктор io.IOBase теперь регистрирует исключения close().

Изменено в версии 3.9: Аргументы encoding и errors теперь проверяются для операций кодирования и декодирования строк.

Пример ResourceWarning

Пример сценария, подсчитывающего количество строк текстового файла, указанного в командной строке:

import sys

def main():
    fp = open(sys.argv[1])
    nlines = len(fp.readlines())
    print(nlines)
    # The file is closed implicitly

if __name__ == "__main__":
    main()

Сценарий не закрывает файл явным образом. По умолчанию Python не выдает никаких предупреждений. Пример с использованием файла README.txt, который содержит 269 строк:

$ python3 script.py README.txt
269

Включение режима разработки Python выводит предупреждение ResourceWarning:

$ python3 -X dev script.py README.txt
269
script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'>
  main()
ResourceWarning: Enable tracemalloc to get the object allocation traceback

Кроме того, включение функции tracemalloc показывает строку, в которой был открыт файл:

$ python3 -X dev -X tracemalloc=5 script.py README.rst
269
script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'>
  main()
Object allocated at (most recent call last):
  File "script.py", lineno 10
    main()
  File "script.py", lineno 4
    fp = open(sys.argv[1])

Исправление заключается в явном закрытии файла. Пример с использованием контекстного менеджера:

def main():
    # Close the file explicitly when exiting the with block
    with open(sys.argv[1]) as fp:
        nlines = len(fp.readlines())
    print(nlines)

Если не закрыть ресурс явно, он может оставаться открытым гораздо дольше, чем ожидалось; это может вызвать серьезные проблемы при выходе из Python. Это плохо в CPython, но еще хуже в PyPy. Закрытие ресурсов в явном виде делает приложение более детерминированным и надежным.

Пример ошибки плохого дескриптора файла

Скрипт, отображающий первую строку самого себя:

import os

def main():
    fp = open(__file__)
    firstline = fp.readline()
    print(firstline.rstrip())
    os.close(fp.fileno())
    # The file is closed implicitly

main()

По умолчанию Python не выдает никаких предупреждений:

$ python3 script.py
import os

Режим разработки Python показывает ResourceWarning и регистрирует ошибку «Bad file descriptor» при завершении работы с файловым объектом:

$ python3 script.py
import os
script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'>
  main()
ResourceWarning: Enable tracemalloc to get the object allocation traceback
Exception ignored in: <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'>
Traceback (most recent call last):
  File "script.py", line 10, in <module>
    main()
OSError: [Errno 9] Bad file descriptor

os.close(fp.fileno()) закрывает дескриптор файла. Когда финализатор файлового объекта пытается закрыть дескриптор файла снова, он терпит неудачу с ошибкой Bad file descriptor. Дескриптор файла должен быть закрыт только один раз. В худшем случае закрытие его дважды может привести к сбою (см. пример bpo-18748).

Исправление заключается в удалении строки os.close(fp.fileno()) или открытии файла с помощью closefd=False.

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