📗

AIアシスタントとPythonパッケージのマニュアルを作る

に公開

1. 背景

GitHub CopilotとSphinxを使って、ドキュメント(マニュアル)作成を行なった。試行錯誤しながらのため、詳細な記録とはできなかったが、自分にとって頻度の高い作業ではないので、未来の自分に向けて+誰かの参考になれば、の想いで記録を残す。

1-1. 目的

なるべく楽をして、作成したプログラムの取り扱い説明書を作りたい。
浮いた時間で、人間はプログラムから直接は読み取れない、発展的な使用方法の文書作成に集中する。

1-2. 目標

すでに出来上がっている(GitHubのpublicなリポジトリで公開している)、Pythonライブラリ SMiPolyのマニュアルを作る。

1-3. 想定ワークフロー

  1. プログラム中の関数の説明は、GitHub Copilotを使ってdocstringとして生成させ、後で目視で修正する。
  2. モジュール(個々の.pyファイル)の概要も、GitHub Copilotを使ってまとめさせる
  3. 目視で内容の確認と修正
  4. これらをSphinxを使ってドキュメント化する

2. 実施結果と対応

2-1. GitHub Copiloit

2-1-1. GitHub Copiloitで関数の説明案を作る

VScodeにGitHub copilot、GitHub copilot Chatの機能拡張をインストール。ここら辺はwebにも色々情報があるので略。

対象の関数を選択、もしくは関数のブロック内にカーソルを置き、ctrl + I でインラインチャットの入力窓を出し、

/docs

と入力すると、docstringが自動生成されるので、内容を確認し、そのままでよければaccept。
としようとしたのだが、docstringに合わせて、コードの訂正案を提示してくる場合があった。
都度キャンセルするのも、それなりに手間がかかる。また、今回は正常に動作しているプログラムを対象としているため、この時点では修正を入れたくない。提案は有り難いが、「うっかり間違って受け入れてしまい、動作しなくなる」のは避けたい。
さらに、なぜかdocstringの様式が異なったり、コードの修正提案のみで、docstringを生成してくれないケースもあった。VScodeでのインデント長と異なるインデントになっているブロックでは、問題が発生する場合が多いように思えた。
そこで、全体として、対象とする関数のブロック内にカーソルを置いた状態で、以下のように明示的にプロンプトで指示をして、docstringを生成するようにした。

describe docstring in google style. do not correct the code

これで大体は問題なく、Google styleでのdocstringを生成してくれた。

2-1-2. モジュールの説明文

VScodeで対象とする.pyファイルを開くと、上部にGitHub Copilotのキャラクター(かな?)のマークがある。ここをクリックして、 "open chat"でworkspaceの横にチャット用のwindowを展開する。
この下部の入力窓(Ask copilotとある部分)に、

@workspace/explain

と入力すると、モジュールの説明文を生成してくれる。モジュール中で使用している関数の説明なども出るが、これはdocstringで生成した内容と重複する場合も多かったため、主として冒頭の概要部分を利用した。
ちなみに、下の方のキャクターマークをクリックすると、copilotの使用状況を確認できた。

2-1-3. 文章の修正

自動発生させた文章を、目視で修正した。主な修正理由は、

  1. 表記ゆれへの対応
  2. 自動発生した内容がプログラムの動作に関する記述が中心の場合、想定ユーザーの分野(今回で言えば化学)の用語や意味合いに置き換える

の二点だった。
項目1については、例えば、Molオブジェクトである引数の型の表現が、rdkit.Chem.Molだったり、単にobjectだったり、といった不統一が見られた。また、同じ引数を受け取るが、異なる処理をおこなう二つの関数で、それぞれの引数の説明が若干異なる場合もあり、統一を図った。
項目2については、プログラムの動作内容ではなく、ユーザーに、その操作の化学的意味合いを伝える必要があっての修正。例えば「アスタリスクをプレースホルダーに置き換え、反応を適用し、結果を元の形式に戻すことで、SMILES 文字列を変換する。」といった”プログラムの内容解説”を、「1,2-付加した構造の高分子繰り返し構造を、1,4-付加体に修正する」という表現に変えた。
一方で、"monomer"、"polymer"や”除外する構造”といった用語は、自動発生の文章でも比較的正しく使えていた。公開しているリポジトリのmarkdownを参照したりしているのかもしれないが、その能力の高さには感動した。

ここまでで実働約3日。この3か月程度、隙間時間を使って、手で関数の説明を書いており、まだ仕掛りだったのだが、その内容はほぼ廃棄して、GitHub Copilotが生成してくれたものベースの方を採用できる満足度。生産性30倍以上ですねえ。もっとも、事前に「手作業によるマニュアル作成」を実体験したことにより、「書きたいものの方向性」を自分で認識することができた点は大きく、全く無駄、ということでもなかった。

2-2. Sphinx

この工程で結構課題が発生した。生成したhtmlにdocstirngの内容が全く反映しない、というものだったが、結論として、index.rstのtoctreeの書き方がよくなかった、ということだった。
webにもよい記事が多々あり、参照させていただいたが、バージョンの違いなどの影響もあると考え、公式を確認しながら、という進め方を心掛けた。
使用環境はwindows 10, Sphinx ver8.2.3, venvで仮想環境を構築。

とりあえず、以下の簡単な関数を作り、

# function "test_func.py"
"""
summary test
"""

def add(x):
    """
    add2
    """
    return x+2

test01配下に空のdocsとsrc以下を作り、コマンドプロンプトでcdによりtest01移動してから、

sphinx-quickstart docs

により以下のようなシンプルな配置にして試験運用した。"__init__.pyの中身は空。ディレクトリの内容の解説はcopilotによる。

