2007年4月アーカイブ

doctestを知った

Djangoのマニュアルを眺めていて、テストの項があったので読んでみたら"doctest"なるものの存在を知りました。
5.2 doctest -- 対話モードを使った使用例の内容をテストする

doctestはunittest同様、Pythonの標準モジュールです。unittestはその名の通り、単体テスト用のツール。テストケースとなるクラスを書きます。一方、doctestもその名の通り、docstring中にテストコードを書きます。

"""
example.
 
>>> a_plus_b(1,2)
3
>>> a_plus_b(2,3)
5
>>> a_plus_b(3,4)
7
"""
def a_plus_b(a, b):
    """
    return a + b
 
    >>> a_plus_b(10, 20)
    30
    >>> a_plus_b(100, 200)
    300
    >>> a_plus_b(1000, 2000)
    3000
    """
    return a + b
 
def _test():
    import doctest
    doctest.testmod()
 
if __name__ == '__main__':
    _test()
 
$ python sample_doctest.py
$ python sample_doctest.py -v
Trying:
    a_plus_b(1,2)
Expecting:
    3
ok
Trying:
    a_plus_b(2,3)
Expecting:
    5
ok
Trying:
    a_plus_b(3,4)
Expecting:
    7
ok
Trying:
    a_plus_b(10, 20)
Expecting:
    30
ok
Trying:
    a_plus_b(100, 200)
Expecting:
    300
ok
Trying:
    a_plus_b(1000, 2000)
Expecting:
    3000
ok
1 items had no tests:
    __main__._test
2 items passed all tests:
   3 tests in __main__
   3 tests in __main__.a_plus_b
6 tests in 3 items.
6 passed and 0 failed.
Test passed.

テストと言えば単体テストでxUnitしか考えていませんでしたが、こういう仕組みというかやり方もあるんですね。

Pythonの順序を持った辞書クラス。

>>> from pythonutils.odict import OrderedDict
>>> od = OrderedDict()
>>> od
OrderedDict([])
>>> od['key1'] = 'value1'
>>> od['key2'] = 'value2'
>>> od
OrderedDict([('key1', 'value1'), ('key2', 'value2')])

pythonutils.validateも好きになれそう。PHPのis_*関数な感じですかね。

PHPのDOMDocumentまわりには、文字化けと数値文字参照への強制変換という問題があるようです。

文字化けについては、DOMDocument->loadHTMLのマニュアルにコメントがありました。

Pay attention when loading html that has a different charset than iso-8859-1. Since this method does not actively try to figure out what the html you are trying to load is encoded in (like most browsers do), you have to specify it in the html head. If, for instance, your html is in utf-8, make sure you have a meta tag in the html's head section:

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>

If you do not specify the charset like this, all high-ascii bytes will be html-encoded. It is not enough to set the dom document you are loading the html in to UTF-8.
PHP: DOMDocument->loadHTML() - Manual

loadHTMLするHTMLについては、metaタグで文字コードを指定していれば正しいエンコーディングで処理してくれるようです。metaタグで文字コードを指定していないHTMLについて(一部を切り抜いたHTMLなど)は補完などの処理が必要になるのでしょうか?

次に、数値文字参照への変換ですが、これをどう防げば良いのかは不明です。ひとまず、数値文字参照を文字に戻す事はできるので、以前書いた(マニュアルからコピった)適当な関数に渡して対処してみました。元々数値文字参照だったものまで戻ってしまうのでどうにかしたいところです。
(libxml2のバージョンの問題と言う情報もありましたが、手元ではパッケージから入れているせいか駄目みたいです。)

ごまかしながら使う事は出来るようですが、どうも微妙な印象です。マニュアルから"(No version information available, might be only in CVS)"が消えるまでは使うな(知らんよ)、という事でしょうか?

テスト環境:

  • OSX, PHP5.2.0, libxml2.6.27(MacPorts)
  • etch, PHP5.2.0-8+etch1, 2.6.27.dfsg-1(aptitude)

PHPでXPath

XPathはSeleniumかXSLTで極稀に使うだけだった事に気がついたので、PHPのXPathを使ってみました。

XPathの結果がDOMで返るようなので、併せてDOMも。

今までは、PHPのDOMサポートがどのような状況なのかよく分かっておらず、HTMLをいじる際にはPEARのXML_HTMLSaxで済ませていました。DOMDocument->loadHTML()のドキュメントに"(No version information available, might be only in CVS)"とあるのはどういう事でしょうか?MacPortsから入れたPHP5.2.0では使えるんですが。

