Режим розробки Python¶
Нове в версії 3.7.
Режим розробки Python вводить додаткові перевірки під час виконання, які занадто дорогі, щоб їх можна було ввімкнути за замовчуванням. Якщо код правильний, він не повинен бути більш детальним, ніж стандартний; нові попередження видаються лише тоді, коли виявляється проблема.
Його можна ввімкнути за допомогою параметра командного рядка -X dev
або встановивши для змінної середовища PYTHONDEVMODE
значення 1
.
Ефекти режиму розробки Python¶
Увімкнення режиму розробки Python подібне до наступної команди, але з додатковими ефектами, описаними нижче:
PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python3 -W default -X faulthandler
Наслідки режиму розробки Python:
Додайте
за замовчуванням
фільтр попереджень. Відображаються такі попередження:Зазвичай наведені вище попередження фільтруються стандартними фільтрами попереджень.
Він поводиться так, ніби використовується параметр командного рядка
-W за замовчуванням
.Використовуйте параметр командного рядка
-W error
або встановіть для змінної середовищаPYTHONWARNINGS
значенняerror
, щоб розглядати попередження як помилки.Встановіть перехоплювачі налагодження на розподілювачі пам’яті, щоб перевірити:
Недоповнення буфера
Переповнення буфера
Порушення API розподілювача пам’яті
Небезпечне використання GIL
Перегляньте функцію C
PyMem_SetupDebugHooks()
.Він поводиться так, ніби для змінної середовища
PYTHONMALLOC
встановлено значенняdebug
.Щоб увімкнути режим розробки Python, не встановлюючи налагоджувальні засоби розподілення пам’яті, встановіть для змінної середовища
PYTHONMALLOC
значенняdefault
.Call
faulthandler.enable()
at Python startup to install handlers for theSIGSEGV
,SIGFPE
,SIGABRT
,SIGBUS
andSIGILL
signals to dump the Python traceback on a crash.Він поводиться так, ніби використовується параметр командного рядка
-X faulthandler
або якщо змінна середовищаPYTHONFAULTHANDLER
має значення1
.Увімкнути асинхронний режим налагодження. Наприклад,
asyncio
перевіряє співпрограми, які не були очікувані, і реєструє їх.Він поводиться так, ніби для змінної середовища
PYTHONASYNCIODEBUG
встановлено значення1
.Перевірте аргументи encoding і errors для операцій кодування та декодування рядків. Приклади:
open()
,str.encode()
іbytes.decode()
.За замовчуванням для найкращої продуктивності аргумент errors перевіряється лише при першій помилці кодування/декодування, а аргумент encoding іноді ігнорується для порожніх рядків.
Деструктор
io.IOBase
реєструє виняткиclose()
.Set the
dev_mode
attribute ofsys.flags
toTrue
.
Режим розробки Python не вмикає модуль tracemalloc
за замовчуванням, оскільки накладні витрати (для продуктивності та пам’яті) будуть занадто великими. Увімкнення модуля tracemalloc
надає додаткову інформацію про походження деяких помилок. Наприклад, ResourceWarning
реєструє відстеження, де було виділено ресурс, а помилка переповнення буфера реєструє відстеження, де було виділено блок пам’яті.
Режим розробки Python не заважає параметру командного рядка -O
видаляти оператори assert
або встановлювати __debug__
значення False
.
Змінено в версії 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
і реєструє помилку «Поганий дескриптор файлу» під час завершення об’єкта файлу:
$ 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())
закриває дескриптор файлу. Коли фіналізатор файлового об’єкта знову намагається закрити файловий дескриптор, це не вдається з помилкою Поганий файловий дескриптор
. Файловий дескриптор має бути закрито лише один раз. У гіршому випадку подвійне закриття може призвести до збою (див. bpo-18748 для прикладу).
Виправлення полягає у видаленні рядка os.close(fp.fileno())
або відкритті файлу closefd=False
.