読者です 読者をやめる 読者になる 読者になる

Re: numfig の闇と向き合う

numfig の闇を垣間見るのにも飽きてきたので、これまで見た numfig 系拡張の問題をカバーすべく
自分でも numfig 系拡張を書いてみた。
new_numfig.py

ざっくり動かしたところ、うまく動いているようにみえる。

既存の fignum 系拡張との違いはこんな感じ。

  • 表の自動採番にも対応してみた
  • :num: ロールを :ref:num: ロールにリネームした(:ref: の拡張っぽく見えるでしょ?)。
  • :ref:num: ロールは HTML 形式でもちゃんと動く
  • 複数の HTML 間でも番号は重複せず、ユニークな番号が割り振られる
  • toctree に :numbered: オプションを付けると 1-1, 1-2 のような枝番を割り振っている
  • 拡張性を持っているので、あとで対応モジュールを増やせるはず

実装してみてわかったこと

ファイルをまたがった採番とその参照

ファイルをまたがって番号が重複しないようにするのは非常に難しい。
Sphinx の処理が次の順序で行われているので、きれいなやり方で実装することができない。

  1. reST の解釈
  2. doctree-read イベント
  3. セクション番号の割り振り (sphinx.environment.BuildEnvironment#assign_section_numbers())
  4. doctree の pickle 化
  5. ここからは rst ファイルごとの処理 (HTML ファイル書き出し)
    1. doctree の復元 (unpickle 化)
    2. doctree-resolved イベント
    3. ライター処理 (visitor メソッド呼び出し)

自動採番で枝番を使おうとすると採番のタイミングはセクション番号の割り振りより後になるのだが、
それより後ろのフックポイントは doctree-resolved イベントと visitor メソッドの二箇所しかない。
だが、どちらの処理も rst ファイル単位で処理が行われ、
他の rst ファイルの doctree はメモリ上に存在しないため、
:ref:num: ロールで図表番号を取得する際に何番が割り振られているのか知るすべがないのだ。

本来であれば doctree-read イベントで自動採番をして、図表番号を含んだ状態で pickle 化するのが望ましいのだが、
doctree-read イベントは

Emitted when a doctree has been parsed and read by the environment, and is
about to be pickled. The *doctree* can be modified in-place.

という説明にも関わらず、pickle 化のもっと前のところで呼び出されてしまう。


new_numfig.py では、他の rst ファイルを読みだして、自動採番処理を再実行することで、
番号が割り振られた doctree を再現して :ref:num: の処理を行っている。
効率はかなり悪いので、あまり好ましいやり方ではない。

ドキュメントの更新への対応

いまの new_numfig.py は部分的にドキュメントが更新された場合に、
番号がずれる可能性を持っている。

先頭に図を追加すると、以降の図表番号はひとつずつずれることになるのだが、
その依存関係を構築していないため、他のファイルは make clean を実行するまで更新が行われない。

また、末尾のファイルを更新した場合でも、それまでに採番した番号を記憶していないので
新たに 1から割り振られてしまう。


前者は BuildEnvironment#check_dependents() のように番号変更の影響を伝えることで、
後者は採番した情報を pickle 化して保存することで対応できると思われる。

今回は実験用の実装なので、make clean してしのいで欲しい。

LaTeX と HTML で Sphinx の動きが違う

new_numfig.py ではオリジナルの numfig.py と同様に、
LaTeX への出力では自動採番は LaTeX にまかせているのですが、
LaTeX と HTML で出力が違うところで異なる番号が割り振られてしまった。

いくつか例を挙げると

  • graphviz のキャプションが LaTeX に出力されない (ので番号も割り振られない) 訂正 (7/31 22:30): キャプションが何故か図の上に出ていました。
  • :name: オプションが LaTeX でラベルにならないことがある

のような違いがいくつか見受けられた。

この辺りはこつこつ Sphinx を直していくしかない模様。

まとめ

  • new_numfig.py を実装して、既存の numfig 系実装の問題を改善してみた
  • Sphinx の拡張インターフェースでは numfig の実現に届かないことが分かった
  • また Sphinx にバグを見つけた

実装に必要な情報が揃ってきたと思われるので、numfig を真に必要としている人は頑張っていただきたい。