Sphinxのtoctreeにおける順序操作
誰向けか
- Sphinxでドキュメンテーションなどを行っている
-
toctree
で全部のドキュメントをツリーにしている - その
toctree
でglob
を使っている - このときに、Sphinx内部処理とは別のルールでソートさせたい
何が困っているのか
自分が個人で管理しているサイトはSphinx製で、今時点では ablog
というブログ用のSphinx拡張を利用してブログを書いています。
ablog単体でできないことがある
ablog
ではブログ設定で指定した配下のreSTを記事として扱うのですが、標準的な使い方の案内としてtoctree
を用いないものとなっており、以下のようなちょっとした弊害が発生します。
-
next
,prev
といったlink
タグ用のリレーションが生成されない [1] - ソースreSTが独立してしまっており、
:doc:
ロールなどを使ったリンクが生成できない
これらの機能は、Sphinx内で各ドキュメントtoctreeの観測範囲に収まっていることが前提になっています。そのため、:glob:
フラグをオンにしたtoctree
ディレクティブを用意してあげることで解消できます。
.. toctree::
:glob:
blog/**/*
このドキュメントをマスタードキュメント経由でtoctreeに収めることができれば、全記事のリレーションが貼られるため、next/prevの用意や記事間リンクが機能するようになります。
toctreeの挙動で困ったこと
しかし、何も考えずにglobで全記事を収めると、「Sphinxとしての記事の前後関係」と「ablogとしての記事の前後関係」の不一致を起こします。
具体的には...
.. post:: 2021-01-11
.. post:: 2021-01-01
.. post:: 2021-01-10
このようなソースがあった場合、ablogとしては日付をもとに前後関係を判断するためtitle-a
->title-c
->title-b
の通りにナビゲート情報が用意されるのに対して、Sphinxのtoctree with globでは内部のファイル探索に基づくため title-a
->title-b
->title-c
の順になってしまいます。[2]
このままではあまりよろしくないので、ちょっと対策を考えてました。
どう解決するか
幸いSphinxにはイベントハンドラを差し込める受け口が用意されています。
そのため、「ソース読み込み完了」と「HTML出力」までの間にtoctreeの中身をいじることでどうにかしてみることにします。
00. 前提として
以下の前提で話を進めます
-
index.rst
には、blog.rst
へのtoctree参照がある -
blog.rst
には、フォルダは以下すべてを含むblog/**/*
へのtoctree参照がある
0. コード
blog_path = "blog"
def resolve_posts_order(app, env):
from sphinx.addnodes import toctree
blog_path = app.config.blog_path
blog_top_doctree = env.get_doctree(blog_path)
toctree_node: toctree = list(blog_top_doctree.traverse(toctree))[0]
entries = []
for _, docname in toctree_node["entries"]:
postinfo = env.ablog_posts[docname][0]
entries.append((_, docname, postinfo))
entries = sorted(entries, reverse=True, key=lambda e: e[2]['date'])
toctree_node["entries"] = [(e[0], e[1]) for e in entries]
toctree_node["includefiles"] = [e[1] for e in entries]
env.toctree_includes[blog_path] = toctree_node["includefiles"]
def setup(app):
app.connect("env-updated", resolve_posts_order)
1. どのタイミングでならうまくいくかを探す
書き換えのためには、ブログ記事のメタデータが全て出揃っている必要があります。
ablogの挙動は、各ブログ記事をdoctreeとして読み込んだ時点で、メタデータを抽出して中央管理している記事情報に引き渡す仕様です。
そのため、blog.rst
の読み込みだけでなく、全てのソースを一通り読み込み終えた状態が理想です。
Sphinxのコアイベント的にはenv-updated
,env-get-updated
,env-check-consistency
あたりならHTML書き出し前なので無難そうです。
ここでは、env-updated
のタイミングで処理をすることにします。
2. blog.rst内のtoctree順序を書き換える
前述の通り、この時点でのtoctree
内の記事リストは、glob処理に基づいて決定されています。
したがって(おそらく)ファイルの文字列順でリストが生成されています。
これを、ブログ記事の日付に基づいた順序に変更してやります。
※resolve_posts_order
の中身の殆どの処理がそれ
- 処理対象のdoctreeをピックアップ
- その中からtoctreeノードをピックアップ
- tocreeノード内にある記事情報をもとに全記事情報から日付情報をピックアップ
- 日付情報をもとに記事一覧をソートして、toctreeに書き戻し
といったような流れになっています。
内部で大量に呼んでいるget_doctree
ファイルI/Oがあるので、ある程度記事数が増えるとちょっと処理時間に影響があるかもしれません。
リンク
- Sphinx
- ablog
Discussion