Python 開發模式¶
在 3.7 版被加入.
Python 開發模式引入了額外的 runtime 檢查,預設啟用這些檢查的成本太高。如果程式碼正確,它不應比預設值更詳細;僅當偵測到問題時才會發出新警告。
可以使用 -X dev
命令列選項或將 PYTHONDEVMODE
環境變數設為 1 來啟用它。
另請參閱 Python 除錯建置。
Python 開發模式的影響¶
啟用 Python 開發模式類似以下指令,但具有如下所述的附加效果:
PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python -W default -X faulthandler
Python 開發模式的效果:
新增
default
警告過濾器。以下警告會被顯示:一般來說,上述警告會被預設的警告過濾器給過濾掉。
它的行為就像使用
-W default
命令列選項一樣。使用
-W error
命令列選項或將PYTHONWARNINGS
環境變數設為error
會將警告視為錯誤。在記憶體分配器上安裝除錯 hook(掛鉤)以檢查:
緩衝區下溢 (underflow)
緩衝區溢位 (overflow)
記憶體分配器 API 違規
GIL 的不安全使用
請參閱
PyMem_SetupDebugHooks()
C 函式。它的行為就好像是將
PYTHONMALLOC
環境變數設定為debug
一樣。若要啟用 Python 開發模式而不在記憶體分配器上安裝偵錯 hook,請將
PYTHONMALLOC
環境變數設為default
。在 Python 啟動時呼叫
faulthandler.enable()
來為SIGSEGV
、SIGFPE
、SIGABRT
、SIGBUS
和SIGILL
訊號安裝處理函式以在當機時傾印 (dump) Python 回溯 (traceback)。它的行為就像使用
-X faulthandler
命令列選項或將PYTHONFAULTHANDLER
環境變數設定為1
。啟用 asyncio 除錯模式。例如
asyncio
會檢查未被等待的 (not awaited) 協程並記錄 (log) 它們。它的行為就像將
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 開發模式在最終化 (finalize) 檔案物件時顯示 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())
會關閉檔案描述器。當檔案物件最終化函式 (finalizer) 嘗試再次關閉檔案描述器時,它會失敗並出現 Bad file descriptor
錯誤。檔案描述器只能關閉一次。在最壞的情況下,將它關閉兩次可能會導致崩潰 (crash)(相關範例請參閱 bpo-18748)。
修復方法是刪除 os.close(fp.fileno())
那列,或使用 closefd=False
開啟檔案。