2013年2月16日

Sphinx と LiveReload を組み合わせる

こちらの記事の Python 版です。

Sphinxの更新をguard-livereloadで検知してブラウザを自動リロードする

sphinxでドキュメントを書く際に生じる「文章の記述 => ビルド => ブラウザでの確認」 という一連のサイクルを人力でやるのは効率が悪い。 いろいろな省力化対策が考えられるが、ここでは guard-livereloadを使って、 文章のビルドとブラウザのリロードを自動化する方法を説明する。

環境構築

livereload の仕組みを Python で実装したパッケージがありますので、これを使います。 Tornado をベースにしていますので、なんとなく相性が良さそうですね。

pyvenv というディレクトリを作成し、そこに Sphinx 用の環境を作成します。

$ mkdir $HOME/pyvenv
$ virtualenv --distribute $HOME/pyvenv/sphinx
$ source $HOME/pyvenv/sphinx/bin/activate

次に、 pip を使って Sphinx と livereload をインストールします。

$ pip install Sphinx
$ pip install livereload

livereload というスクリプトが利用可能になっていれば環境構築は完了です。

$ livereload --help
Python LiveReload

Usage:
    livereload [-p <port>|--port=<port>] [-b|--browser] [<directory>]

Options:
    -h --help                       show this screen
    -p <port> --port=<port>         specify a server port, default is 35729
    -b --browser                    open browser when start server

Guardfile を用意する

LiveReload のタスク定義ファイル - Guardfile - を用意します。 make における Makefile、Maven における pom.xml みたいなものです。 Python で関数を定義し、監視しておくファイルに対してタスクを登録するだけです。

Guardfile

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import logging
from livereload.task import Task


def sphinx():
    from subprocess import Popen, PIPE
    from os import path
    builddir = "_build"
    argv = [
        "sphinx-build", "-q",
        "-b", "html",
        "-d", path.join(builddir, "doctrees"),
        ".", path.join(builddir, "html")
    ]
    logging.debug("Start to build into %s", builddir)
    p = Popen(argv, stdin=PIPE, stdout=PIPE, stderr=PIPE)
    # Wait reloading until build process finished.
    stdout, stderr = p.communicate()
    if stderr:
        logging.error(stderr)
    logging.debug("Finish to build: %s", stdout)


Task.add('*.rst', sphinx)

カレントディレクトリにある拡張子が rst のファイルに変更があった場合に、 sphinx() を呼び出すようにしています。 ファイルの配置ルールが異なる場合には適宜修正してください。

実行してみる

8000番ポートで待ち受けるようにして、ブラウザで http://localhost:8000/_build/html/ にアクセスすると、コンソールには次のようなログが出力されます。

$ livereload -p 8000
Serving path . on 127.0.0.1:8000
[I 130216 21:17:23 web:1462] 304 GET /_build/html/ (::1) 78.80ms
[I 130216 21:17:23 web:1462] 304 GET /_build/html/_static/haiku.css (::1) 1.81ms
[I 130216 21:17:23 web:1462] 304 GET /_build/html/_static/pygments.css (::1) 0.84ms
[W 130216 21:17:23 web:1462] 404 GET /_build/html/_static/print.css (::1) 1.08ms
[I 130216 21:17:23 web:1462] 304 GET /_build/html/_static/jquery.js (::1) 2.65ms
[I 130216 21:17:23 web:1462] 304 GET /_build/html/_static/underscore.js (::1) 19.79ms
[I 130216 21:17:23 web:1462] 304 GET /_build/html/_static/doctools.js (::1) 1.42ms
[W 130216 21:17:23 web:1462] 404 GET /_build/html/_static/theme_extras.js (::1) 0.42ms
[I 130216 21:17:23 web:1462] 304 GET /livereload.js (::1) 21.14ms
[I 130216 21:17:24 web:1462] 304 GET /_build/html/_static/basic.css (::1) 16.90ms
[I 130216 21:17:24 web:1462] 304 GET /_build/html/_static/bullet_orange.png (::1) 0.88ms
[I 130216 21:17:24 web:1462] 304 GET /_build/html/_static/bg-page.png (::1) 1.36ms
[I 130216 21:17:24 server:98] Browser Connected: http://localhost:8000/_build/html/
[I 130216 21:17:24 server:102] Reading Guardfile
[I 130216 21:17:24 task:34] Add task: *.rst
[I 130216 21:17:24 server:109] Start watching changes
[W 130216 21:17:24 web:1462] 404 GET /favicon.ico (::1) 0.77ms

index.rst (.rst ならなんでも良い) を更新してみると、 コンソールには次のようなログが追加されます。

[I 130216 21:19:56 task:66] file changed: index.rst
[E 130216 21:19:59 Guardfile:23] WARNING: html_static_path entry '/Users/shigeru/projects/python-clitool-template/doc/_static' does not exist

[I 130216 21:19:59 server:60] Reload 1 waiters
[I 130216 21:19:59 web:1462] 200 GET /_build/html/ (::1) 1.53ms
[I 130216 21:19:59 web:1462] 304 GET /_build/html/_static/haiku.css (::1) 1.59ms
[I 130216 21:19:59 web:1462] 304 GET /_build/html/_static/underscore.js (::1) 0.80ms
[I 130216 21:19:59 web:1462] 304 GET /_build/html/_static/jquery.js (::1) 1.90ms
[W 130216 21:19:59 web:1462] 404 GET /_build/html/_static/theme_extras.js (::1) 0.35ms
[W 130216 21:19:59 web:1462] 404 GET /_build/html/_static/print.css (::1) 0.32ms
[I 130216 21:19:59 web:1462] 304 GET /_build/html/_static/pygments.css (::1) 0.48ms
[I 130216 21:19:59 web:1462] 304 GET /_build/html/_static/doctools.js (::1) 1.67ms
[I 130216 21:19:59 web:1462] 304 GET /livereload.js (::1) 4.20ms
[I 130216 21:19:59 web:1462] 304 GET /_build/html/_static/basic.css (::1) 2.28ms
[I 130216 21:19:59 web:1462] 304 GET /_build/html/_static/bullet_orange.png (::1) 0.91ms
[I 130216 21:19:59 web:1462] 304 GET /_build/html/_static/bg-page.png (::1) 1.28ms
[W 130216 21:19:59 web:1462] 404 GET /favicon.ico (::1) 0.64ms
[I 130216 21:19:59 server:98] Browser Connected: http://localhost:8000/_build/html/

ブラウザの方は勝手に更新されていますので、ドキュメントを書くことだけに専念できますね。

終わりに

Sphinx と LiveReload を組み合わせてみました。 Python だけで環境を構築できますので、導入の敷居は低いのではないかと思います。 また、 make html と書かなくて良いので、Makefile と make.bat に関することや make って何?という説明をスキップでき、これから書き始める人に説明する手間が省けることを期待しています。 (使いだしてみると色々知ってる方が便利ですが、最初に覚えることが少ないのはメリットが大きい)

_build/html をブラウザに入れるのはかっこ悪いですが、 当面の作業効率アップに比べれば軽微な話なので目をつむることとします。 気になるようであればシンボリックリンクでも張れば良いわけですから。

今どきドキュメントを書かないソフトウェアも少ないでしょうし、 Write The Docs というカンファレンスも開催されるくらいですから、 効率的に明快なドキュメントを用意できるようになりたいですね。

-- Docs or it didn’t happen. (Why Documentation is a Priority)

コメントを投稿