Sphinx + Recommonmarkの環境ではリンクの箇条書きはtoctreeになることを失念してトラブルに見舞われた話

Sphinx + Recommonmarkの環境でMarkdownでドキュメントを書いていたところリンクのリストが消えてしまうトラブルに遭遇しました。設定変更で簡単に解決できましたので紹介します。
2020.03.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。サービスグループの武田です。

みなさんドキュメント書いてますか?その際どのようなツールを使っていますか?今回はSphinxを採用していたプロダクトで(主に筆者の不理解で招いた)トラブルに見舞われ、その解決策の備忘録となります。

発生した問題

SphinxはreStructuredTextというマークアップ言語を採用しています。ただ新しく覚えるモチベーションが湧かないというのと、使い慣れているMarkdownで書きたい!という要求からRecommonmarkを利用してMarkdownで書いていました。そこで次のようなリンクの箇条書きを記載しました。

参考サイト。

- [Classmethod](https://classmethod.jp)
- [Developers.IO](https://dev.classmethod.jp)

そしてsinglehtmllatexpdfでビルドしたら、このリンクがきれいさっぱり消えてしまったのでした(htmlでビルドする分には問題ないように見える)。

原因と対応

まず原因ですが、今回書いたようなリンクの箇条書きはRecommonmarkによってtoctreeとして解釈されます(設定によります、後述)。完全にこの仕様は頭から抜けていたのですが、問題の調査をしている中でドキュメントにある記載を見つけました。

AutoStructify Component — Recommonmark 0.6.0 documentation

toctreeとして認識された結果、singlehtmllatexpdfではリンク先を記載箇所に差し込もうとしていたわけです。ただ外部リンクのため差し込めずリンクそのものが消失しました(内部リンクでも意図しない差し込みが起きて変な構成になります)。

enable_auto_toc_treeという設定項目が提供されており、これを切ることで問題は解決できました。

conf.py

def setup(app):
    app.add_config_value('recommonmark_config', {
            'enable_auto_toc_tree': False,
            }, 'env')
    app.add_transform(AutoStructify)

問題の再現手順

上記の設定で問題は解決できましたので、ここからは再現する手順を記載します。解決策さえわかれば十分という方には蛇足になります。

検証環境

次のような環境で検証しています。

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.15.3
BuildVersion:	19D76

$ pipenv --version
pipenv, version 2018.11.26

$ pipenv run python -V
Python 3.7.6

$ pipenv run python -m pip list | grep -E 'Sphinx|recommonmark'
recommonmark                  0.6.0
Sphinx                        2.4.4

ドキュメントの作成

まずはさくっとドキュメントのひな型を作成しましょう。sphinx-quickstartを実行するといくつか質問をされますが、基本的にはデフォルト設定で十分です。

$ cd /path/to/working

$ cat > Pipfile <<EOS
[source]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
sphinx = "*"
recommonmark = "*"

[requires]
python_version = "3.7"
EOS

$ pipenv install
$ pipenv run sphinx-quickstart

これで環境が作成できました。試しにドキュメントをビルドしてみましょう。

$ pipenv run make html singlehtml
$ open _build/html/index.html _build/singlehtml/index.html

シンプルな画面ですが、問題なく生成できたことが確認できます。

Recommonmarkの設定を追加

続いてSphinxのドキュメントをMarkdownで書くための設定をconf.pyに追加します。

conf.py

@@ -14,6 +14,7 @@
 # import sys
 # sys.path.insert(0, os.path.abspath('.'))

+from recommonmark.transform import AutoStructify

 # -- Project information -----------------------------------------------------

@@ -28,8 +29,18 @@ author = 'test'
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
 extensions = [
+    'recommonmark'
 ]

+source_suffix = {
+    '.rst': 'restructuredtext',
+    '.txt': 'restructuredtext',
+    '.md': 'markdown',
+}
+
+def setup(app):
+    app.add_transform(AutoStructify)
+
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']

設定はこれだけです。実際にMarkdownのファイルを追加してみましょう。

hello.md

# hello

これは**Markdown**で書いています。

```eval_rst
- `Classmethod <https://classmethod.jp>`_
```

- [Developers.IO](https://dev.classmethod.jp)

toctreeに追加します。

index.rst

@@ -10,6 +10,7 @@ Welcome to test's documentation!
    :maxdepth: 2
    :caption: Contents:

+   hello


 Indices and tables

あらためてドキュメントをビルドします。

$ pipenv run make clean html singlehtml
$ open _build/html/hello.html _build/singlehtml/index.html

こちらがhtmlのドキュメント。リンクは健在です。

こちらがsinglehtmlのドキュメント。リンクが消失しました……。

なお、実際には他の箇所にも影響は出ているのですが、テーマなどに依存するため今回の問題に焦点を当てています。

auto_toc_treeの設定を追加して解決

実は今回の問題は、解決策が2個あります(他にもあったら教えてください)。1個目は上述したenable_auto_toc_treeを設定する方法。2個目はauto_toc_tree_sectionを設定する方法です。

enable_auto_toc_treeはtoctreeの生成を完全にOFFにできます。

conf.py

def setup(app):
    app.add_config_value('recommonmark_config', {
            'enable_auto_toc_tree': False,
            }, 'env')
    app.add_transform(AutoStructify)

もうひとつの方法であるauto_toc_tree_sectionはtoctreeを生成するセクションを限定できます。

conf.py

def setup(app):
    app.add_config_value('recommonmark_config', {
            'auto_toc_tree_section': 'Welcome',
            }, 'env')
    app.add_transform(AutoStructify)

いずれかの設定をしてドキュメントをビルドします。

$ pipenv run make clean html singlehtml
$ open _build/html/hello.html _build/singlehtml/index.html

こちらがhtmlのドキュメント。リンクは健在です。

こちらがsinglehtmlのドキュメント。リンクが復活しました!

まとめ

始めはビルド環境の問題なのか、Sphinxのバグなのか、など問題の切り分け自体に難航しました。一時的にはeval_rstで解消できたのですが、根本解決をするためにドキュメントを眺めたりしていたところ今回の解決に至りました。ドキュメントはちゃんと読もう!(戒め)