みおもん倶楽部 技術雑記

世界の片隅で、諜報活動と称して本を読んだりゲームをしたり

Sphinxを使ってpythonスクリプトからドキュメントを生成してみる

いつも恒例のn番煎じシリーズいきます。

やること

pythonスクリプト(docstring付き)をhtmlファイルのドキュメントに変換します。 docstring自体の記法はここでは触れません。

事前準備

pipでパッケージを入れておきます。

pip install sphinx

あとで使うので、テーマファイルも入れます。

pip install sphinx_rtd_theme

ソースコードを用意

.
└── src
    ├── entry.py
    ├── mod1.py
    └── mod2.py

上記のような感じでpythonファイルをいくつか用意します。とりあえずdocstringがちょっと書かれていれば中身はなんでもいいです。 ここでは以下のような感じでざっくり書いてみました。

entry.py

from mod1 import split_basename, compute_awesome_value, check_list_has_even_length
from mod2 import DownCounter


def main():
    """
    エントリーポイント

    以下の関数を順に実行する

    - split_basename
    - compute_awesome_value
    - check_list_has_even_length
    - DownCounterのインスタンス作成
    - DownCounterのtickメソッドを5回実行
    """
    rev1 = split_basename("hello/world.txt")
    rev2 = compute_awesome_value(2, 3.4, True)
    rev3 = check_list_has_even_length([1, 2, 3])

    print(rev1)
    print(rev2)
    print(rev3)

    counter = DownCounter(5, 1)
    for _ in range(5):
        counter.tick()


if __name__ == "__main__":
    main()

mod1.py

def split_basename(filepath: str) -> str:
    """ファイルパスからベース名を取得する

    Args:
        filepath (str): ベース名を取得するファイルパス

    Returns:
        str: ファイルパスのベース名
    """
    return filepath.split('/')[-1]


def compute_awesome_value(i: int, f: float, multiple: bool) -> float:
    """条件に基づいて素晴らしい値を計算する

    Args:
        i (int): 整数値
        f (float): 浮動小数点数
        multiple (bool): 掛け算または足し算を行うかを示すブール値

    Returns:
        float: 算術演算の結果
    """
    if multiple:
        return i * f
    else:
        return i + f


def check_list_has_even_length(numlist: list[int]) -> bool:
    """リストの長さが偶数かどうかを確認する

    Args:
        numlist (list[int]): 整数のリスト

    Returns:
        bool: リストの長さが偶数の場合はTrue、そうでない場合はFalse
    """
    if type(numlist) is not list:
        return False

    return len(numlist) % 2 == 0

mod2.py

class DownCounter:
    """
    カウンタ機能を持ったクラス

    Attributes:
        count: カウンタの値

    Methods:

        - increment: カウンタを1増やす
        - decrement: カウンタを1減らす
        - set: カウンタの値を設定する
        - get: カウンタの値を取得する
    """

    initial_count: int
    count: int
    step: int

    def __init__(self, count: int = 100, step: int = 1):
        """
        カウンタ値とstepを初期化する

        Args:
            count: カウンタの初期値、デフォルトは100
            step: カウンタを増減させる値、デフォルトは1

        """
        if type(count) is not int:
            raise ValueError("count must be an integer")

        if type(step) is not int:
            raise ValueError("step must be an integer")

        if count < 0:
            raise ValueError("count must be a non-negative integer")

        if step < 0:
            raise ValueError("step must be a non-negative integer")

        self.initial_count = count
        self.count = count
        self.step = step

    def tick(self):
        """
        カウンタ値を1減らし、0未満になった場合は警告を出力する
        """
        self.count -= self.step

        if self.count <= 0:
            print("beep beep beep!!")

    def reset(self):
        """
        カウンタ値を初期値にリセットする
        """
        self.count = self.initial_count

ドキュメントの雛形生成

sphinx-apidocで作っていきます。

sphinx-apidoc ./src -a -F -o docs

いろいろファイルが生成されますが、ビルドに関わる設定はconf.pyに対して行います。

説明

  • -a: conf.py./srcを追加、ただし絶対パスなので、複数人で持ち回る場合は相対パスに書き換えた方がよいと思います
  • -F: ビルドに必要なあれこれ(Makefileとか)も同時に作成します
  • -o docs: 出力先を指定します

-a についてはこんな感じです。

import os
import sys
sys.path.insert(0, '../src')

他にもオプションがありますが、詳しく以下からご確認ください。

https://www.sphinx-doc.org/ja/master/man/sphinx-apidoc.html

ファイル構成は以下のようになります。

