debug_sphinx-build.py 技術ドキュメント

プログラムの動作

debug_sphinx-build.py は、Sphinxドキュメントビルド時に発生するPythonモジュールのimportエラーを診断するためのユーティリティスクリプトです。このプログラムの主な目的と機能、および解決する課題は以下の通りです。

  • プログラムの目的: Sphinxプロジェクトにおいて、特にautodoc機能を使用する際に、依存するPythonモジュールが環境に存在しないことによるimportエラーが発生することがあります。このスクリプトは、Sphinxビルドプロセスの一部であるモジュールのロードとimport処理をシミュレートし、エラー箇所を特定する手助けをします。

  • 主な機能:

    1. conf.py の優先的なロード: Sphinxのメイン設定ファイルである./source/conf.pyを最初にロードし、その内容を解析します。

    2. autodoc_mock_imports の適用: conf.py内で定義されているautodoc_mock_importsリストを読み取り、指定されたモジュール名をPythonのsys.modulesにダミーモジュールとして登録します。これにより、実際にインストールされていないモジュールがimportされてもエラーにならないようにします。

    3. ソースファイルの再帰的import: sourceディレクトリとそのサブディレクトリ内にあるすべての.pyファイルを再帰的に検索し、動的にimportを試みます。

    4. 特定のファイルのスキップ: conf.py自身は個別に処理するため再帰探索からは除外されます。また、ファイル名が _数字列 で終わるファイル(例: sample_1.pytest_2024.pyなど)は、通常テストファイルなどでありSphinxの対象外となることが多いためスキップします。

    5. エラー処理と継続: 通常、importエラーが発生すると処理を中断しますが、--keep-goingオプションが指定された場合は、エラーを記録しながらすべての対象ファイルを最後まで処理し、最後にエラーのサマリーを表示します。

    6. 手動モック機能: --mockオプションを使用して、conf.pyがロードされる前に、任意のモジュールを手動でダミーモジュールとして登録できます。

    7. sys.path の調整: --add-cwd--add-source オプションにより、カレントディレクトリやSphinxのソースディレクトリをsys.pathに追加し、importパスを調整できます。

  • 解決する課題: Sphinxドキュメントのビルドが、システムにインストールされていないライブラリのimportエラーで失敗する場合、原因となっているファイルを特定するのが困難な場合があります。このスクリプトは、Sphinxが内部的に行うimport処理を再現することで、どのファイルがどの依存関係で問題を抱えているかを明確にし、開発者がデバッグしやすくします。特に、大規模なプロジェクトで多数のPythonモジュールがある場合に、効率的にimportエラー箇所を特定するのに役立ちます。

原理

このプログラムは、Pythonの動的なモジュールロード機能と、sys.modulesというPythonのモジュールキャッシュ機構を利用して、Sphinxのimportエラー診断を行います。

  1. ダミーモジュールの導入:

    • DummyObjectクラスは、どのような属性アクセス (__getattr__) や関数呼び出し (__call__) に対しても自身を返すように実装されています。これにより、未定義の属性や関数にアクセスしてもAttributeErrorTypeErrorが発生せず、処理が続行されます。

    • DummyModuleクラスは、標準ライブラリのtypes.ModuleTypeを継承し、__getattr__をオーバーライドしています。これにより、DummyModuleの属性にアクセスすると、その属性名を持つDummyObjectインスタンスが動的に生成され、返されます。

    • install_mock_modules関数は、指定されたモジュール名をsys.modules辞書にDummyModuleインスタンスとして登録します。Pythonのimport機構は、まずsys.modulesをチェックするため、ここに登録されたモジュールは実際のファイルからのロードではなく、ダミーモジュールとして扱われます。

  2. conf.py の処理:

    • プログラムは最初に、指定されたsourceディレクトリ内のconf.pyファイルを、load_module_from_file関数を使用してsphinx_debug.confという一時的なモジュール名で動的にロードします。

    • conf.pyが正常にロードされた後、そのモジュールオブジェクトからautodoc_mock_imports属性を読み取ります。この属性(Sphinxの標準的な設定)にリストされているモジュール名も、上記のinstall_mock_modules関数を使ってダミーモジュールとしてsys.modulesに登録されます。これにより、Sphinxがドキュメント生成時にモックするのと同様の環境を再現します。

  3. ソースファイルの動的ロード:

    • load_module_from_file関数は、importlib.util.spec_from_file_locationimportlib.util.module_from_specを利用して、任意の.pyファイルを特定のモジュール名で動的にロードします。これにより、通常のimport文を使わずにファイルパスからモジュールを直接実行し、sys.modulesに登録することができます。

    • make_module_name関数は、sourceディレクトリからの相対パスに基づいて、ユニークかつPythonの識別子として有効なモジュール名を生成します。これは、サブディレクトリ内のファイルやハイフン、スペースを含むファイル名を適切に処理するためです。

    • should_skip関数は、conf.py(既に処理済みのため)や、_数字列で終わるファイル(例: sample_1.pyなど、テストファイルや一時ファイルによく見られるパターン)をスキップするロジックを提供します。

  4. エラーハンドリングと継続処理:

    • --keep-goingオプションが指定された場合、try...exceptブロックでimportエラーを捕捉し、プログラムの実行を中断せずに次のファイルに進みます。

    • record_error関数は、発生したエラーの詳細(フェーズ、ファイルパス、エラータイプ、メッセージ、トレースバック)を辞書のリストとして内部的に記録します。

    • プログラムの最後に、print_error_summary関数が、記録されたすべてのエラーをまとめて表示し、問題の全体像を把握しやすくします。--show-traceback-summaryオプションを使用すると、個々のエラーの詳細なトレースバックも表示されます。

