check_sphinx_api_rst.py
プログラムの動作
check_sphinx_api_rst.py は、SphinxプロジェクトのReStructuredText (RST) ファイル、特に *_api.rst という命名規則を持つファイルを走査し、Sphinxの autodoc 拡張機能で問題を引き起こしやすい記述を検出・修正するためのユーティリティです。
このプログラムの主な機能は以下の通りです。
問題のあるモジュール名の検出:
.. automodule::ディレクティブに指定されたモジュール名に、Pythonのモジュールインポート規則では許可されないハイフン (-) や、パス区切り文字 (\または/) が含まれていないかをチェックします。これらの文字は通常、ドット (.) またはアンダースコア (_) に置換される必要があります。
__main__ガードなしのargv使用の検出:automoduleで参照されているPythonソースコード (.pyファイル) 内で、sys.argvまたはargvが使用されているにもかかわらず、if __name__ == "__main__":という慣用的なガード句が存在しないケースを検出します。これにより、autodocがモジュールをインポートした際に、意図せずスクリプトのメイン処理が実行されてしまう可能性を回避します。
自動修正機能 (
--fix):--fixオプションが指定された場合、検出された問題を自動的に修正します。automoduleディレクティブ内のモジュール名に含まれる\と/を.に、-を_に置換します。対応するPythonファイル (
.py) のファイル名に含まれる-を_にリネームします。*_api.rstファイル名に含まれる-を_にリネームします。同じディレクトリ内の関連するドキュメントファイル (
usage,examples,indexを含む*.rstや*.mdファイル) における、修正前のファイル名・モジュール名への参照も自動的に修正します。関連するドキュメントファイルのリネームも行います。
ドライランモード (
--dry-run):--fixオプションと組み合わせて--dry-runを指定すると、実際のファイル変更を行わずに、計画されている修正内容をレポートとして出力します。
このプログラムは、Sphinxドキュメントのビルドエラーや意図しない動作を防ぎ、Pythonモジュールとドキュメントの整合性を保つことを目的としています。
原理
check_sphinx_api_rst.py は、主に正規表現とファイルシステム操作を組み合わせて動作します。
ファイル検索と読み込み:
find_files関数が指定されたルートディレクトリ以下から、ワイルドカードパターン (*_api.rstなど) に一致するファイルを再帰的に検索します。read_text関数が、ファイルの文字コードを自動推定して内容を読み込みます。一般的なエンコーディング (utf-8,cp932,shift_jis,latin-1) を試行し、デコードできない場合はerrors="replace"で読み込みます。
RSTファイル解析:
extract_automodule_names関数が、RSTファイルの内容に対してAUTOMODULE_RE正規表現を適用し、.. automodule::ディレクティブからモジュール名を抽出します。AUTOMODULE_RE = r"(^\s*\.\.\s+automodule::\s+)([^\s]+)(\s*$)"これは行頭の
.. automodule::の後に続く空白を含まない文字列(モジュール名)をキャプチャします。
モジュール名の正規化とパス解決:
safe_module_name関数が、抽出されたモジュール名に対して以下の変換を行います。パス区切り文字
\および/を.に置換。ハイフン
-を_に置換。これにより、Pythonの
import文で有効なモジュール名形式に変換されます。
module_name_to_py_path関数が、正規化されたモジュール名から対応するPythonファイルへのパスを推測します (例:regression.adaptive_gaussian_ridge→root/regression/adaptive_gaussian_ridge.py)。fallback_py_path_from_rst関数は、*_api.rstファイル名から同じディレクトリ内の対応するPythonファイルを推定するフォールバックメカニズムを提供します (例:regression/foo_api.rst→regression/foo.py)。
Pythonファイル解析と
argvチェック:has_unprotected_argv関数が、対応するPythonファイルのコンテンツに対して以下の正規表現チェックを行います。MAIN_GUARD_RE = r"if\s+__name__\s*==\s*['\"]__main__['\"]\s*:"if __name__ == "__main__":またはif __name__ == '__main__':のパターンを検出します。
ARGV_RE = r"\b(?:sys\s*\.\s*)?argv\b"単語境界にある
argvまたはsys.argvのパターンを検出します。
この関数は、
__main__ガードが存在しない (MAIN_GUARD_REがマッチしない) かつargvが使用されている (ARGV_REがマッチする) 場合にTrueを返します。
自動修正のロジック (
--fix):fix_module_name関数が、safe_module_nameで得られた正しいモジュール名に基づいて、関連するファイルシステムやファイル内容の修正を行います。rename_file関数は、ファイルが存在し、かつ移動先が存在しない場合にファイルのリネームを実行します。replace_automodule_name関数は、AUTOMODULE_REを用いて、.. automodule::ディレクティブ内のモジュール名のみを安全に置換します。これは、str.replace()では意図しない箇所まで置換してしまう可能性があるためです。replace_in_file関数は、指定された文字列をファイル内で置換します。これは、RSTファイル内の他の参照(例:_usage.rstから_api.rstへの参照)を修正するために使用されます。同じディレクトリにある
_usage,_examples,_indexを含むファイル名やその中の参照も、ハイフンからアンダースコアへの変更が行われます。
これらの原理により、プログラムはSphinx autodoc の厳密な要件を満たすように、RSTドキュメントとPythonソースコードの間の整合性を確保します。
必要な非標準ライブラリとインストール方法
check_sphinx_api_rst.py は、Pythonの標準ライブラリのみを使用しており、追加でインストールが必要な非標準ライブラリはありません。
必要な入力ファイル
このプログラムは、以下の種類のファイルを読み込み、解析します。
Sphinx RSTソースファイル:
デフォルトでは、
--rootオプションで指定されたディレクトリ (デフォルトは./source) 以下にある*_api.rstというパターンに一致するファイル。--filesオプションを使用して、異なるワイルドカードパターン (例:*.rst) を指定することも可能です。これらのファイルには、
.. automodule::ディレクティブが含まれていることが期待されます。
Pythonソースファイル:
*_api.rstファイル内の.. automodule::ディレクティブで指定されたモジュール名に対応する.pyファイル。これらの
.pyファイルは、--rootディレクトリを基準としたパスに存在することが期待されます。例:
.. automodule:: my_package.my_moduleであれば、--root/my_package/my_module.pyが検索されます。対応する
.pyファイルが見つからない場合でも、プログラムは*_api.rstファイル名から同名の.pyファイルをフォールバックとして探します (例:foo_api.rst→foo.py)。
関連するRST/Markdownファイル:
--fixオプションが指定された場合、*_api.rstファイルと同じディレクトリ内にある*_usage.rst,*_examples.rst,*_index.rst,*.rst,*.mdなどのファイルも、修正されたモジュール名やファイル名への参照を更新するために読み込まれることがあります。
生成される出力ファイル
プログラムは、以下の出力ファイルを生成します。
failed_api_rst.txt(デフォルト):--outfileオプションでパスを指定できます。このファイルには、チェックに失敗した
*_api.rstファイルの相対パスと、それぞれ失敗した理由が一覧で記述されます。ファイル例:
path/to/my-module_api.rst - invalid module name contains '-': my-module - corresponding py file not found for module: my-module another/path/script_api.rst - argv is used but __main__ guard was not found: another/path/script.py [ACTIONS] - RENAME path/to/my-module.py -> path/to/my_module.py - REPLACE automodule in path/to/my-module_api.rst: my-module -> my_module - RENAME path/to/my-module_api.rst -> path/to/my_module_api.rst
--fixオプションが使用された場合、ファイルへの変更が実際に実行されたか、または--dry-runで計画された修正アクションのログもこのファイルに追記されます。
修正されたソースファイル (オプション):
--fixオプションが指定され、かつ--dry-runが指定されていない場合、プログラムは検出された問題に基づいて以下のファイルを直接変更します。*_api.rstファイルのコンテンツ (.. automodule::ディレクティブのモジュール名)。対応するPythonソースファイル (
.py) のファイル名。*_api.rstファイルのファイル名。*_usage.rst,*_examples.rst,*_index.rst,*.rst,*.mdなどの関連ファイルのファイル名とそのコンテンツ。
コマンドラインでの使用例 (Usage)
check_sphinx_api_rst.py は、以下のコマンドライン引数を受け付けます。
python check_sphinx_api_rst.py [-h] [--root PATH] [--files PATTERN] [--outfile PATH] [--exclude KEYWORD] [--show-ok {0,1}] [--fix] [--dry-run]
-h,--help: ヘルプメッセージを表示して終了します。--root PATH: Sphinxソースディレクトリのルートパスを指定します。デフォルトは"./source"です。--files PATTERN: ターゲットとなるRSTファイルのワイルドカードパターンを指定します。デフォルトは"*_api.rst"です。--outfile PATH: 出力ファイルパスを指定します。デフォルトは"failed_api_rst.txt"です。--exclude KEYWORD: チェックから除外するパスキーワードを指定します。複数回指定可能です。--show-ok {0,1}: 問題がなかったファイルも表示するかどうかを指定します。デフォルトは0(表示しない) です。--fix:automodule名のハイフンやパス区切り文字を自動的に修正します。--dry-run:--fixオプションと組み合わせて使用し、ファイルを変更せずに計画された修正内容を表示します。
コマンドラインでの具体的な使用例
1. 基本的なチェック実行
デフォルトの ./source ディレクトリ内にある *_api.rst ファイルをチェックし、問題があれば標準出力に表示し、failed_api_rst.txt にログを書き込みます。
python check_sphinx_api_rst.py
実行結果の例:
root : /path/to/project/source
pattern : *_api.rst
found : 5 files
fix : False
dry-run : False
[FAILED] my-module_api.rst
- invalid module name contains '-': my-module
- corresponding py file not found for module: my-module
[FAILED] another_module_api.rst
- argv is used but __main__ guard was not found: another_module.py
checked : 5
skipped : 0
failed : 2 / 5
actions : 0
output : /path/to/project/failed_api_rst.txt
2. 特定のルートディレクトリを指定して実行
./docs ディレクトリをSphinxのソースルートとして指定してチェックします。
python check_sphinx_api_rst.py --root ./docs
3. 自動修正 (ドライランモード)
検出された問題を自動修正しますが、--dry-run が指定されているため、実際のファイル変更は行われません。どのような修正が提案されるかを確認できます。
python check_sphinx_api_rst.py --fix --dry-run
実行結果の例:
root : /path/to/project/source
pattern : *_api.rst
found : 5 files
fix : True
dry-run : True
[FIX] my-module_api.rst
- RENAME /path/to/project/source/my-module.py -> /path/to/project/source/my_module.py
- REPLACE automodule in /path/to/project/source/my-module_api.rst: my-module -> my_module
- RENAME /path/to/project/source/my-module_api.rst -> /path/to/project/source/my_module_api.rst
- REPLACE in /path/to/project/source/index.rst: my-module_api -> my_module_api
[FAILED] my-module_api.rst
- invalid module name still contains invalid character: my-module (Note: This might still show if not fully fixed in the dry run, or if other issues remain)
[FAILED] another_module_api.rst
- argv is used but __main__ guard was not found: another_module.py
checked : 5
skipped : 0
failed : 2 / 5
actions : 4
output : /path/to/project/failed_api_rst.txt
この出力では、[FIX] のセクションでどのような変更が実行される予定かが詳細に表示されます。ファイル名は my-module_api.rst と表示されていますが、これは dry-run 時の表示のため、実際にはリネームされると my_module_api.rst になります。
4. 実際にファイルを修正
--fix オプションのみを指定すると、検出された問題がファイルシステム上で実際に修正されます。
この操作は元に戻せないので、実行前に必ずバックアップを取るか、--dry-run で内容を十分に確認してください。
python check_sphinx_api_rst.py --fix
実行結果の例:
root : /path/to/project/source
pattern : *_api.rst
found : 5 files
fix : True
dry-run : False
[FIX] my-module_api.rst
- RENAME /path/to/project/source/my-module.py -> /path/to/project/source/my_module.py
- REPLACE automodule in /path/to/project/source/my-module_api.rst: my-module -> my_module
- RENAME /path/to/project/source/my-module_api.rst -> /path/to/project/source/my_module_api.rst
- REPLACE in /path/to/project/source/index.rst: my-module_api -> my_module_api
[FAILED] another_module_api.rst
- argv is used but __main__ guard was not found: another_module.py
checked : 5
skipped : 0
failed : 1 / 5
actions : 4
output : /path/to/project/failed_api_rst.txt
この例では、my-module_api.rst の関連する問題は修正され、残る問題は another_module_api.rst の __main__ ガードに関するものだけとなっています。__main__ ガードの不足は自動修正の対象外です。