.
├── docs
│   ├── Makefile
│   ├── _build
│   ├── _static
│   ├── _templates
│   ├── conf.py
│   ├── entry.rst
│   ├── index.rst
│   ├── make.bat
│   ├── mod1.rst
│   └── mod2.rst
└── src
    ├── entry.py
    ├── mod1.py
    └── mod2.py

ドキュメントのビルド

docsに移動してビルドしていきます。

Makefileの確認

以下のような内容になっています。

# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS    ?=
SPHINXBUILD   ?= sphinx-build
SOURCEDIR     = .
BUILDDIR      = _build

# Put it first so that "make" without argument is like "make help".
help:
    @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
    @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

sphinx-buildのラッパとして動作するようです。

単にmakeするとhelpが表示されるようなので、試してみます。

Sphinx v7.2.6
Please use `make target' where target is one of
  html        to make standalone HTML files
  dirhtml     to make HTML files named index.html in directories
  singlehtml  to make a single large HTML file
  pickle      to make pickle files
  json        to make JSON files
  htmlhelp    to make HTML files and an HTML help project
  qthelp      to make HTML files and a qthelp project
  devhelp     to make HTML files and a Devhelp project
  epub        to make an epub
  latex       to make LaTeX files, you can set PAPER=a4 or PAPER=letter
  latexpdf    to make LaTeX and PDF files (default pdflatex)
  latexpdfja  to make LaTeX files and run them through platex/dvipdfmx
  text        to make text files
  man         to make manual pages
  texinfo     to make Texinfo files
  info        to make Texinfo files and run them through makeinfo
  gettext     to make PO message catalogs
  changes     to make an overview of all changed/added/deprecated items
  xml         to make Docutils-native XML files
  pseudoxml   to make pseudoxml-XML files for display purposes
  linkcheck   to check all external links for integrity
  doctest     to run all doctests embedded in the documentation (if enabled)
  coverage    to run coverage check of the documentation (if enabled)
  clean       to remove everything in the build directory

いざビルド

htmlターゲットがあるので、それを実行してみましょう。

make html

ビルドできたようです。

.
├── docs
│   ├── Makefile
│   ├── _build
│   │   ├── doctrees
│   │   │   ├── entry.doctree
│   │   │   ├── environment.pickle
│   │   │   ├── index.doctree
│   │   │   ├── mod1.doctree
│   │   │   └── mod2.doctree
│   │   └── html
│   │       ├── _modules
│   │       │   ├── entry.html
│   │       │   ├── index.html
│   │       │   └── mod1.html
│   │       ├── _sources
│   │       │   ├── entry.rst.txt
│   │       │   ├── index.rst.txt
│   │       │   ├── mod1.rst.txt
│   │       │   └── mod2.rst.txt
│   │       ├── _static
│   │       │   ├── alabaster.css
│   │       │   ├── basic.css
│   │       │   ├── custom.css
│   │       │   ├── doctools.js
│   │       │   ├── documentation_options.js
│   │       │   ├── file.png
│   │       │   ├── language_data.js
│   │       │   ├── minus.png
│   │       │   ├── plus.png
│   │       │   ├── pygments.css
│   │       │   ├── searchtools.js
│   │       │   └── sphinx_highlight.js
│   │       ├── entry.html
│   │       ├── genindex.html
│   │       ├── index.html
│   │       ├── mod1.html
│   │       ├── mod2.html
│   │       ├── objects.inv
│   │       ├── py-modindex.html
│   │       ├── search.html
│   │       └── searchindex.js
│   ├── _static
│   ├── _templates
│   ├── conf.py
│   ├── entry.rst
│   ├── index.rst
│   ├── make.bat
│   ├── mod1.rst
│   └── mod2.rst
└── src
    ├── __pycache__
    │   ├── entry.cpython-312.pyc
    │   ├── mod1.cpython-312.pyc
    │   └── mod2.cpython-312.pyc
    ├── entry.py
    ├── mod1.py
    └── mod2.py

ビルド結果

alabaster entry
alabaster entry

alabaster mod1
alabaster mod1

alabaster mod2
alabaster mod2

とりあえず情報は出揃っているようです。

テーマを変える

少しテーマが味気ないように感じるので、よく見る感じのテーマに変えてみます。

conf.pyを少しいじります。

- html_theme = 'alabaster'
+ html_theme = 'sphinx_rtd_theme'

Makefileのあるディレクトリに移動してもう一度ビルドします。

make html

rtd entry
rtd entry

rtd mod1
rtd mod1

rtd mod2
rtd mod2

いい感じです。

ソースを更新したら

./src/配下のpythonスクリプトを編集した場合は、もう一度 make htmlを実行すればhtmlファイルに変更が反映されます。