Python 開發(fā)模式?
3.7 新版功能.
開發(fā)模式下的 Python 加入了額外的運行時檢查,由于開銷太大,并非默認啟用的。如果代碼能夠正確執(zhí)行,默認的調(diào)試級別足矣,不應(yīng)再提高了;僅當覺察到問題時再提升警告觸發(fā)的級別。
使用 -X dev
命令行參數(shù)或?qū)h(huán)境變量 PYTHONDEVMODE
置為 1
,可以啟用開發(fā)模式。
另請參考 Python debug build 。
Python 開發(fā)模式的效果?
啟用 Python 開發(fā)模式后的效果,與以下命令類似,不過還有下面的額外效果:
PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python3 -W default -X faulthandler
Python 開發(fā)模式的效果:
加入
default
warning filter 。下述警告信息將會顯示出來:通常上述警告是由默認的 warning filters 負責(zé)處理的。
效果類似于采用了
-W default
命令行參數(shù)。使用命令行參數(shù)
-W error
或?qū)h(huán)境變量PYTHONWARNINGS
設(shè)為error
,可將警告視為錯誤。在內(nèi)存分配程序中安裝調(diào)試鉤子,用以查看:
緩沖區(qū)下溢
緩沖區(qū)上溢
內(nèi)存分配 API 沖突
不安全的 GIL 調(diào)用
參見 C 函數(shù)
PyMem_SetupDebugHooks()
。效果如同將環(huán)境變量
PYTHONMALLOC
設(shè)為debug
。若要啟用 Python 開發(fā)模式,卻又不要在內(nèi)存分配程序中安裝調(diào)試鉤子,請將 環(huán)境變量
PYTHONMALLOC
設(shè)為default
。在啟動 Python 時調(diào)用
faulthandler.enable()
,會安裝SIGSEGV
、SIGFPE
、SIGABRT
、SIGBUS
和SIGILL
信號的處理程序,以便在程序崩潰時將 Python 跟蹤信息轉(zhuǎn)儲下來。其行為如同使用了
-X faulthandler
命令行選項或?qū)?PYTHONFAULTHANDLER
環(huán)境變量設(shè)為1
。啟用 asyncio debug mode。比如
asyncio
會檢查沒有等待的協(xié)程并記錄下來。效果如同將環(huán)境變量
PYTHONASYNCIODEBUG
設(shè)為1
。檢查字符串編碼和解碼函數(shù)的 encoding 和 errors 參數(shù)。例如:
open()
、str.encode()
和bytes.decode()
。為了獲得最佳性能,默認只會在第一次編碼/解碼錯誤時才會檢查 errors 參數(shù),有時 encoding 參數(shù)為空字符串時還會被忽略。
io.IOBase
的析構(gòu)函數(shù)會記錄close()
觸發(fā)的異常。將
sys.flags
的dev_mode
屬性設(shè)為True
。
Python 開發(fā)模式下,默認不會啟用 tracemalloc
模塊,因為其性能和內(nèi)存開銷太大。啟用 tracemalloc
模塊后,能夠提供有關(guān)錯誤來源的一些額外信息。例如,ResourceWarning
記錄了資源分配的跟蹤信息,而緩沖區(qū)溢出錯誤記錄了內(nèi)存塊分配的跟蹤信息。
Python 開發(fā)模式不會阻止命令行參數(shù) -O
刪除 assert
語句,也不會阻止將 __debug__
設(shè)為 False
。
Python 開發(fā)模式只能在 Python 啟動時啟用。其參數(shù)值可從 sys.flags.dev_mode
讀取。
在 3.8 版更改: 現(xiàn)在, io.IOBase
的析構(gòu)函數(shù)會記錄 close()
觸發(fā)的異常。
在 3.9 版更改: 現(xiàn)在,字符串編碼和解碼操作時會檢查 encoding 和 errors 參數(shù)。
ResourceWarning 示例?
以下示例將統(tǒng)計由命令行指定的文本文件的行數(shù):
import sys
def main():
fp = open(sys.argv[1])
nlines = len(fp.readlines())
print(nlines)
# The file is closed implicitly
if __name__ == "__main__":
main()
上述代碼沒有顯式關(guān)閉文件。默認情況下,Python 不會觸發(fā)任何警告。下面用 README.txt 文件測試下,有 269 行:
$ python3 script.py README.txt
269
啟用 Python 開發(fā)模式后,則會顯示一條 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])
修正方案就是顯式關(guān)閉文件。下面用上下文管理器作為示例:
def main():
# Close the file explicitly when exiting the with block
with open(sys.argv[1]) as fp:
nlines = len(fp.readlines())
print(nlines)
未能顯式關(guān)閉資源,會讓資源打開時長遠超預(yù)期;在退出 Python 時可能會導(dǎo)致嚴重問題。這在 CPython 中比較糟糕,但在 PyPy 中會更糟。顯式關(guān)閉資源能讓應(yīng)用程序更加穩(wěn)定可靠。
文件描述符錯誤示例?
顯示自身的第一行代碼:
import os
def main():
fp = open(__file__)
firstline = fp.readline()
print(firstline.rstrip())
os.close(fp.fileno())
# The file is closed implicitly
main()
默認情況下,Python 不會觸發(fā)任何警告:
$ python3 script.py
import os
在 Python 開發(fā)模式下,會在析構(gòu)文件對象時顯示 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())
會關(guān)閉文件描述符。當文件對象析構(gòu)函數(shù)試圖再次關(guān)閉文件描述符時會失敗,并觸發(fā) Bad file descriptor
錯誤。每個文件描述符只允許關(guān)閉一次。在最壞的情況下,關(guān)閉兩次會導(dǎo)致程序崩潰(示例可參見 bpo-18748 )。
修正方案是刪除 os.close(fp.fileno())
這一行,或者打開文件時帶上 closefd=False
參數(shù)。