Режим разработки 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
.