XPathについて半端な知識しかないまま過ごして来たので、XPath自体は改めてドキュメントを読もうと思っていますが、ひとまずPHPでどうつかえばよいかを簡単に試した結果をメモしておきます。

PythonPerlを触ってみた

macでビルドに失敗したので、etchをPowerBookG4に入れた上でインストールしてみました。
KOSHIGOE学習帳 - [python]Perl関連

Pythonから"簡単"にCPANモジュールを使えると思っていたのですが、意外(当然?)にめんどくさい事が多そうです。

そもそも、Pythonのタプルを渡す事が出来ません。ソースのコメントを眺める限りでは、Boolean, Integer, Float, String, List, Dictionaryだけが許可されている風です。

さらに、よく分かっていませんが日本語も何かしらの理由で受け取る事が出来ない場合があるようです。Acme::KensiroというCPANモジュールを使ってみたのですが、返ってくる文字列をPythonで受け取る事が出来ませんでした。

# -*- coding: utf-8 -*-
import perl
 
perl.addVariable('jap', 'あいうえお')
perl.execute('print utf8::is_utf8($jap) ? "=> utf8 flag.\n" : "=> not utf8 flag\n";')
perl.execute('print "in pl[0]: $jap\n";')
print 'in py[0]:', perl.getVariable('jap')
 
perl.execute('$p_jap = "かきくけこ"; print "in pl[1]: $p_jap\n";')
perl.execute('print utf8::is_utf8($p_jap) ? "=> utf8 flag.\n" : "=> not utf8 flag\n";')
perl.execute('print "in pl[2]: $p_jap\n";')
print 'in py[1]:', perl.getVariable('p_jap')
 
perl.execute('use utf8; $p_jap_u = "かきくけこ"; print "in pl[3]: $p_jap_u\n";')
perl.execute('print utf8::is_utf8($p_jap_u) ? "=> utf8 flag.\n" : "=> not utf8 flag\n";')
perl.execute('print "in pl[4]: $p_jap_u\n";')
print 'in py[2]:', perl.getVariable('p_jap_u')
 
perl.execute('''
use Acme::Kensiro;
$ata = kensiro(16);
print utf8::is_utf8($ata) ? "=> utf8 flag.\n" : "=> not utf8 flag\n";
print "in pl[5]: $ata\n";
''')
print 'in py[3]:', perl.getVariable('ata')
=> not utf8 flag
in pl[0]: あいうえお
in py[0]: あいうえお
in pl[1]: かきくけこ
=> not utf8 flag
in pl[2]: かきくけこ
in py[1]: かきくけこ
Wide character in print at (eval 6) line 1.
in pl[3]: かきくけこ
=> utf8 flag.
Wide character in print at (eval 8) line 1.
in pl[4]: かきくけこ
in py[2]: かきくけこ
=> not utf8 flag
in pl[5]: あたたたた
in py[3]:
Traceback (most recent call last):
  File "perl_acme_kensiro.py", line 25, in ?
    print 'in py[3]:', perl.getVariable('ata')
ValueError: Unknown Perl variable type

とりあえず、CPANに頼るしか無い場面がやってくるまで封印しておきます。ジョークモジュールを触るくらいならPythonから動かす必要は無いわけですし。


久しぶりにcpanコマンドを触ってみましたが、相変わらずテストでよく失敗します。"perl Makefile.PL; make; make intall"だと平気なんですが、よく分かりません。よく分からないままモジュールをインストールしていると、突然よく分からない依存モジュールをインストールしてみたりして、気がついたらインストールしていたはずのモジュールが消えていたり。やはり鬼門です。
いや、まあ、ちゃんと"読め"ばいいわけですけどね。。。

PythonでPlaggerしてますか?

以前どこかで、PerlでPythonコードを埋め込んで実行出来るという記事を見かけたのが頭に残っていたので、Pythonではどうかと調べてみると、ありますね。

PyPerlが先発なれど"pyperl is currently unmaintained."との事。ここを読む限りでは、PythonPerlは2006/06/16頃に0.9がリリースされていてアナウンスされている様子。

PythonPerlを使って適当なPerlコードをPythonに埋め込んで実行してみようと思ったのですが、手元のOSXではビルドに失敗してしまいました。

