Python 開発モード
*****************

Added in version 3.7.

Python 開発モードは、コストが高すぎてデフォルトでは有効にできない追加
の実行時チェックを行います。 コードが正しければ、デフォルトより多くの
メッセージが表示されることはないはずです。 問題を検出したときだけ新た
な警告を出します。

"-X dev" コマンドラインオプションを使うか、"PYTHONDEVMODE" 環境変数を
"1" に設定すると有効にできます。

Python のデバッグビルド も参照してください。


Python 開発モードの影響
=======================

Python 開発モードを有効化することは次のコマンドに似ていますが、以下で
説明される追加の影響があります:

   PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python -W default -X faulthandler

Python 開発モードの影響:

* "default" の 警告フィルタ を追加します。次の警告が表示されます:

  * "DeprecationWarning"

  * "ImportWarning"

  * "PendingDeprecationWarning"

  * "ResourceWarning"

  通常、上記の警告は デフォルトの 警告フィルタ によりフィルタリングさ
  れます。

  "-W default" コマンドラインオプションを使った場合と同じ動作になりま
  す。

  警告をエラーとして扱うには、"-W error" コマンドラインオプションを使
  うか、"PYTHONWARNINGS" 環境変数を "error" に設定してください。

* メモリアロケーターにデバッグフックを設定し、次の問題をチェックします
  :

  * バッファーアンダーフロー

  * バッファーオーバーフロー

  * メモリアロケーター API の規則違反

  * GIL の安全でない使用

  C 関数 "PyMem_SetupDebugHooks()" を参照してください。

  "PYTHONMALLOC" 環境変数を "debug" に設定した場合と同じ動作になります
  。

  メモリアロケーターにデバッグフックを設定せずに Python 開発モードを有
  効にするには、"PYTHONMALLOC" 環境変数を "default" に設定してください
  。

* Python 起動時に "faulthandler.enable()" を呼び、"SIGSEGV"、"SIGFPE"
  、"SIGABRT"、"SIGBUS"、"SIGILL" シグナルのハンドラーを設定して、クラ
  ッシュ時に Python のトレースバックを出力します。

  "-X faulthandler" コマンドラインオプションを使うか、
  "PYTHONFAULTHANDLER" 環境変数を "1" に設定した場合と同じ動作になりま
  す。

* asyncio のデバッグモード を有効にします。 例えば "asyncio" は await
  されなかったコルーチンを検出してログに記録します。

  "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" への設定は妨げられません。

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)
       # ファイルは暗黙的に閉じられる

   if __name__ == "__main__":
       main()

このスクリプトはファイルを明示的に閉じていません。 デフォルトでは
Python は警告を出しません。 269 行の README.txt を使った例です:

   $ 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():
       # ブロックを終了する際に明示的にファイルを閉じる
       with open(sys.argv[1]) as fp:
           nlines = len(fp.readlines())
       print(nlines)

リソースを明示的に閉じないと、予想よりずっと長い間開いたままになり、
Python 終了時に重大な問題を起こすことがあります。 CPython でも問題です
が、PyPy ではさらに深刻です。 リソースを明示的に閉じることで、アプリケ
ーションの動作がより確定的になり、信頼性も高まります。


Bad file descriptor エラーの例
==============================

自分自身の最初の行を表示するスクリプトです。

   import os

   def main():
       fp = open(__file__)
       firstline = fp.readline()
       print(firstline.rstrip())
       os.close(fp.fileno())
       # ファイルは暗黙的に閉じられる

   main()

デフォルトでは Python は警告を出しません:

   $ python script.py
   import os

Python 開発モードでは "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())" はファイル記述子を閉じます。 ファイルオブジェ
クトのファイナライザーがそのファイル記述子を再度閉じようとすると、"Bad
file descriptor" エラーで失敗します。 ファイル記述子は 1 回だけ閉じな
ければなりません。 2 回閉じると、最悪の場合クラッシュすることがありま
す (例として bpo-18748 を参照してください)。

"os.close(fp.fileno())" の行を削除するか、"closefd=False" を指定してフ
ァイルを開けば修正できます。
