読者です 読者をやめる 読者になる 読者になる

水深1mm

広く浅い個人的メモ。

SphinxでのAPIドキュメント作成 まとめ

作業環境

環境構築

Sphinxのインストール

pip install sphinx

ドキュメント出力対象のプロジェクトと最終的なSphinxプロジェクト

https://drive.google.com/file/d/0B4fmLEFqtND0MGZRMWh2TlNNTlE/view?usp=sharing

PromotedTest

PromotedTest
│  ButtonWrapper.py
│  MainWindow.py
│  ui_PromotedTest.py
│  ViewWrapper.py
│  WrapperBase.py
│  __init__.py
│
└─ui
        ui2py.py
        ui_PromotedTest.ui

ソースへdocstringの記述

記述位置

大体こんな感じ

# -*- coding:utf-8 -*-

"""
Module no docstring
"""

import os
import sys


class Hogehoge(object):
  """
  Class no docstring
  """

  def __init__(self, a, b):
    """
    Method no docstring
    """
    pass

  def huga(self, i, j):
    """
    Method no docstring
    """
    pass

変数については

sphinx python docstring instance attribute

とか

sphinx python docstring class attribute

でググれば出る

Methodのdocstring書式

PyCharmの場合docstringで補完とかしてくれるのでドキュメントを出さないにしても書いておくと楽
引数の説明等々は以下の通り

def huga(self, a, b):
  """
  説明

  :type a: 引数の型
  :type b: 引数の型
  :param a: 引数の説明
  :param b: 引数の説明
  :rtype: 戻り値の型
  """
  pass

@property
def value(self):
  """
  説明

  :getter: getterの説明
  :type: 戻り値の型
  """
  pass

setterは知らん

PyCharmのdocstringショートカット

def huga(self, a, b):
  pass

という形の関数があった場合に関数名・引数のそれぞれの変数部分でAlt+Enter押すと
何種類かのdocstringを挿入する項目が表示される

  • Insert documentation string stub
    • docstringのテンプレを追加
  • Specify return type in docstring
    • 関数の戻り値の型の記述を追加
  • Specify type for reference in docstring
    • 引数の型の記述を追加

このショートカットで上記の形式のdocstringになっていない場合には

Setting > Tools > Python Integrated Tools > docstring

の設定を reStructuredText に変更する必要がある

Sphinxでの作業

Sphinxではrstファイルを使用してhtmlなどのファイルを出力する
そのために、まずはSphinxのプロジェクト・rstファイルの作成をする必要がある

基本的にプロジェクトを作成するには sphinx-quickstart のコマンドを使用するが、
apiドキュメント用のプロジェクト作成コマンド sphinx-apidoc があったのでそれを使用する

プロジェクトの作成

コマンドラインから

sphinx-apidoc -F -f -o [OUTPUT] [PATH]

  • -F
    • フルサイズのSphinxプロジェクトの生成
  • -f
    • 強制的に上書き
  • -o [OUTPUT]
    • プロジェクト出力先の指定
  • [PATH]

コマンド実行後、出力されたSphinxプロジェクトに移動して

make html

と実行するとhtmlファイルが生成される...はずだが、このままではパス諸々の設定が
正しくない場合があるので conf.py を編集して設定を変更する

conf.pyの編集

Sphinxプロジェクトから対象プログラムへのパスを通す

conf.pyの割と最初のほうにある

import os
import sys
sys.path.insert(0, os.path.abspath('.'))

コメントアウトを外して os.path.abspath('.') の部分に対象となる
プログラムが読み込めるようなパスを指定する

ここに指定するパスはconf.pyの存在するディレクトリから見たパスなので

_DATA
├─SphinxProject
└─TargetPackage

このように対象となるプログラムがSphinxプロジェクトのディレクトリと同階層にあった場合には

sys.path.insert(0, os.path.abspath('..'))

といった指定にする

―――
ここまで設定した後に

make html

を実行するとhtmlが出力される
.
.
.
.
...で、出力されたのがコレ f:id:bonbonbe:20160922205723p:plain

どこから飛んでもこのページ...クッソ見づらい...見づらくない??

そもそもSphinxでのドキュメント出力を勉強しようと思った切っ掛けが、
仕事でバカでかいパッケージの修正(ドキュメントなし)案件が来たので

  • 開発用にprivateも含めたすべてのMethodが見たい
  • Method・Attributeの一覧が見たい
  • クラスの一覧→1クラス1ページでなんかよくあるドキュメントっぽくしたい

といったことがしたかった。

というわけで上記を実現するための処理を追加していく。

privateを含めたMethodの表示

conf.pyに autodoc_default_flags を追加して private-members を設定

autodoc_default_flags = [
    'members',
    'private-members'
]

この設定はSphinxの拡張であるautodocに対してのデフォルトを設定する
ここに special-members と追加することで__init__等の特殊関数の表示もできる

autodocはrstファイルに書かれてる

  • automodule
  • autoclass
  • automethod

とかそのへんのやつ

クラスの一覧→1クラス1ページで表示

Moduleごとにrstファイルの生成

クラスの一覧→1クラス1ページを実現するためにはまずモジュールごとの
rstファイルを作成する必要がある

