Python 开发模式 — Python 文档
Python开发模式
3.7 版中的新功能。
Python 开发模式引入了额外的运行时检查,这些检查成本太高而无法默认启用。 如果代码正确,它不应该比默认的更冗长; 只有在检测到问题时才会发出新警告。
可以使用 -X dev 命令行选项或通过将 PYTHONDEVMODE 环境变量设置为 1
来启用它。
Python开发模式的影响
启用 Python 开发模式类似于以下命令,但具有以下描述的附加效果:
PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python3 -W default -X faulthandler
Python开发模式的作用:
添加
default
警告过滤器。 显示以下警告:通常,上述警告由默认 警告过滤器 过滤。
它的行为就像使用了 -W default 命令行选项。
使用 -W error 命令行选项或将 PYTHONWARNINGS 环境变量设置为
error
以将警告视为错误。在内存分配器上安装调试钩子以检查:
缓冲区下溢
缓冲区溢出
内存分配器 API 违规
GIL 的不安全使用
请参阅 PyMem_SetupDebugHooks() C 函数。
它的行为就像 PYTHONMALLOC 环境变量设置为
debug
。要启用 Python 开发模式而不在内存分配器上安装调试挂钩,请将 PYTHONMALLOC 环境变量设置为
default
。在 Python 启动时调用 faulthandler.enable() 为
SIGSEGV
、SIGFPE
、SIGABRT
、SIGBUS
和SIGILL
信号在崩溃时转储 Python 回溯。它的行为就像使用了 -X faulthandler 命令行选项,或者 PYTHONFAULTHANDLER 环境变量设置为
1
。启用 异步调试模式 。 例如,asyncio 检查没有等待的协程并记录它们。
它的行为就像 PYTHONASYNCIODEBUG 环境变量设置为
1
。检查字符串编码和解码操作的 encoding 和 errors 参数。 示例:open()、str.encode() 和 bytes.decode()。
默认情况下,为了获得最佳性能,errors 参数仅在第一个编码/解码错误时检查,而 encoding 参数有时会被忽略为空字符串。
io.IOBase 析构函数记录
close()
异常。将sys.flags的
dev_mode
属性设置为True
。
Python 开发模式默认不启用 tracemalloc 模块,因为开销成本(对性能和内存)会太大。 启用 tracemalloc 模块可提供有关某些错误来源的附加信息。 例如,ResourceWarning 记录分配资源的回溯,缓冲区溢出错误记录分配内存块的回溯。
Python 开发模式不会阻止 -O 命令行选项删除 assert 语句,也不会阻止将 __debug__ 设置为 False
。
3.8 版更改: io.IOBase 析构函数现在记录 close()
异常。
3.9 版更改:现在检查 encoding 和 errors 参数的字符串编码和解码操作。
资源警告示例
计算命令行中指定的文本文件的行数的脚本示例:
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
错误。 一个文件描述符只能关闭一次。 在最坏的情况下,关闭它两次可能会导致崩溃(参见 :issue:`18748` 示例)。
修复方法是删除 os.close(fp.fileno())
行,或使用 closefd=False
打开文件。