aptpod Advent Calendar 2024 の12月9日の記事です。
本日は、テクニカルライターの篠崎が担当します。
当社では今年、ドキュメントサイトをリニューアルし、複数のプロダクトのドキュメントを1つのウェブサイトに統合しました。
これにより、読み手は全体を俯瞰しながら各製品の詳細に読み進めることができるようになりました。 また、私のような制作側にとっては、ページ間のリンクを張りやすくなりました。内容の重複も減らすことができ、更新作業もしやすくなりました。
製品ドキュメントの作成にはSphinxを使っています。これまでプロダクトごとに別々だったSphinxデータは、これからは1つのプロジェクトとして管理するようにしました。
ただし、いままで別のプロジェクトだったものを1つにまとめるには、Sphinxデータのフォルダーをマージするだけでなく、いろいろな調整が必要でした。
例えば、複数のプロダクトを1つのドキュメントで扱うことになったため、ドキュメント内には複数のプロダクトのバージョン番号が登場するようになりました。
通常、Sphinxでは説明対象プロダクトのバージョンを設定ファイル内に書くことで、それを文中に展開して使用することができますが、複数のプロダクトを扱う場合には一工夫が必要でした。 ここではそれについて説明します。
ドキュメント内で展開できる変数(substitution)
Sphinxでは、設定ファイル conf.py
内で version
という変数を定義しておくことにより、本文のreStructuredText原稿で |version|
と書いた部分にその値を展開することができます。
後日バージョンが変更になったときは、設定ファイル内の version
定義を変更するだけで文中に反映することができます。
conf.py
version='1.0.0'
reStructuredText原稿
本サイトでは、MyApp v\ |version| について説明します。
(マークアップとしては |version|
の前後にスペースが必要です。ただし MyApp v
の後にバックスラッシュを入れ、その直後のスペースを無視するようにしています。)
HTMLビルド結果
この |version|
のような |...|
を使った一種の変数展開はSphinxではsubstitutionと呼ばれています。
conf.py
内で使用される変数のうち、いくつかの変数はデフォルトでsubstitutionとして使用可能になっています。
最初に書いたとおり、これからは複数のプロダクトを1つのドキュメントで扱うことになったので、「各プロダクトのバージョンを全部substitutionとして扱えるようにしたい」と思いました。そのために、この version
とは別のsubstitutionを独自に設定することにしました。
substitutionは、reStructuredText上で以下のような書式で定義することができます。
reStructuredText原稿
.. |my-app1-version| replace:: 1.2.3 .. |my-app2-version| replace:: 4.5.6
これにより |my-app1-version|
が 1.2.3
という文字列に、 |my-app2-version|
が 4.5.6
という文字列に置換されます。
文中で以下のように書くと展開されます。
reStructuredText原稿
本サイトは以下のバージョンについて説明しています。 * MyApp1 v\ |my-app1-version| * MyApp2 v\ |my-app2-version|
HTMLビルド結果
ですが、 .. |my-app1-version| replace:: 1.2.3
のような定義はreStructuredTextファイルごとに書かなければなりません。複数のページからなるドキュメントでは大変です。
これを解決するのが、 conf.py
の rst_prolog
(または rst_epilog
)です。
rst_prolog
変数の内容は、ビルド時にすべてのreStructuredTextファイルの先頭に付加されます。
そのため、 conf.py
を以下のようにすればよいわけです。
conf.py
rst_prolog=''' .. |my-app1-version| replace:: 1.2.3 .. |my-app2-version| replace:: 4.5.6 '''
これで、ひとつひとつのreStructuredTextファイルで定義したのと同じ効果を得られます。 どのファイルでもこれらのsubstitutionを使えるようになりました。
外部へのリンクを作成するsphinx.ext.extlinks
さらに弊社製品では、ドキュメントにはAPIリファレンスへのリンクが含まれます。
どうしても長いURLを原稿内に書くことになります(例えばこのような形式のURL: https://www.example.com/app1-reference/1.2.3/get-users
)。
長いURLを繰り返し記載したいときに便利なのが、sphinx.ext.extlinksです。
sphinx.ext.extlinksは機能拡張ですが、Sphinxに組み込まれているため新たにインストールする必要はありません。使用するには、 conf.py
で extensions
リストに入れるだけでOKです。
conf.py
# すでにextensionsの定義がある場合は適宜修正してください extensions = ['sphinx.ext.extlinks']
そのうえで、以下のように、リンクのテンプレートを定義します。
conf.py
extlinks = { 'app1-api': ('https://www.example.com/app1-reference/1.2.3/%s', None), }
このようにすると、原稿内で以下のような表記ができるようになります。
reStructuredText原稿
:app1-api:`ユーザー一覧取得APIリファレンス <get-users>`
HTMLをビルドすると以下のように展開されます。
HTMLビルド結果(一部省略)
<a href="https://www.example.com/app1-reference/1.2.3/get-users" >ユーザー一覧取得APIリファレンス</a>
上のreStructuredText原稿で <>
の中に入っていたURL断片 get-users
が https://www.example.com/app1-reference/1.2.3/
の後ろ(extlinks定義の %s
のところ)に展開されています。これにより、長いURLを毎回書く必要がなくなり、原稿がシンプルになります。
当社の場合、APIリファレンスはバージョンごとに用意しているので、リンクには特定のバージョン番号を含める必要があります。
説明対象プロダクトの情報をまとめて定義する
バージョン文字列をsubstitutionsやextlinksなどの複数の形式で使うのであれば、重複のないように1か所で定義したくなります。
ここで「設定ファイル conf.py
は実行されるPythonコードでもある」ということが活きてきます。以下のようなことができます。
conf.py
# 2つの説明対象プロダクトの情報をまとめてdictionaryとして定義 app_versions = { 'my-app1': { 'name': 'MyApp1', 'version': '1.2.3', }, 'my-app2': { 'name': 'MyApp2', 'version': '4.5.6', }, } # substitutionを定義 # app_versionsをループして、 # `.. |my-app1| replace:: MyApp1 - v1.2.3` の形式でrst_prologを作成 rst_prolog='' for app_id, app_info in app_versions.items(): rst_prolog += f'.. |{app_id}| replace:: {app_info["name"]} - v{app_info["version"]}\n' # extlinksを定義 # app_versionsをループして、特定バージョン番号の入った外部リンクの短縮表記を定義する extensions =['sphinx.ext.extlinks'] extlinks = {} for app_id, app_info in app_versions.items(): extlinks[f'{app_id}-api'] = (f'https://www.example.com/{app_id}-reference/{app_info["version"]}/%s', None)
ループの中が少し読みづらいですが、結果は以下のようになるはずです。
rst_prolog = ''' .. |my-app1| replace:: MyApp1 - v1.2.3 .. |my-app2| replace:: MyApp2 - v4.5.6 ''' extlinks = { 'my-app1-api': ('https://www.example.com/my-app1-reference/1.2.3/%s', None), 'my-app2-api': ('https://www.example.com/my-app2-reference/4.5.6/%s', None), }
これにより、以下のような原稿を書くことができるようになります。
reStructuredText原稿
本サイトは以下のバージョンについて説明しています。 * |my-app1| * |my-app2| 以下のAPIを使用できます: :my-app1-api:`ユーザー一覧取得APIリファレンス <get-users>` :my-app2-api:`プロジェクト一覧取得APIリファレンス <get-projects>`
HTMLビルド結果
ループを使ってsubstitutionと外部リンクをいっぺんに定義することができました。
これで、説明対象のプロダクトが増えても conf.py
の app_versions
に追加していくことで管理できます。
まとめ
Sphinxの設定ファイル conf.py
は、これ自体がPythonで書かれたプログラムなので、設定ファイル内でさまざまな操作をプログラム的に行うことができます。この柔軟性は大変便利です。
当社では日ごろ多くのドキュメントをSphinxで作成していますが、この柔軟性にいつも助けられています。