Python 开发模式¶
在 3.7 版本加入。
Python 开发模式引入了额外的运行时检查,这些检查的开销太大,默认情况下无法启用。如果代码正确,它不应比默认模式更详细;只有在检测到问题时才会发出新警告。
可以使用 -X dev
命令行选项或将 PYTHONDEVMODE
环境变量设置为 1
来启用它。
另请参阅 Python 调试构建。
Python 开发模式的影响¶
启用 Python 开发模式类似于以下命令,但具有以下描述的额外效果
PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python -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 调试模式。例如,
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
,并在文件对象最终化时记录“错误文件描述符”错误
$ 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
打开文件。