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

Added in version 3.7.

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

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

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

Влияние режима разработки Python

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

PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python -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 строк:

$ python script.py README.txt
269

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

$ python -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 показывает строку, в которой был открыт файл:

$ python -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 не выдает никаких предупреждений:

$ python script.py
import os

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

$ python -X dev 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.