Commits

SHIBUKAWA Yoshiki committed a99cb4c

Add hacking guide

Comments (0)

Files changed (6)

cookbook/translation.rst

-================================
-ドキュメントの翻訳にSphinxを使う
-================================
+=============================================
+ドキュメントの翻訳にSphinxを使う (i18n非利用)
+=============================================
 
 :author: 渋川
 :date: 2010/04/18
 
 Sphinxが使っているreStructuredTextにはコメントアウト機能があるので、気軽に翻訳が行えます。ただし、dokuwikiのように、日本語、英語、中国語、ドイツ語・・・など複数言語を一度に扱うのは難しいです。
 
+.. note::
+
+   Sphinxにi18n機能が入る前から利用していたワークフローです。i18n機能を利用したtipsも今後作られるでしょう。
+
 基本的なやり方
 ==============
 

hack/domain/create.rst

+ドメインの処理の流れ
+====================
+
+1. ドメインの登録
+
+   * ドメイン用のロールとディレクティブを作成
+   * ロールとディレクティブの登録
+
+2. ドキュメントのパース時
+
+   * ディレクティブの引数を分析
+   * ロールの実装
+   * リンクを解決
+   * インデックスページを作成
+
+ドメイン開発で参考にするもの
+============================
+
+基本的には既存のドメインを参考にして改造して作っていくことになるでしょう。作りたい言語に近い特性を持つ言語を選びましょう。例えば、クラスとモジュールがあればPythonやRuby、関数型言語(アリティがある)ならErlang、といった具合です。本当に近いものが見つかれば、90%終わったも同然です。
+
+ロールとディレクティブの作成
+============================
+
+まずはディレクティブを作成します。ユーザの学習コストを下げるために、Pythonドメインと同じにできるところは同じにしましょう。ディレクティブは「引数のタイプ」と、「階層上の位置」の種類だけ作ります。
+
+例えば、Rubyの場合、クラスメソッドとアトリビュートはクラスのメンバーにしかなりません。グローバル変数はグローバルに属します。定数とメソッドはクラスのメンバーにもできるし、モジュールのメンバーにもなります。関数はモジュールに属しますよね。言語を知っていると、メソッドとクラスメソッドと関数を同じディレクティブにしたくなるところですが、ぐっとこらえましょう。
+
+とはいえ、共通化できるところも多いので、Rubyの場合は次のようなクラス階層になっています。多くのコードがRubyObjectにあります。これは説明を書くためのディレクティブです。 ``RubyModule`` と ``RubyCurrentModule`` はPythonドメインに準拠しています。インデックスでモジュール名をクリックしたときには ``RubyModule`` ディレクティブの場所にジャンプします。 ``RubyCurrentModule`` はアンカーは作成しないが、そのディレクティブから先に置かれたクラスなどを特定のモジュールに属させたい場合に利用します。どちらも、説明を書くディレクティブではなく、フラグのようなディレクティブなので、多機能な ``ObjectDescription`` ではなく、 ``Directive`` を直接利用します。
+
+.. code-block:: none
+
+   - sphinx.directives.ObjectDescription
+      + RubyObject
+        + RubyGloballevel
+        + RubyModulelevel
+        + RubyEverywhere
+        + RubyClasslike
+        + RubyClassmember
+
+   - sphinx.util.compat.Directive
+      + RubyModule
+      + RubyCurrentModule
+
+ロールは基本的に1種類で済むでしょう。ただし、引数の数やカッコの有無など、自動で修正したい場合はそのような機能を実装します。
+
+
+
+ロールとディレクティブの登録
+============================
+
+``Domain`` クラスを継承したクラスを作ります。 ``object_types``, ``directives``, ``roles``, ``initial_data``, ``indices`` というクラス変数に設定を登録していきます。534行目のクラス定義を参照してください。
+
+``directives`` は、実際にドキュメントで使うディレクティブ名と、先程作ったディレクティブクラスの対応表です。 ``roles`` も、実際にドキュメントで使うロール名と、ロールオブジェクトの対応表です。 ``object_types`` は、ディレクティブとロールを結びつける情報です。それ以外については後述します。
+
+クラスができたら、 688行目の ``setup`` 関数と同じコードを書いて、拡張機能が登録されたときにドメインを追加するようにします。
+
+ディレクティブの引数の分析
+==========================
+
+51行目からのRubyObjectクラスの中で引数の分析を行なっています。
+
+ディレクティブのオプションは、 ``doc_field_types`` クラス変数で設定します。これを登録するだけで、関数やメソッドなどの属性の説明が行えるようになります。
+
+ディレクティブに関する処理は89行目からの ``handle_signature`` メソッドで行なっています。
+
+このメソッドでやっていることは、主に以下のタスクです。
+
+* 正規表現を使ってディレクティブの引数の分析(98行目〜)
+
+  長い正規表現になりがちなので、 `re.VERBOSE <http://ymotongpoo.hatenablog.com/entry/20110123/1295791197>`_ を利用すると良いでしょう。
+
+* 完全修飾名を作る(105行目〜)
+
+  SphinxのPythonドメインでは、メソッド説明を書く時に、 ``.. py:method:: ClassName#methodName`` という名前でも書けますし、 ``.. py:class:: ClassName`` ディレクティブを書いて、その中に ``.. py:method:: methodName`` と書くこともできます。どちらの場合も同じロール名で参照できる必要があるため、文脈情報を利用して、完全修飾名を作ります。 ``self.env.temp_data`` でソースを検索してみてください。登録と情報取得はそれほど難しくはありません。
+
+* タグを追加して、情報を整形する(135行目〜)
+
+  Sphinxの ``addnodes`` モジュールの関数を利用して、ドキュメントを整形します。返り値を最初に書きたい、後に書きたいなどはここを調整することで変更することができます。
+
+ロールの実装
+============
+
+ロールの実装に関してはあまり多くのことをする必要はありません。区切り文字の正規化と、チルダを使った記法のサポートぐらいです。440行目の ``RubyXRefRole`` クラスで実装されています。
+
+リンクを解決
+============
+
+ロールで名前を指定したときに、適切なディレクティブの場所にリンクをするために、アンカー情報を登録します。195行目の ``add_target_and_index`` メソッドでこの処理を行なっています。ここでは ``self.env.domaindata['rb']['objects']`` にすべての名前を登録しています。なお、Rubyの場合は ``['objects']`` にすべて格納していますが、例えば関数がファーストクラスではなく、変数と関数で同名のものが利用できるのであれば、別の辞書に格納させます。
+
+またここでは、インデックスの作成も行なっています。
+
+リンクの解決は594行目の ``find_obj`` メソッドで行なっています。完全修飾名を類推して返します。Rubyではメソッド名の区切りに#を使ったり、::をつかったり、.を使ったりしますが、そのようなファジーな検索はここで行います。
+
+モジュールに関するディレクティブも、 ``self.env.domaindata['rb']['modules']`` にモジュール情報を登録しています。この ``domaindata`` にプログラム言語中の名前空間と同じオブジェクト階層を作るのが、ドメイン実装のキモです。
+
+インデックスを作成
+==================
+
+Sphinxの期待する形式でインデックス情報を作成して返します。474行目の ``generate`` テンプレートメソッドで実装されています。
+
+仕上げ
+======
+
+`SphinxのドメインのAPI <http://sphinx-users.jp/doc11/ext/appapi.html#domain-api>`_ で指定されているメソッド(``resolve_xref``, ``get_objects`` など)をいくつか追加します。ほぼ、参照元のコードのコピーでいけると思います。後は動くようになるまでデバッグして完成させます。 :doc:`/hack/start` で紹介したように、最初に受け入れテスト的なドキュメントを作成しておくことをおすすめします。クロスリファレンスが適切に解決できるかどうかがポイントです。
+
+Sphinxのドメインの場合、完全修飾名、モジュールやクラスの省略を考えると、どうしても組み合わせが複雑になってくるので、ユーザに読ませるドキュメントと、テスト用のドキュメントは別にした方が良いでしょう。Rubyの場合は次の組み合わせが考えられます。
+
+* モジュール外から
+
+  * 他のモジュール内のクラスを参照
+  * 他のモジュール内のクラスのメソッドを参照
+  * 他のモジュール内のクラスの属性を参照
+  * 他のモジュール内の関数を参照
+  * グローバル要素の参照
+
+* モジュールの中から
+
+  * 完全修飾名でモジュール内のクラスを参照
+  * クラス名だけでモジュール内のクラスを参照。
+  * 完全修飾名でモジュール内の関数を参照
+  * クラス名だけでモジュール内の関数を参照。
+
+* クラス内から
+
+  * 完全修飾名でメソッドを参照
+  * メソッド名だけでメソッドを参照
+  * 完全修飾名で属性を参照
+  * 属性名だけで属性を参照
+
+それ以外にも、モジュールのネストなどもあります。
+
+Rubyドメインが行なっているテストは `ここに <https://bitbucket.org/birkenfeld/sphinx-contrib/src/5f95add2ec31/rubydomain/test/test_doc.rst?at=default>`_ あります。

