nikkie-ftnextの日記

イベントレポートや読書メモを発信

Sphinxでクロスリファレンスを機能させるためには、includeするファイルをrst以外の拡張子にすべきと身をもって学びました

はじめに

頑張れば、何かがあるって、信じてる。nikkieです。
技術同人誌執筆以来使っているドキュメンテーションツールSphinxで、includeするファイルはrst以外の拡張子にするべきということを知りました。

Tips: you should use other extensions for included documents (cf .txt).

目次

要するに

Sphinxにはrefnumrefを使ったクロスリファレンスの仕組み1があります。
セクションの直前にラベルを付けて、それを参照できます(ドキュメントにある例)。

.. _my-reference-label:

Section to cross-reference
--------------------------

This is the text of the section.

It refers to the section itself, see :ref:`my-reference-label`.

これを使ったところ、PDFのビルドに限っては、includeしたファイルの中のラベルを参照することができませんでした。
HTMLでは参照できる2のですが、PDFになると参照できませんprobrefとラベルそのものが出てしまっています)。
出力先によって動きが違う理由に見当が付かず、「悔しくって死にそう」でした。

Issueを漁っていたところ、冒頭で紹介したものに出会い、解消できました。
includeするファイルはrst以外の拡張子にする必要がありました。

サンプルドキュメント

上記Issueに添付されたファイルを例にします。

.
├── Makefile
├── _build
├── conf.py
├── index.rst
├── make.bat
├── section1.rst
├── subsec1.rst
└── subsec2.rst

index.rst

sections1.rstからなるtoctreeです。

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

   section1

section1.rst

subsec1.rst と subsec2.rst をincludeしています。
参照しているラベル probref は subsec1.rst にあります。

Section 1
=========

Labels in included files cannot be referenced.

.. include:: subsec1.rst
.. include:: subsec2.rst

Subsection 3
------------

References also do not work from parent document: :ref:`probref`

subsec1.rst

「Subsection 1」に probref ラベルを付けています。

.. _probref:

Subsection 1
------------

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt
ut labore et dolore magna aliquyam erat, sed diam voluptua.

subsec2.rst

subsec1.rst にあるラベル probref を参照しています。

Subsection 2
------------

References do not work from other included document: :ref:`probref`

環境

PDFビルドには sphinxdoc/sphinx-latexpdf:3.3.1DockerHub)を使いました。4

PDFビルドコマンド

docker run --rm -v $PWD:/docs sphinxdoc/sphinx-latexpdf:3.3.1

作られたPDFではラベルが参照できず、probrefというラベル名が表示されています(図を再掲します)

解決策

probref ラベルを定義したファイル subsec1.rst を subsec1.rst.txt のように拡張子をrst以外に変更すると解決します。

合わせて、section1.rst の include も更新します。

.. 変更がないので省略

.. include:: subsec1.rst.txt
.. include:: subsec2.rst

.. 変更がないので省略

「Subsection 1」とラベルが指すセクションの名前が表示されていますね🙌

解決する理由

前掲のIssueでは、includeされたドキュメントが二度処理されたためと説明されていました。

  1. section1.rst の処理で、subsec1.rst と subsec2.rst を include したドキュメントとなる(probref ラベルが section1に定義される)
  2. Sphinx*.rst という名前のファイルを検索することで、 subsec1.rst もドキュメントとして処理される。これにより probref ラベルが subsec1にも定義される5

同じラベルが2回現れるため、Sphinxは混乱し、最初の画像のようにリファレンスが壊れるとのことでした。

裏付けるように以下のwarningも出ています。

/docs/subsec1.rst:4: WARNING: duplicate label probref, other instance in /docs/section1.rst

include するファイルを subsec1.rst.txt とリネームすると、上記の2で処理されなくなります。
これにより、同じラベルが2回出てこなくなるということですね!

Sphinxのドキュメント6には include ディレクティブについて

単純に一つのファイルを別のファイルに"挿入"する場合、 include ディレクティブを使えます。

という注釈があるのですが、この「一つのファイル」がrst以外のファイルという前提があったことが分かりました。

拡張子を変えたあとの色分け設定(VSCode

nikkieはVSCodeを使っていますが、subsec1.rst.txtを開くと、rstファイルで見られた色分けがされません(文字は白一色です)。

これは、Select Language Mode から reStructuredText を選択することで解決しました。

(追記)選択を頻繁に繰り返していたので .vscode/settings.jsonfiles.associations を設定しました。

    "files.associations": {
        "*.rst.txt": "restructuredtext"
    }

終わりに

rstファイルを分割して include して使っていましたが、これはプラクティスに反していたことを身をもって体験しました。
拡張子を変えたことで、refnumrefでラベルが参照されるようになり、心の平穏を取り戻しました。

Sphinxの動きはまだまだブラックボックスですが、昨年のPyCon JPの以下はチェックしようと思います。


  1. https://www.sphinx-doc.org/ja/master/usage/restructuredtext/roles.html#role-ref
  2. make htmlの実行に使いました。PDFビルドに使うDockerイメージとSphinxのバージョンを合わせる意図でバージョン指定しています。
  3. リポジトリhttps://github.com/sphinx-doc/docker 。DockerでLaTeXビルド環境が立ち上がってとても助かっています!
  4. 元のコメントでは subsec2.rst ですが、ラベルがある subsec1.rst のことを言っているのだと理解しました
  5. https://www.sphinx-doc.org/ja/master/usage/restructuredtext/directives.html#table-of-contents