~$ python setup.py build
running build
running build_ext
building 'perl' extension
/usr/bin/gcc-4.0 -fno-strict-aliasing -Wno-long-double -no-cpp-precomp -mno-fused-madd -fno-common -fno-common -dynamic -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -I/opt/local/Library/Frameworks/Python.framework/Versions/2.4/include/python2.4 -I/opt/local/include/python2.4 -c perlmodule.c -o build/temp.darwin-8.9.0-Power_Macintosh-2.4/perlmodule.o -I/opt/local/include -fno-common -DPERL_DARWIN -no-cpp-precomp -fno-strict-aliasing -pipe -Wdeclaration-after-statement -I/usr/local/include -I/opt/local/include -I/opt/local/lib/perl5/5.8.8/darwin-2level/CORE
perlmodule.c: In function ‘perl_reset’:
perlmodule.c:355: error: ‘environ’ undeclared (first use in this function)
perlmodule.c:355: error: (Each undeclared identifier is reported only once
perlmodule.c:355: error: for each function it appears in.)
perlmodule.c: In function ‘initperl’:
perlmodule.c:424: error: ‘environ’ undeclared (first use in this function)
error: command '/usr/bin/gcc-4.0' failed with exit status 1

ビルド出来ないものは仕方が無いので、触らないまま話を進めます。要は、DjangoなりTurboGearsなりでGUIを用意して、PythonPerlとかでPlaggerとお話するようなアプリケーションを作っている人はいたりするのかな、と(別にフレームワークは関係ありませんが例として)。想像でしかありません(し、普通なのかもしれません)が、PythonからPerlを触れるという事はPlaggerのPythonバインディングをPythonで書けるという事でしょうか?既にあるとか?

通常(ですかね?)はCとかで書かれたアプリケーションに対してLL言語用のバインディングが用意される流れのように思うのですが、少なくとも遊ぶ限りにおいてはLL言語で書かれたアプリケーションに対して他のLL言語用のバインディングを書いても楽しめそうです。

ここいらでfuseを一区切り

どうすればfuseを使えるのかを知りたかったので、サンプルコードを参考にあれこれしていましたが、ここらで一区切り。

"ローカルファイル"以外の何かを使って読み書きが出来ればFUSEのさわり位は体験出来るだろうという事で、お題を『memcachedへの入出力をFUSEを使って書いてみる』としてみました。

書いたコードでは、読み・書き・削除くらいしか出来ませんが、カーネルもファイルシステムもろくに知らない自分でも、ls,cat,vi,rmなどのコマンドが使える似非ファイルシステムが書けました。

FUSEを利用したファイルシステムを実装する作業は、システムコールAPIを実装する作業と言えると思います。今回利用したPythonバインディングでは、fuse.Fuseクラスを継承してopen,read,writeなどのシステムコールをメソッドとして実装しました。この時、当然ながらシステムコール関数が利用する構造体(statやfstat)の遷移(?)などを扱う必要があるので、各システムコールがどんな役割を持っていてどんな動作をするのかは知っている必要があります。この辺りは適当な知識しか無いので困りました。

今回は目標を"感覚を掴む事が出来ればよし"としていたので、この辺りでやめにしておきますが、やはりI/Oの基礎(になるのかな?)は勉強し直さないと駄目ですね。ある程度"なにを知りたいのか"がはっきりしているので、やり易い気がしています。

そんなこんななFUSE体験記でした。

"虎の門"の映画のやつ。
どうも、「ラストキング・オブ・スコットランド」という作品を"史実"だと思って観たけど、エンドロール辺りでフィクションだと気がついてご立腹のご様子。井筒監督の視点ではオフザケが過ぎる作品だったという事でしょうか。

この作品をぼーっと観てしまうと、"史実"として頭に残ったり、微妙なイディ・アミン像を植え付けられたりするのでしょうかね。

側近扱いの医師は実在しないようです(少なくとも、映画で演じられたニコラスは嘘らしい)。


というか、こっちに『へぇ』。

ウガンダ・トラ - 容姿が似ていた事からこの芸名がつけられた。 イディ・アミン - Wikipedia

帰り道のラーメン屋脇でそんな場面に出くわしました。

どうやら、IEがクラッシュする問題を抱えているようです。

JSONPを返すプログラムを、mod_cacheを利用してサーバ側でキャッシュしています。ところが、コールバック関数名がリクエストごとに毎回異なっていると、mod_cacheでは(たぶん)キャッシュできません(URLをキーとしているはず)。このため、タイムスタンプを利用せずに何とかできないものかと試行錯誤してみました。

