2013-02-16

ほかのモジュールで使うかもしれなくて、頭の体操めいたモジュールを作った。こういうものが世の中にあるのかどうか知らないけど、自分ではちょっと面白いと思ってるので書きます。

文字列の範囲を指定してマークアップするという作業を文字列から独立させたようなモジュールです。

markup.Marker オブジェクトを作り、markup.Marker.markup() メソッドで範囲 startend を指定して stagetag でマークアップします。markup.Marker オブジェクトはマークアップする対象の文字列(実際には文字列に限らず任意のシーケンスでよい)とは独立しています。markup.Marker.apply() メソッドを呼んで、マークアップ結果を文字列と混ぜ合わせて列挙します。”.join() で繋げればマークアップ文字列になります。

>>> import markup
>>> marker = markup.Marker()
>>> marker.markup(0, 5, '<b>', '</b>')
>>> marker.markup(-1, None, '<i>', '</i>')
>>> marker.markup(None, None, '<p>', '</p>')
>>> list(marker.apply('Hello, world!'))
['<p>', '<b>', 'Hello', '</b>', ', world', '<i>', '!', '</i>', '</p>']
>>> ''.join(marker.apply('Hello, world!'))
'<p><b>Hello</b>, world<i>!</i></p>'

markup.Marker.markup() の順に関わらず、マークアップの入れ子を適切に列挙するのが仕事です。start, end が正の整数値(fixed な範囲指定といいます)なら markup.Marker.markup() 実行時その場で、None や負の整数値であれば(こちらは unfixed な範囲指定)なら markup.Marker.apply() メソッド実行時に、入れ子の整合性チェックを行って、破綻するようであれば例外 ValueError を発生させます。

>>> marker = markup.Marker()
>>> marker.markup(0, 5, '<b>', '</b>')
>>> marker.markup(0, 6, '<c>', '</c>')
>>> marker.markup(1, 7, '<d>', '</d>')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File ".\markup.py", line 148, in markup
    .format(span, conflicting))
ValueError: markup.Span(1, 7, '<d>', '</d>') conflicts with existing markup.Span
(0, 5, '<b>', '</b>')
>>> ''.join(marker.apply('ABCDEFGHIJ'))
'<c><b>ABCDE</b>F</c>GHIJ'
>>> ''.join(marker.apply('123'))
'<c><b>123</b></c>'

同じ範囲であれば、あとから追加したほうが外側になります。

今回の用ではこれだけで足りてるのだけど、入れ子が破綻してたら分割点で分けて追加するとか、いろいろ機能の追加のしどころはあると思う。あんまり長くならないのが好きだけど。

unicodeseg.py 開発停止

やる気があるのかないのかわからなかった unicodeseg.py ですが、開発停止。理由は、ICU (International Components for Unicode) の Python バインディングである、PyICU がちゃんと動いたから。せこせこ作って試してた unicodeseg.py の LineBreaking 境界のン千項目のテストコードに試しに食わせてみたところ一瞬にして完全クリアしたのを見て踏ん切りついた(笑)。

いや、もともと PyICU の存在は知ってたんですよ。作る前に調べたから。けどどうもその時は開発がアクティブでなさそうで、なんか頼りなさそうだったのだ。しかしいまや新しい ICU のバージョンにもキャッチアップしてるのでこれでいけると。まあプロジェクトとして人は足りてなさそうだけど……。

なので次の関心としては、PyICU のバイナリ配布物を作りたい。ICU のビルドやそれと PyICU と組み合わせての動作確認はできてるので、使う人が ICU を自分で Visual Studio でビルドしなくても簡単にインストールできるようにしたい。それと OS X 版もバイナリ配布あったほうがいいよね。

OS X も Mountain Lion の今は開発ツールが別途ダウンロードだったと思うので、バイナリ配布物の需要はあると思う。というか、みんな忙しいんだからいちいち ICU みたいなでかいライブラリを手元でビルドさせることはないと思うんだよね。そういう意味で、バイナリ配布というのは現実的には(Windows にとっても Mac にとっても)重要な要素だと思うんだけど、現状のパッケージング開発界隈にはそうは見えてないらしい。まあおそらくウェブ開発の人中心なのだろうから、Win/Mac のデスクトップ・コンピューティングにはあまり関心がないのだと思う。かれらはどちらかというとダウンロードからインストールまでの工程の自動化という面にフォーカスしているように思える。

そういや pip って今はバイナリ配布できるのかな。Windows だと、setup.py bdist_wininst の出来がアレだし、MSI 形式も Python 3 では使えなくなっちゃったしで、現状バイナリ egg がいちばん現実的な形式になっている。そうなると easy_install からいつまでも卒業できないし、かといって wxPython のようにインストーラでしか配布してないモジュールもあるから、けっきょくパッケージ管理一元化なんて Windows では夢のまた夢、と悲しくなってしまう。

(wxPython のために一言添えておくと、かれらの独自インストーラはカスタマイズ用のオプションを持っているので標準のセットアップ配布物よりはまとも。けどたぶんセットアップ・オプションの存在なんて誰も知らないと思う(笑)。)

そういうわけで、バイナリ配布物の作り方のようなものを調べなくちゃいけないんだけど、手元で動いてるとそれでいいやあとなっちゃうのでなかなか進まない。というか PyICU いじる時間自体もあんまない。ああ、PyICU にもコミット取らなきゃいけないだろうしねえ。優先度としては低めかなあ……。