hack/domain/index.rst

+==================
+ドメイン開発ガイド
+==================
+
+:date: 2013/01/01
+:author: 渋川よしき
+
+Sphinxの強みは、プログラム用のドキュメントの作成のための機能が充実している点にあります。最初からPython/C/C++/JavaScriptのAPIを記述するディレクティブが備わっています。同じ関数名でも、対象となる言語を識別してきちんとリンクを貼ることもできますし、ドキュメント内で参照したい言語が一種類しかない場合は、設定ファイルでデフォルトを設定してシンプルにドキュメントを書くこともできます。そのための中核の仕組みが `ドメイン <http://sphinx-users.jp/doc11/domains.html>`_ です。
+
+Sphinxでは(がんばれば)対象となる言語を増やすこともできます。Erlang/Ruby/PHPなどのための拡張機能がすでに作成されています。
+
+ここでは、 `Rubyドメイン <https://bitbucket.org/birkenfeld/sphinx-contrib/src/5f95add2ec31/rubydomain/sphinxcontrib/rubydomain.py?at=default>`_ のソースコードをモデルに紹介します。
+
+.. toctree::
+
+   create
+
+======================
+Sphinxハッキングガイド
+======================
+
+Sphinxハッキングガイドは、プログラマー向けのSphinx拡張開発に関する話題を紹介します。
+
+もし何かあれば、 :ref:`mailinglist` 、 `Twitterで@sphinxjp宛につぶやく <http://twitter.com/sphinxjp>`_ などで情報を出してもらえれば、どんどん載せていきます。
+
+.. toctree::
+   :maxdepth: 2 
+
+   start
+   domain/index
+==================
+拡張開発最初の一歩
+==================
+
+:date: 2013/01/01
+:author: 渋川よしき
+
+拡張開発をするにはもろもろの準備が必要になります。残念ながら、拡張開発に関しては資料が充実しているとは言えない状態です。ソースコードを参照しなければならないケースも多いでしょう。
+
+下記のものがあるのを前提としています。
+
+* Pythonの知識(必須)
+* Mercurialの知識(オプション)
+* Bitbucketのアカウント(オプション)
+* PyPIのアカウント(オプション)
+
+ここでは、 `Rubyドメイン <https://bitbucket.org/birkenfeld/sphinx-contrib/src/978951438662c55de40902506752bd492da0ec8b/rubydomain?at=default>`_ をモデルケースとして説明しています。
+
+ひな形を作る
+============
+
+まずは下記のリポジトリをフォークし、ローカルにクローンしましょう。もし、Bitbucketのアカウントがなく、Bitbucket上でソースコードを管理する予定がない場合、Githubなどを使いたい場合は、Bitbucketのページの右側のペイン内のDownloadと書かれているリンクでダウンロードするのでもかまいません。
+
+https://bitbucket.org/birkenfeld/sphinx-contrib
+
+クローン、もしくはダウンロードが完了したら、このリポジトリに含まれる ``make-ext.py`` を実行します。
+
+.. code-block:: bash
+
+   $ python make-ext.py
+   Creating a new sphinx-contrib package
+   Name: sampledomain (←名前を入れる)
+   Author name: Yoshiki Shibukawa (←作者名を入れる)
+   E-mail: yoshiki at shibu.jp (←メールアドレスを入れる)
+   Created new package in directory sampledomain
+
+これで、ひな形ができあがります。 ``setup.py`` や、 ``MANIFEST.in`` などのファイルもできあがります。
+
+受け入れテストのドキュメントの作成
+==================================
+
+作成されたひな形のトップに ``test`` というフォルダを作り、Sphinxのドキュメントを作ります。
+
+テスト用のドキュメントを実行する場合は、そのフォルダにある ``sphinxcontrib`` フォルダを参照する必要があるので、下記のコードを ``conf.py`` に追加します。
+
+.. code-block:: py
+
+   sys.path.insert(0, os.path.abspath('..'))
+   extensions = ['sphinxcontrib.sampledomain']
+
+このドキュメントには、これから追加する機能ができあがった場合を想定して文章を書いておきます。
+
+ソースコードを作成
+==================
+
+``sphinxcontrib`` フォルダ内にソースコードを置きます。Nameに設定した名前 + ``.py`` が良いでしょう。
+
+拡張機能を作成する場合は下記のものを参照することになるでしょう。
+
+* `Sphinx本体の拡張の説明 <http://sphinx-users.jp/doc11/extensions.html>`_
+* `他の人の作ったSphinx拡張 <https://bitbucket.org/birkenfeld/sphinx-contrib>`_
+* `Sphinxのソースコード <https://bitbucket.org/birkenfeld/sphinx>`_
+* `Docutilsのドキュメント及びソース <http://docutils.sourceforge.net/docs/index.html>`_
+
+例えば、ビルダーの開発をするのであれば、Sphinx本体のビルダーのコードが役に立つでしょう。また、SphinxはDocutilsをベースとして、大規模ドキュメント向けの機能を追加して作られたシステムなので、ドキュメント(:doc:`XMLのようなツリー構造 </reverse-dict/system/doctree>`)のタグを操作して、情報を収集したり、新しいノードを追加する場合はDocutilsや、他に似たようなことをしている拡張機能が役に立ちます。
+
+ドキュメントの作成
+==================
+
+作成されたひな形のトップに、 ``doc`` フォルダを作り、Sphinxのドキュメントを作ります。
+
+おそらく、 ``.. rst:directive::`` ディレクティブと、 ``.. rst:role::`` ディレクティブを使ったリファレンスと、書き方のサンプルのドキュメントになるでしょう。
+
+ドキュメントの方では、設定ファイルの項目を追加する場合は、Sphinx本体と同じように設定項目用のディレクティブとロールを ``conf.py`` で設定すると良いでしょう。
+
+.. code-block:: py
+
+   def setup(app):
+       app.add_description_unit(
+           'confval', 'confval',
+           objname='configuration value',
+           indextemplate='pair: %s; configuration value')
+
+ライセンスは、BSDライセンスで問題なければ、 ``sphinxcontrib`` リポジトリのルートからコピーしていれておきましょう。
+
+また、変更履歴をまとめた、 ``CHANGES`` ファイルも作りましょう。中身はreSTフォーマットで記述します。 ``doc/index.rst`` で次のように書けば、ライセンスと変更履歴をドキュメントにもコピペする必要がなくなり、変更箇所を一箇所に集約できます。
+
+.. code-block:: rest
+
+   ChangeLog
+   =========
+
+   .. include:: ../CHANGES
+
+   License
+   =======
+
+   .. include:: ../LICENSE
+      :literal:
+
+ドキュメント、ライセンス、変更履歴ファイルは ``MANIFEST.in`` にも追加しておきましょう。
+
+PyPIへの公開
+============
+
+ドキュメントを確認して、自分のローカル環境でインストールしてのテストも完了したら、PyPIにアップロードして世界中のSphinxユーザが利用できるようにしましょう。
+
+登録名は、 ``sphinxcontrib-<パッケージ名>`` がよく使われます。
+
+.. code-block:: bash
+
+   $ python setup.py register sdist upload
+
+ドキュメントもPyPIサイトにアップします。
+
+.. code-block:: bash
+
+   $ python setup.py upload_docs --upload-dir=/doc/_build/html/
+
+詳しくは下記の書籍が参考になります。
+
+* `エキスパートPythonプログラミング 5章 <http://www.amazon.co.jp/dp/4048686291>`_
+* `Pythonプロフェッショナルプログラミング 7章 07-01 <http://www.amazon.co.jp/dp/4798032948>`_
+
    reverse-dict/index
    cookbook/index
    articles/index
+   hack/index
    doc
    history
    link