ロードのたびにユニークにしなければならなかったのは、script要素を削除している辺りにIEのバグが潜んでいたかららしいので、その辺を中心にtry&error。

if (typeof(MochiKit.Async.JSONPCallbacks) == 'undefined') {
    MochiKit.Async.JSONPCallbacks = {

        nextCallbackId: MochiKit.Base.counter()
    };
}
try {
    /* remove script element */
    setTimeout(
        function(){
            var element = document.getElementById('_JSONPRequest' + callbackId);
            element.parentNode.removeChild(element);
        }, 100);

} catch (e) {
    // pass
}

以下のページに従って、setTimeoutを使ってscript要素の削除を遅延させてみました。
掲示板/JavaScript質問板/過去ログ/一覧/ removeChildでIEが落ちる - TAG index

手元の環境では落ちなくなったように思いますが、"描画が追いつかなくなると"という事なので、環境によっては変わらず落ちる気がしています。


いざとなったら、script要素を削除しなければ。。。

FUSEで遊ぶために、まず何をどうしたらいいのか調べている最中です。

まずは、Pythonバインディングに付属するサンプルを読みつつ動かして感じをつかみます。

example/hello.pyを、"./hello.py /path/to/mount/point"などとして実行すると、"/path/to/mount/point"にサンプルのFSがマウントされます。サンプルコードでは、helloというファイルが見えるようになり、その中身が"Hello World!\n"となっています。当然、lsやlvなどで操作可能です。ただし、サンプルのFSではgetattr,readdir,open,readのみが実装されているだけなので、書き込み操作などを実行した場合にはエラーが返ります。

今のところ分かった事は、Fuseというクラスを継承したFSクラスを用意して、その中で必要なメソッドを実装すればローカルのFSにマッピング(?)出来るのだろうという事だけです。また、サンプルコード内にコマンドライン引数の処理が書かれていない事から、その辺の処理はFuse内でやっている気配があるという事(第1引数がマウントポイントでしょうか)。

サーバデーモンをアンマウント時に終了させる方法や、メソッド実装に要求される事、その他諸々と、まだまだ調べておきたい事はありますが、ひとまず実際にコードを書きながら調べて遊んでみれば良いのかなと思っています。

特別なネタが思いつかない限りは、FUSEでこっそり遊んでみたいと思います。


しかし、あれですね。
4/1は皆さん頑張りますね。
恥ずかしながら、"午前中だけ"だとは知りませんでした。
エイプリルフール - Wikipedia

sshfsを触ってみた

気になったままスルーしていたので、触るだけ触ってみました。

"macfuse - Google Code"からdmgが落とせるようなので、落としてきてマウントしてpkgからインストールしてインストール完了後に再起動。
sshfsも同じところにおいてあるので、これもインストール。インストール(dmgからのコピー)しましたが、何も起きないので放置しました。折角なので、CUI版を入れてから判断します。

上記手順でGUI版sshfsを入れてあれば一緒にsshfsコマンドが入っていますが、MacPortsにもあるらしいのでMacPortsから/opt/localにおいてみる事にします。
そういうわけで、"sudo port install sshfs"しました。

ここで余談ですが、sshfsをMacPortsで入れる前に他のFUSE関連パッケージを入れようとして失敗してました。Python用のバインディングが用意されているらしいので入れようと思ったのですが、出来ませんでした。
いくつかのFUSE関連パッケージで同じようなエラーメッセージが出ていて、xcode云々というメッセージがやけに気になったので、Xcodeのバージョンを調べてみましたが、これが2.0。
macfuseをインストーラ(pkg)を使ってインストールしたので細かいドキュメントを見ていませんでした。どうもXcodeの2.4が必要だったらしいのです(macfuse - Google Code)。
2.0の段階でインストール出来た理由は不明ですが、ひとまずよしとします。
Tools - Xcode

さて、sshfsがインストール出来たので使ってみました。

~$ sshfs <user>@<host>:<path> <mount path>

"sshfs -h"を見る限りオプションがこんもりとあるようですが、今は無視します。ひとまず、レンタルサーバにつないで、読み書き出来る事が確認出来ました。

そんなこんなで、最後に"sudo port install fuse-bindings-python"しておしまい。

プロフィール

このアーカイブについて

このページには、2007年4月に書かれたブログ記事が新しい順に公開されています。

前のアーカイブは2007年3月です。

次のアーカイブは2007年5月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。