tewt01/
│── docs/                  # Sphinxのドキュメントフォルダ
│   │── conf.py             # Sphinxの設定ファイル
│   │── index.rst           # メインのドキュメントファイル
│   │── make.bat
│   │── Makefile
│   │── _build/             # ビルド済みドキュメントの出力先
│   │── _static/            # 静的リソース(CSS, JS など)
│   │── _templates/         # カスタムテンプレート
│   └── my_module.rst       # 各モジュールのドキュメント
└── src/                    # Pythonのソースコード
    └── test_mod         
        │── __init__.py
        └── test_func.py    # 解析対象のPythonコード      

2-2-1. conf.pyの修正

  • sphinx-quickstartで生成したcof.py中に、多くの参照記事では記載があるpath setupのブロックが存在しなかった。従って、コメントの解除ではなく、モジュールへのpathの指定を追記した。公式ではPathlibを使って絶対パスを発生させ、sys.pathに渡しているので、それに従った
  • 拡張機能"sphinx_rtd_theme"と "myst_parser"は、General configurationsのextentionsのリストに追記するのであれば、予めpip installしておく必要あり
$pip install myst-parser
$pip install sphinx-rtd-theme

2-2-2. sphinx-apidoc

解析対象のモジュールの.rstファイルを作成する。

sphinx-apidoc -f -e -o ./docs ./src

今回のケースでは、

tewt01/
│── docs/        
│   │── conf.py 
│   │── index.rst        
│   │── make.bat
│   │── Makefile
│   │── modules.rst         #生成
│   │── test_mod.rst        #生成
│   │── test_mod.test_func.rst #生成
│   │── _build/        
│   │── _static/       
│   │── _templates/      
│   └── my_module.rst    
└── src/  
    └── test_mod         
        │── __init__.py
        └── test_func.py      

標準ライブラリ以外のライブラリ(NumPyやpandasも)をimportしている場合には、仮想環境にインストールしておく必要がある。conf.pyにautodoc_mock_imports = ['external_library'] を追記して回避する方法もあるとのことだが、試していない。

生成した.rstファイルを見ながら、index.rstのtoctreeに追記した。今回は下の例の終わり三行。

.. toctree::
   :maxdepth: 2
   :caption: Contents:

   modules   #ここを追記
   test_mod  #ここを追記
   test_mod.test_func #ここを追記

toctreeを記述しないと、次の項目で実施する

$make html

で、生成するhtmlにdocstringが反映されないなど、不具合が出る。今回、ここが一番の「ハマってしまった」ポイントだった。
そもそものdocstringの記述法に問題があるのか、Sphinxの使い方で問題を発生させてしまっているのかの切り分けは、2-2-4項の方法でおこなった。

2-2-3. make htmlの操作

make.batが存在するディレクトリまでcdで移動して実行する必要あり。他の階層では、「'make' は、内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチ ファイルとして認識されていません。」と警告され、実行できない。

$make html

で、doc_buil_htmlに該当するhtmlファイルが生成する。

なお、

make clean

で_buildより下の階層が消去され、初期化できるので、再作成の際に利用した。

2-2-4. docstringの確認

test01\srv\test_modに移動し、

python -c "import test_func; print(test_func.__doc__)"

python -c "import test_func; print(test_func.add.__doc__)"

でdocstringを取り出すことはできたので、docstringの記述がおかしい、というのではなさそうと問題を切り分けた。

2-3. 本番: SMiPolyのdocument作成

GitHubリポジトリにあるものと同じ構成のsrc以下を試験運用のsrcと置き換え(05/06/2025現在、docstringを含むものはdevelopブランチにあるもの)。用いた仮想環境には、Sphinxおよびその機能拡張の他、SMiPolyの依存パッケージであるnumpy、pandas、rdkitをinstallしておいた。
モジュールごとにdocumentを作りたかったので、shinx-apidocで-eのオプションを付けた。オプションの内容はこちら参照。
6種類の.rstファイル、modules.rst、smipoly.rst、smipoly.smip.funclib.rst、smipoly.smip.monc.rst、smipoly.smip.polg.rst、smipoly.smip.rstが生成した。文書の内容から、index.rstのtoctreeにはこの中の3種と、モジュールの説明を記載したmarkdownファイルを、ファイル名の拡張子以外の部分で記述した。なお、該当のmarkdownファイル本体は、他の.rstファイルと同じ階層、ディレクトリdocs内に配置した。

   about SMiPoly
   Installing SMiPoly
   smipoly.smip.monc
   smipoly.smip.polg
   smipoly.smip.funclib

を記載。

その後、make htmlを走らせ、docstringを(一応)反映したhtmlは生成するが、warningが出た部分、表記が異常な部分は、個別個別に修正の対応をした。修正内容は、主としてインデントの調整、および各docstringの末尾に空白行を設けたこと。

  • summaryとArgsの間など、セクションの間には空白行が一行必要
  • Argsのある引数の説明が複数行にわたる際は、二行目以降はインデント下げ
  • Noteセクションの箇条書きが二行になるときは、二行目はインデントではなく、スペースで一行目の文字始まりとそろえた

など。手動での修正時に誤って削除してしまった空白行も含まれている可能性はあるため、次回以降注意したい。

2-4. その他気づき

  • ページトップの文書名がプロジェクト名+documentationなので、変更したい時は、index.rstの該当部分を変更する
  • Markdown記法でのurlリンクは、htmlに変換されず、文字列になってしまった。Markdownファイルにhtml記法で記載しておけば、どちらの文書でもリンクとして生きた形になる(表などでもありがちな事象だけれども、ちょっと面倒)。
  • 関数内関数のdocstringは抽出してくれなかった。やり方があるのかもしれないが、現在未解決。

3. 今後

作製した文書をGitHub Pagesで、マニュアルとして公開予定

Discussion