Python開発モード
バージョン3.7の新機能。
Python開発モードでは、コストがかかりすぎてデフォルトで有効にできない追加のランタイムチェックが導入されます。 コードが正しければ、デフォルトよりも冗長であってはなりません。 新しい警告は、問題が検出された場合にのみ発行されます。
-X dev コマンドラインオプションを使用するか、 PYTHONDEVMODE 環境変数を1
に設定することで有効にできます。
Python開発モードの影響
Python開発モードの有効化は次のコマンドに似ていますが、以下に説明する追加の効果があります。
PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python3 -W default -X faulthandler
Python開発モードの効果:
default
警告フィルターを追加します。 次の警告が表示されます。通常、上記の警告はデフォルトの警告フィルターでフィルタリングされます。
-W default コマンドラインオプションが使用されているかのように動作します。
-Wエラーコマンドラインオプションを使用するか、 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 引数で、文字列のエンコードおよびデコード操作がチェックされるようになりました。
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は警告を発しません。 269行のREADME.txtの使用例:
$ 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())
はファイル記述子を閉じます。 ファイルオブジェクトファイナライザーがファイル記述子を再度閉じようとすると、Bad file descriptor
エラーで失敗します。 ファイル記述子は一度だけ閉じる必要があります。 最悪のシナリオでは、2回閉じるとクラッシュする可能性があります(例については、:issue: `18748` を参照してください)。
修正は、os.close(fp.fileno())
行を削除するか、closefd=False
でファイルを開くことです。