はじめに
未来ちゃ、お誕生日おめでとうございます!🍵 nikkieです
先日取り上げたsphinx-designの:octicon:
ロール、どのように実装されているのか覗きました。
目次
sphinx-designの:octicon:
ロール
Sphinxで作るHTMLのドキュメントに、ボタンやカード、グリッド、アイコン(など)を追加できるのがsphinx-design1!
最近ではGitHub octiconもサポートしていたことを知りました
この:octicon:
ロールについて実装を見ていきます。
OcticonRole
クラス
実体はOcticonRole
クラスです。
ロール設定箇所
https://github.com/executablebooks/sphinx-design/blob/v0.6.0/sphinx_design/icons.py#L30
app.add_role("octicon", OcticonRole())
https://www.sphinx-doc.org/ja/master/extdev/appapi.html#sphinx.application.Sphinx.add_role
SphinxのSphinxRole
クラスを継承しており、run()
メソッドがポイントです。
https://github.com/executablebooks/sphinx-design/blob/v0.6.0/sphinx_design/icons.py#L124
https://www.sphinx-doc.org/ja/master/development/tutorials/extending_syntax.html#the-role-class
This class extends the SphinxRole class.
The class contains a run method, which is a requirement for every role.
run()メソッドは以下のような記法を受け入れる実装となっており
:octicon:`report` :octicon:`report;1em;sd-text-info`
docutilsのraw
ノードを返します。
https://github.com/executablebooks/sphinx-design/blob/v0.6.0/sphinx_design/icons.py#L140
node = nodes.raw("", nodes.Text(svg), format="html")
このsvg
はget_octicon()
関数の返り値です。
https://github.com/executablebooks/sphinx-design/blob/v0.6.0/sphinx_design/icons.py#L132
get_octicon()
関数
https://github.com/executablebooks/sphinx-design/blob/v0.6.0/sphinx_design/icons.py#L64
svgタグを返す関数です。
GitHub octiconのSVGはJSONファイルに格納されています。
https://github.com/executablebooks/sphinx-design/blob/v0.6.0/sphinx_design/compiled/octicons.json
octiconの名前でSVGのデータを取得します。
データのイメージ(reportを例に)
https://github.com/executablebooks/sphinx-design/blob/v0.6.0/sphinx_design/compiled/octicons.json#L14811-L14872
"report": { "name": "report", "heights": { "16": { "path": "<path> 詳細は https://primer.style/foundations/icons/report-16 参照</path>", }, "24": { "path": "<path> 詳細は https://primer.style/foundations/icons/report-24 参照</path>", } } },
ビルドされたHTML中のsvgタグ
<svg version="1.1" width="1.0em" height="1.0em" class="sd-octicon sd-octicon-report" viewBox="0 0 16 16" aria-hidden="true"><path d="..."></path></svg>
reportには16pxと24pxのデータがありますが、16pxが使われています(viewBox
)。
このあたりの分岐:https://github.com/executablebooks/sphinx-design/blob/v0.6.0/sphinx_design/icons.py#L87-L96
widthとheightを引数から指定できることで、1em
のようにして16pxのデータでも本文のフォントサイズになじませられるのですね。
get_octicon_data()関数
octiconのJSONからSVGデータ全件を取得する関数です。
https://github.com/executablebooks/sphinx-design/blob/v0.6.0/sphinx_design/icons.py#L50
Load all octicon data.
ファイルの読み込みが絡むと遅くなりそうですが、functools.lru_cache
で対処しています!
@lru_cache(1) def get_octicon_data() -> dict[str, Any]:
https://docs.python.org/ja/3/library/functools.html#functools.lru_cache
関数をメモ化用の呼び出し可能オブジェクトでラップし、最近の呼び出し最大 maxsize 回まで保存するするデコレータです。
高価な関数や I/O に束縛されている関数を定期的に同じ引数で呼び出すときに、時間を節約できます。
1回呼んだらそれ以降はキャッシュが効くので速くできるという理解です。
実際octiconロールの実装では、get_octicon()
関数が呼ばれるたびにget_octicon_data()
が呼ばれています。
自分でスクリプトを書くときは最初に読み込んで変数に代入して読み込みは1回だけにするのですが、lru_cache
を使うことで変数を用意しなくても同様のことができるというのは興味深いです。
終わりに
sphinx-designの:octicon:
ロールの実装を以下のように理解しました
- SVGデータは1つのJSONにまとめて保持し、名前で引ける状態(ファイル読み込みにはLRUキャッシュも!)
- SVGデータをもとに、viewBoxやwidth, heightを指定してHTMLのsvgタグを返す
自作Sphinx拡張のfeature request対応の中で、sphinx-designの実装を眺めました。
非常に参考になったのでパクります!