rst2textile-0.1.0 リリースと実装の裏話¶
先日 Sphinx + 翻訳 Hack-a-thon 2012.04 にて rst2textile-0.1.0 をリリースしました。
rst2textileの紹介¶
rst2textileは reStructuredText
形式のテキストを textile
形式に変換するためのツールです。例えば以下のように変換することが出来ます。
元になるsample.rstファイル:
==========
Heading1
==========
:Date: Today
:Author: SpamEgg
:Location: Here
Heading2
==========
Heading3
----------
Heading4
^^^^^^^^^^
*emphasized* (e.g., italics)
**strongly emphasized** (e.g., boldface)
- An item in a bulleted (unordered) list
- Another item in a bulleted list
- Second Level
* Second Level Items
* Third level
#. An item in an enumerated (ordered) list xxxxxxx
#. Another item in an enumerated list yyyyyy
#. Another level in an enumerated list vvvvvvvv
Blockquotes
This text will be enclosed in an HTML blockquote element.
Second Paragraph.
Links
`link text <link_address>`_
Images
.. image:: image.png
以下のコマンドで変換実行:
$ rst2textile.py sample.rst sample.txt
出力されたsample.txtファイル:
h1. Heading1
Date:
Today
Author:
SpamEgg
Location:
Here
h2. Heading2
h3. Heading3
h4. Heading4
_emphasized_ (e.g., italics)
*strongly emphasized* (e.g., boldface)
* An item in a bulleted (unordered) list
* Another item in a bulleted list
** Second Level
** Second Level Items
*** Third level
# An item in an enumerated (ordered) list xxxxxxx
# Another item in an enumerated list yyyyyy
## Another level in an enumerated list vvvvvvvv
Blockquotes
bq. This text will be enclosed in an HTML blockquote element.
bq. Second Paragraph.
Links
bq. "link text":link_address
Images
!image.png!
このrst2textileパッケージは内部にdocutils_textileというdocutilsのwriter実装を持っています。rstファイルをパースして中間形式のノードツリーにするところまではdocutilsがやってくれるので、docutils_textileが実装するべきことはそれほど難しくありません。 Docutilsのnode-treeを疑似XMLで出力する で紹介した方法でノードツリーの構造を確認しながら実装を進めていきます。ただこれがけっこう時間がかかります。textileのフォーマットに一つずつ対応していくので、ひたすら手がかかります。
そこで楽しく実装できるように、余計なこと...工夫をしてみました。
Distutils2でパッケージング¶
パッケージを作成するためにDistutils2のpysetupコマンドを使ってsetup.cfgを作成し、後方互換性のためにsetup.pyも生成しました。
pysetupは Distutils2/packagingのpysetup createが長い で紹介したように、以下のように実行しています。
$ pysetup create
(対話形式で入力、 setup.cfgが生成される)
$ pysetup generate-setup
(setup.pyが生成される)
その後、いくつかのDistutils2のバグに悩みながらsetup.pyをsetuptools対応に書き換えたり、一部のUnicode文字列をstrにencodeするようコードを書き換えたりしました。こういった課題がクリアされればパッケージングはかなり楽になると思いますが、今の Distutils2-1.0a4 ではまだ厳しいですね。みんなでバグレポート出して改善しましょう。
一応、rst2textileは setup.py / setup.cfg とも調整してあるので、以下のどちらでもインストール可能です。
pip:
$ pip install rst2textile
pysetup:
$ pysetup install rst2textile
README.rstにテスト可能な変換サンプルを用意¶
README.rst にrst2textileの変換例として、以下のように記載しています。
List items
-----------
.. container:: test, rst, textile
rst::
- An item in a bulleted (unordered) list
- Another item in a bulleted list
- Second Level
* Second Level Items
* Third level
textile::
* An item in a bulleted (unordered) list
* Another item in a bulleted list
** Second Level
** Second Level Items
*** Third level
この記述はテスト出来るように、docutils標準の containerディレクティブでマーキングしてあって、test.pyの中でこのcontainerノードを見つけてテストするようにしてあります。もし実装に間違いがある状態で実行すると、実行結果は以下のようになります。
$ python test.py
......F......
======================================================================
FAIL: runTest (__main__.ReSTTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 42, in runTest
self.assertEqual(self.expect, self.actual, message)
AssertionError: Convertion Mismatch (rst -> docutils_textile)
##source:
- An item in a bulleted (unordered) list
- Another item in a bulleted list
- Second Level
* Second Level Items
* Third level
##expect:
* An item in a bulleted (unordered) list
* Another item in a bulleted list
** Second Level
** Second Level Items
*** Third level
##actual:
* An item in a bulleted (unordered) list
* Another item in a bulleted list
* Second Level
* Second Level Items
* Third level
##node-tree:
<document source="<string>">
<bullet_list bullet="-">
<list_item>
<paragraph>
An item in a bulleted (unordered) list
<list_item>
<paragraph>
Another item in a bulleted list
<bullet_list bullet="-">
<list_item>
<paragraph>
Second Level
<bullet_list bullet="*">
<list_item>
<paragraph>
Second Level Items
<bullet_list bullet="*">
<list_item>
<paragraph>
Third level
----------------------------------------------------------------------
Ran 13 tests in 0.004s
FAILED (failures=1)
$
上記のように「source(入力)」「expect(期待)」「actual(実際)」「node-tree(疑似XML)」といった実装修正の手がかりを全て表示してくれます。この仕組みを用意するためにdocutilsのコードをけっこう読む羽目になりましたが、それ以降は実装がかなり楽になりました。
test.pyがどのように実装されているかについては https://bitbucket.org/shimizukawa/rst2textile/src/tip/test.py を参照して下さい。
テストとして見ると無駄に努力していますが、以下のようなことをやりたくてこの仕組みを用意しました。
README.rst で変換サンプルを提示したい
変換サンプルはテキストのままでも読みたい(自動生成したくない)
実装時にnode-treeのpseudoxml出力が欲しい
docutils標準外のdirective等は使用したくない(PyPI公開のため)
以下のフローで実装を進めたい
フォーマット対応の対象とする rst, textile の対を README.rst に書く
python test.py を実行してactualとnode-treeを確認する
docutils_textileのwriter実装を行う
テストが通るまで2,3を繰り返す
リリースではサンプルとしてREADME.rstをそのまま利用する
上記フローならtextile仕様把握後にnode-treeを確認しながら実装できて楽だし、サンプルと実装とテストが乖離しないのでメンテも楽かなと思います。あくまで壊れてないサンプルを提示するのが主体なので、この方式上でフォーマットの組み合わせを細かくとかは考えない方向で。
まとめ¶
rst2textileって、自分は使う予定があんまりないので、今対応してないフォーマット部分は要望次第で実装ということにしたいなあ。