Режим разработки на 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()
исключения.
Режим разработки 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
.