そのために sphinx-apidoc 実行時にオプションを追加する

コマンドラインから

sphinx-apidoc -F -f -e -o [OUTPUT] [PATH]

  • -e
    • Moduleごとにrstファイルを生成

としてコマンドを実行

これでモジュールごとにrstファイルが生成される

一覧表示とクラス別ページの生成

モジュールごとに生成されたrstファイルに autosummary のディレクティブを追加することで
一覧表示とクラス別ページを同時に実現できる

conf.pyの修正

autosummaryはSphinxの拡張なので使用することを明示する必要がある

conf.pyの extensionssphinx.ext.autosummary を追加する

extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.autosummary',
    'sphinx.ext.todo',
    'sphinx.ext.viewcode',
]

また、 autosummary_generate = True という記述も追加しておく必要がある
これがないとモジュール別→クラス別のページとして作成されない

モジュールごとのrstファイルの修正

モジュールごとに

PromotedTest.ButtonWrapper module
=================================

.. automodule:: PromotedTest.ButtonWrapper
    :members:
    :undoc-members:
    :show-inheritance:

上記のような内容のrstファイルが生成されるので

PromotedTest.ButtonWrapper module
=================================

.. automodule:: PromotedTest.ButtonWrapper
    :members:
    :undoc-members:
    :show-inheritance:

    .. autosummary::
        :toctree: _gen

        ButtonWrapper

というように追加

この場合の

    .. autosummary::
        :toctree: _gen

        ButtonWrapper

ButtonWraperは クラス名 なので注意
1Moduleに複数のクラスがある場合は表示させたいクラス名を全て記述する必要がある

:toctree: _gen は(autosummary_generate = Trueの場合に)_genディレクトリに
rstファイル出力しますよ的なアレ
で、rstが生成されていないと別ページにならない

―――
ここまで設定した後に

make html

を実行するとhtmlが出力される
.
.
.
.
...で、出力されたのがコレ f:id:bonbonbe:20160922205730p:plain

出来た!!
.
.
.
.
...ただし親クラスのpublic、テメーはダメだ!!

この状態だとクラス別のページで親クラスのpublicも一覧に表示されてしまい、非常に見づらい。
というわけで、消す。

autosummaryの一覧から親クラスのpublicの表示を消す

恐らく他の方法がある

自分が調べる限り、conf.pyやディレクティブでなんやかんやできるという情報は見つからなかった。
ので、ソースとautosummaryのclassのtemplateを修正する。

修正するのは

  • \sphinx\ext\autosummary\generate.py
  • \sphinx\ext\autosummary\templates\autosummary\class.rst

の2つ

generate.pyのget_membersを以下のように修正

def get_members(obj, typ, include_public=[], no_inherited=True):
    items = []
    for name in dir(obj):
        try:
            documenter = get_documenter(safe_getattr(obj, name),
                                        obj)
        except AttributeError:
            continue
        if documenter.objtype == typ:
            items.append(name)

    if no_inherited:
        import inspect
        items = set(items)
        for cls in inspect.getmro(obj)[1:]:
              items = items - set(dir(cls))

        items = list(items)

    public = [x for x in items
              if x in include_public]
    return public, items

inspectでなんやかんやしてitemsから親クラスのmemberを削除している
また、クラス別ページのMethod一覧内にprivateも含めたかったので最後の内包表記ではじいていた処理を削除

class.rstは以下のように修正

{{ fullname }}
{{ underline }}

.. currentmodule:: {{ module }}

.. autoclass:: {{ objname }}
   :show-inheritance:

   {% block methods %}
   .. automethod:: __init__

   {% if all_methods %}
   .. rubric:: Methods

   .. autosummary::
   {% for item in all_methods %}
      ~{{ name }}.{{ item }}
   {%- endfor %}
   {% endif %}
   {% endblock %}

   {% block attributes %}
   {% if all_attributes %}
   .. rubric:: Attributes

   .. autosummary::
   {% for item in all_attributes %}
      ~{{ name }}.{{ item }}
   {%- endfor %}
   {% endif %}
   {% endblock %}

修正箇所は

  • autoclassの下に:show-inheritance:を追加
  • methodsをall_methodsに変更
  • attributesをall_attributesに変更

の3箇所

:show-inheritance:の追加は親クラスのMember一覧については別ページで表示するため

methods・attributesを修正したのは上記のget_membersの修正によりmethod名・attribute名が
all~に入るようになったため

get_membersの変更を正しく行えばmethods・attributesからall~の変更は不要
また、autosummaryのtemplateの修正については別ファイルで行い、autosummaryディレクティブに
:template: のフラグを追加するべきなのだろうが、今回は力尽きたのでこれで良しとした

―――
ここまで設定した後に(略)
.
.
.
.
...で、出力されたのがコレ f:id:bonbonbe:20160922205715p:plain

完成!! 第三部完!!

―――
(追記:10/1)

その他・気になったところ

  • クラス別ページの出力はされているのに、クラス一覧→クラス別ページに飛ばないことがあった。
    • automoduleディレクティブに:noindex:を追加することで解決

参考・気になったところとかは気が向いたら追加