【Sphinx】Pythonドキュメントをdocstringから良い感じに作成する
Pythonのドキュメント・リファレンスをdocstringの内容から良い感じに生成してくれる、Sphinxの簡単な使い方を紹介します!
動作環境
Python 3.7.4 Sphinx 1.7.6
完成イメージ
ドキュメントのデザインはこんな感じです。
画像では1ページしかありませんが、モジュールが複数あればモジュールごとにページが作成され、左のリンク集から辿ることができるようになります!
手順
1. Sphinxをインストール
pip install sphinx
2. プロジェクト作成
mkdir docs sphinx-quickstart docs
対話形式でたくさんの質問が来るが、全てそのままEnterでOKです(あとで設定ファイルを編集します)
ただし、 Project name
と Author name(s)
は入力が必要になります。
> Project name: mi-restapi > Author name(s): kizuki-engineer
3. 設定の編集
conf.py
を編集
# 以下のコメントを外し、conf.pyから見たルートディレクトリへのパスを設定します(ここでは../) - # import os - # import sys - # sys.path.insert(0, os.path.abspath('.')) + import os + import sys + sys.path.insert(0, os.path.abspath('../'))
conf.py
に拡張機能を追加
- extensions = [] + extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + ]
autodocの設定を拡張して、プライベートメソッドもドキュメントに含めるように設定します。
conf.py
に以下を追加
+ autodoc_default_flags = [ + 'members', + 'private-members' + ]
4. ドキュメント作成
pyファイルからrstファイルを生成
今回はモジュールごとにドキュメントを生成したいので、 -e
を指定します。
-E, --no-headings Do not create headings for the modules/packages. This is useful, for example, when docstrings already contain headings.
sphinx-apidoc -e -f -o ./docs .
その他のオプションは以下のリンクを参照ください。
トップページの設定をするため、 index.rst
を編集
今回は modules.rst
に全てのモジュールがまとまっていたため、modulesを設定しました。
.. toctree::
:maxdepth: 2
:caption: Contents:
+ modules
ビルドを実行します。
sphinx-build ./docs/ ./docs/_build/
成功すると、./docs/_build/ の下にindex.htmlが生成されます!
質素なページなので、スタイルを変更していきます。
スタイルの変更
sphinx_rtd_themeをインストール、適用していきます。
pip install sphinx_rtd_theme
conf.py
を編集します。
- html_theme = 'alabaster' + html_theme = 'sphinx_rtd_theme'
再度ビルドすると、最初の完成イメージのようなデザインのページが出来上がります!
sphinx-build ./docs/ ./docs/_build/
napoleonのエラー
docstringの書き方によっては、napleonのパース時にwarningが発生してしまいます。
例えば以下のような感じのエラーです。
WARNING: Definition list ends without a blank line; unexpected unindent.
以降では、実際に筆者がハマったエラーとその修正方法を紹介していきます。(例に挙げてるソースコードは雑な再現です)
また、筆者のチームではGoogleスタイルのDocstringを採用しています。
【エラーケース1】dictの書き方が不正
Before
def get_user_detail(user_id): """ ユーザーの詳細情報を取得する Returns: { "ユーザーID": str, "ユーザー名": str, "電話番号": str, "メールアドレス": str, "住所": str, } """
After
def get_user_detail(user_id): """ ユーザーの詳細情報を取得する Returns: dict: ユーザーの詳細情報:: { "ユーザーID": str, "ユーザー名": str, "電話番号": str, "メールアドレス": str, "住所": str, } """
【エラーケース2】 * の使い方が不正
展開で使う * をdocstringで使ってしまうと、napoleonはそれを強調と認識してしまいます。
閉じる * がないのでwarningを出してしまう、というわけです。
before
def create_user_df(args): """ hogehoge Args: user_df: [*args, "hogehoge"] """
after
def create_user_df(args): """ hogehoge Args: user_df: 以下のカラムを持つdf:: [args, "hogehoge"] """
【エラーケース3】不正なセクション
napoleonがサポートしてないセクションを設定してしまうとNG。
before
def get_sales_data(store_id, start_date, end_date): """ 期間内の店舗売り上げを取得する columns: 日付: date 売上: int 出費: int 来客数: int 平均単価: float """
after
def get_sales_data(store_id, start_date, end_date): """ 期間内の店舗売り上げを取得する Returns: dict: 店舗売り上げのデータ:: 日付: date 売上: int 出費: int 来客数: int 平均単価: float """