このアプローチにより、実際の環境に特定のライブラリがインストールされているかどうかに依存せず、多くのPythonソースファイルを順次importし、どのステップでどのようなimportエラーが発生するかを診断することが可能になります。

必要な非標準ライブラリとインストール方法

このプログラムは、Pythonの標準ライブラリのみを使用しており、特別な非標準ライブラリは必要ありません。

したがって、追加のpipコマンドによるインストールは不要です。Pythonがインストールされていれば、そのまま実行できます。

必要な入力ファイル

このプログラムは、以下の構造を持つファイルを期待します。

  1. Sphinxプロジェクトのソースディレクトリ:

    • デフォルトでは source/ という名前のディレクトリを想定しています。

    • このディレクトリは、プログラム実行時に --source オプションで変更できます。

    • このディレクトリ内に、Sphinxの設定ファイル conf.py が存在する必要があります。

    • このディレクトリとそのサブディレクトリ内に、importをテストしたいPython .py ファイルが存在している必要があります。

    例:

    .
    ├── debug_sphinx-build.py
    └── source/
        ├── conf.py
        ├── index.rst
        ├── _static/
        ├── _templates/
        └── my_project/
            ├── __init__.py
            ├── module_a.py
            └── subpackage/
                └── module_b.py
    

生成される出力ファイル

このプログラムは、直接的にはいかなるファイルも生成しません。

すべての出力は標準出力 (stdout) または標準エラー出力 (stderr) に表示されます。 出力される情報には、以下のものが含まれます。

  • 実行中の処理状況 ([INFO], [IMPORT], [SKIP])

  • 発生したエラーメッセージとトレースバック ([ERROR])

  • --keep-going オプション使用時の、最後に表示されるimport失敗のサマリー ([SUMMARY] import 失敗一覧)

  • --show-traceback-summary オプション使用時の、詳細なトレースバック情報

コマンドラインでの使用例 (Usage)

プログラムの基本的な使用法と引数を以下に示します。

python debug_sphinx-build.py [-h] [--source SOURCE] [--add-cwd] [--add-source] [--mock MOCK] [--show-skip] [--keep-going] [--show-traceback-summary]
  • -h, --help: ヘルプメッセージを表示して終了します。

  • --source SOURCE: Sphinxのソースディレクトリを指定します (デフォルト: source)。

  • --add-cwd: カレントディレクトリを sys.path の先頭に追加します。

  • --add-source: 指定したソースディレクトリを sys.path の先頭に追加します。

  • --mock MOCK: conf.py をimportする前に手動でモックするモジュールを ; 区切りで指定します (例: "whisper;torch;numba")。

  • --show-skip: スキップしたファイルも表示します。

  • --keep-going: エラーが出ても終了せず、最後まで続行し、最後に失敗一覧を表示します。

  • --show-traceback-summary: 最後の失敗一覧で traceback も全文表示します (--keep-going と併用)。

コマンドラインでの具体的な使用例

1. 基本的な実行

source/ ディレクトリが存在し、その中に conf.py とPythonモジュールがある最も基本的なケースです。

python debug_sphinx-build.py

実行結果の説明: このコマンドは、source/conf.py をimportし、autodoc_mock_imports を適用した後、source/ ディレクトリ配下のすべての .py ファイルを(スキップ条件に合致しない限り)順番にimportします。途中でimportエラーが発生した場合、その時点でトレースバックを表示して終了します。最後にエラーのサマリーが表示されます。

2. エラーが出ても最後まで継続し、詳細なトレースバックを表示する

複数のimportエラーがある可能性がある場合に、すべての問題を一度に把握したいときに便利です。

python debug_sphinx-build.py --keep-going --show-traceback-summary

実行結果の説明: このコマンドは、source/ ディレクトリ内のすべてのPythonファイルを最後までimportしようとします。途中でimportエラーが発生しても処理を中断せず、エラー情報を記録しながら続行します。処理が完了した後、発生したすべてのimportエラーの要約と、それぞれのエラーの詳細なトレースバックがまとめて表示されます。これにより、複数の問題点を一度に確認できます。

3. 特定のモジュールを手動でモックし、カレントディレクトリも sys.path に追加する

conf.pyautodoc_mock_imports に登録されていないが、conf.py のimport自体が依存しているモジュールや、ソースコードがカレントディレクトリにあるモジュールに依存している場合に役立ちます。

python debug_sphinx-build.py --mock "torch;numpy" --add-cwd

実行結果の説明: このコマンドは、conf.py をimportする前に、torchnumpy モジュールをダミーモジュールとして sys.modules に登録します。また、プログラム実行時のカレントディレクトリを sys.path の先頭に追加します。これにより、conf.py や他のソースファイルがこれらのモジュールやカレントディレクトリ内のモジュールをimportしようとしたときに、エラーを回避し、処理を続行できます。通常通り、importの進捗が表示され、エラーがあれば適宜報告されます。

4. スキップされたファイルも表示する

どのファイルがテスト対象から除外されているかを確認したい場合に。

python debug_sphinx-build.py --show-skip

実行結果の説明: このコマンドは、通常のimport処理に加え、conf.py (個別処理済みのため) や _数字列 で終わるファイル名など、should_skip 関数によってスキップされたファイルとその理由も [SKIP] プレフィックス付きで表示します。これにより、どのファイルがテスト対象外とされたのかを視覚的に確認できます。