2009年12月16日水曜日

aws with python

botoがよろしい
boto
結構パッチを取り込んでもらったのでだいぶ使いやすくなった。

botoドキュメント

amiをEBSへ変換する
Amazon EC2 – Boot from EBS and AMI conversion

botoで変換するとこんな感じ
Creating an EBS-backed AMI from an S3-backed AMI

いろいろ自動化できて楽しい。
AMI内でサーバを再起動するより、裏で立ち上げてElastic IPをすりかえてしまうとか
いろいろ試せて楽しい
Spot Instanceにも、とりあえずだけど対応してもらった

felica with python

felicalibが主流。
felicalib
ただしWindowsのみ

Pythonからの使用例(ctypes)
FeliCa の Id を python で読む(その2)
Python: PaSoRiでSuicaの履歴を読み出す

Javaからの使用例(JNA)
'PaSoRi'を使用して、FeliCaカード内の'IDm'を読み取る
Javaの応用:Felicaカードリーダ

libpafe(Linux用)
libpafe

2ch
■◇FeliCa でソフトを作りまくるスレ◇■
FeliCa でソフトを作りまくるスレ 2ブロック目

追記 12/24 18:00
三者間通信
三者間通信 - 新・招き猫のページ
PaSoRi「パソリ」(RC-S320/RC-S330)三者間通信 サンプル

SDK For Felica + AIR
SDK for FeliCa & Adobe AIR
FlashDevelop.jp
フリーのFlash統合開発環境 FlashDevelop (+flex 3 SDK)を入れてみました

2009年9月30日水曜日

String concatenation in Python

文字列連結の効率の話
addのほうが早かったよなと思って、色々試してみた。

def str_mul():
    return name * count

def str_join():
    lst = []
    append = lst.append
    for i in xrange(count):
        append(name)
    return ''.join(lst)

def str_map():
    lst = map(lambda x:name, xrange(count))
    return ''.join(lst)

def list_comp():
    return ''.join(name for i in xrange(count))

def str_add():
    s = ''
    for i in xrange(count):
        s += name
    return s

import cStringIO
def cstring_io():
    io = cStringIO.StringIO()
    write = io.write
    for i in xrange(count):
        write(name)
    return io.getvalue()

import StringIO
def string_io():
    io = StringIO.StringIO()
    write = io.write
    for i in xrange(count):
        write(name)
    return io.getvalue()

from array import array
def str_array():
    a = array('c')
    add = a.fromstring
    for i in xrange(count):
        add(name)
    return a.tostring()

from mmap import mmap
def str_mmap():
    m = mmap(-1, count * len(name))
    write = m.write
    for i in xrange(count):
        write(name)
    m.seek(0)
    return m.read(count * len(name))

def main():
    global count, name
    func_list = (str_mul, str_join, str_add, string_io, cstring_io,
                 str_map, list_comp, str_array, str_mmap)

    # test
    count = 2
    name = 'hello'
    assert all((name * count).__eq__(f()) for f in func_list)

    import timeit
    for c in (10, 1000):
        for b in (1, 1000):
            for f in func_list:
                count = c
                name = 'hello' * b
                print '%10s x %4s, "hello"*%4s: %9.4fms'%(
                    f.func_name, c, b, timeit.timeit(f, number=100)/100*1000)

if __name__ == '__main__':
    main()


   str_mul x   10, "hello"*   1:    0.0006ms
  str_join x   10, "hello"*   1:    0.0042ms
   str_add x   10, "hello"*   1:    0.0033ms
 string_io x   10, "hello"*   1:    0.0312ms
cstring_io x   10, "hello"*   1:    0.0072ms
   str_map x   10, "hello"*   1:    0.0059ms
 list_comp x   10, "hello"*   1:    0.0077ms
 str_array x   10, "hello"*   1:    0.0081ms
  str_mmap x   10, "hello"*   1:    0.0180ms
   str_mul x   10, "hello"*1000:    0.0108ms
  str_join x   10, "hello"*1000:    0.0131ms
   str_add x   10, "hello"*1000:    0.0126ms
 string_io x   10, "hello"*1000:    0.0411ms
cstring_io x   10, "hello"*1000:    0.0364ms
   str_map x   10, "hello"*1000:    0.0146ms
 list_comp x   10, "hello"*1000:    0.0167ms
 str_array x   10, "hello"*1000:    0.0377ms
  str_mmap x   10, "hello"*1000:    0.1105ms
   str_mul x 1000, "hello"*   1:    0.0017ms
  str_join x 1000, "hello"*   1:    0.2352ms
   str_add x 1000, "hello"*   1:    0.2235ms
 string_io x 1000, "hello"*   1:    2.4272ms
cstring_io x 1000, "hello"*   1:    0.5111ms
   str_map x 1000, "hello"*   1:    0.3591ms
 list_comp x 1000, "hello"*   1:    0.2296ms
 str_array x 1000, "hello"*   1:    0.5338ms
  str_mmap x 1000, "hello"*   1:    0.4778ms
   str_mul x 1000, "hello"*1000:    8.5971ms
  str_join x 1000, "hello"*1000:    8.3908ms
   str_add x 1000, "hello"*1000:   29.4590ms
 string_io x 1000, "hello"*1000:   10.6351ms
cstring_io x 1000, "hello"*1000:   28.7235ms
   str_map x 1000, "hello"*1000:    8.8407ms
 list_comp x 1000, "hello"*1000:    8.2300ms
 str_array x 1000, "hello"*1000:   38.9228ms
  str_mmap x 1000, "hello"*1000:   18.6053ms

addは50kB程度までは、文字列長や回数にかかわらず早い。
'str' * nをjoinが逆転するのは見てびっくり。
StringIOは1MBを超えたあたりで突然加速、理由不明。
cStringIOを余裕で追い抜き、100MBあたりではjoinに並ぶ。
ただし、1MBを超えるようなものはPythonで扱うべきではないかも。
for, map, list内包は勝ったり負けたり。
disってみたところ、早そうなのはfor-join。
psycoも使ってみたけど、速度はあまりかわらず。

結論:
addでもjoinでも扱いやすいほうを使おう。
速度はPythonを使う上では気にしない。

2009年9月14日月曜日

Interpolation surprise — And now for something completely Pythonic...

Interpolation surprise — And now for something completely Pythonic...

おそろしや

class Surprise(object):
def __str__(self):
return "[str]"
def __unicode__(self):
return u"[unicode]"

surprise = Surprise()

print "%s %s %s" % (surprise, u"foo", surprise)

出力:
[str] foo [unicode]

つまり、途中でUnicodeが来ると、それ以降は__unicode__が呼ばれるようになるわけです。


ちなみにPython2.3なら
出力:
[str] foo [str]
となります。

2009年9月3日木曜日

setuptools

SolacePlurkからリリースされました。

Stack Overflowのクローンです。
あいかわらず仕事が早いしソースが読みやすい。

しかし今回、最も驚いたのが、setup.pyの使い方。
こんな便利なものだったんですね。
なお、正しい手順はREADMEInstallationを見てください。

私の手順は以下のとおり。

$ hg clone http://bitbucket.org/plurk/solace
$ mkvirtualenv solace
(solace)$ cd solace
(solace)$ python setup.py develop
(solace)$ echo SECRET_KEY = \'`mkpasswd -l 40`\' > config.py
(solace)$ SOLACE_SETTINGS_FILE=config.py python setup.py reset
(solace)$ SOLACE_SETTINGS_FILE=config.py python setup.py runserver

はやい!本当に早い!
setup.pyってこんなに便利だったんだ。
拡張コマンドを追加できるなんて知らなかったよ!

さて、setup.pyを読んでいきます。
まずはここ。

try:
from solace import scripts
except ImportError:
pass
else:
extra['cmdclass'] = {
'runserver': scripts.RunserverCommand,
'initdb': scripts.InitDatabaseCommand,
'reset': scripts.ResetDatabase,
'make_testdata': scripts.MakeTestData,
'compile_catalog': scripts.CompileCatalogEx
}


コマンドの実装はこんな感じ

from distutils.cmd import Command
class InitDatabaseCommand(Command):
description = 'initializes the database'
user_options = [
('drop-first', 'D',
'drops existing tables first')
]
boolean_options = ['drop-first']

def initialize_options(self):
self.drop_first = False

def finalize_options(self):
pass

def run(self):
from solace import database
if self.drop_first:
database.drop_tables()
print 'dropped existing tables'
database.init()
print 'created database tables'

わかりやすい!

もう一つ勉強になったのがtests_require

setup(
name='Plurk_Solace',
version='0.1',
url='http://opensource.plurk.com/solace/',
license='BSD',
author='Plurk Inc.',
author_email='opensource@plurk.com',
description='Multilangual User Support Platform',
long_description=__doc__,
packages=['solace', 'solace.views', 'solace.i18n', 'solace.utils'],
zip_safe=False,
platforms='any',
test_suite='solace.tests.suite',
install_requires=[
'Werkzeug>=0.5.1',
'Jinja2',
'Babel',
'SQLAlchemy>=0.5',
'creoleparser',
'simplejson',
'webdepcompress'
],
tests_require=[
'lxml',
'html5lib'
], **extra
)

ここで、テストのときだけ必要なライブラリを書くことができる。
python setup.py test
とすると依存ライブラリをとりにいきます。
すばらしい!


setuptoolsをもっと勉強します。
deploy+setup Scriptとして、今後活用していくことを誓います。

2009年8月6日木曜日

eclipse WTP encoding

eclipse


WTP encoding

最近、Javaの仕事が増えたせいでEclipseをよく使います。
WTPのencoding設定で困ってほかのプラグインに移ってしまう人が多いので、
まとめておきます。

その1. JSPがISO-8859-1になってしまって困っちゃう
Window => Preferences => General => Content Types
より
Text => JSP を選んで"Default encoding"にUTF-8を指定し、Updateを押す。


その2. HTMLがShift_JISに決め打ちされて困っちゃう。
eclipse.iniに
-Duser.language=en
と書いておく。

mercurial

http://www.vectrace.com/mercurialeclipse/
ためしにインストールしてみたmercurialプラグインは比較で文字化けしてしまい、
使い物になりません。

http://bitbucket.org/mercurialeclipse/main/
bitbucketが使われているのならば、対応は私がしようと思って、最新版をとってみたら
あれれ?使える。
というわけで、しばらくmercurial用クライアントとして、Eclipseも使ってみようと思います。

しかし、他のクライアントを使ってみるとわかるのですが、
Subversiveは本当によくできている。
Syncronize with Repositoryが本当に使いやすい。
あれをmercurialに使えたらどれほど幸せか。
まあ、ほしい機能はIssuesに全て登録してあるので、生ぬるく見守ります。

merge

分散型にした一番のメリットはマージ。
多人数で開発するときは、最初はpush --forceを使ってもらって、headを作りまくってもらう。
あとはこちらで全部マージできる。これは安心だ。
でも、この方式ならdarcsのほうがいいのかなー。

まだマージのBest Practiceを見つけられない。
試行錯誤の連続だけど、ツールとしてはextdiff=WinMergeが今のところ楽かな。

2009年6月19日金曜日

Java detect Encoding for Japanese

javaにはエンコード自動認識が標準ではなかったので、探し回った。
* jchardet
* juniversalchardet
などが見つかったけど、とりあえず、標準で逃げる方法。


private final List<CharsetDecoder> decoders;
{
// 色々試した結果、この順序が必須
String[] names = new String[] { "ISO-2022-JP", "EUC-JP", "UTF-8",
"windows-31j" };
decoders = new LinkedList<CharsetDecoder>();
for (String name : names) {
decoders.add(Charset.forName(name).newDecoder());
}
}
public Charset detectEncoding(byte[] bytes) throws Exception {
for (CharsetDecoder d : decoders) {
try {
d.decode(ByteBuffer.wrap(bytes));
} catch (CharacterCodingException e) {
continue;
}
return d.charset();
}
throw new IllegalArgumentException("デコードできませんでした。");
}

public void testDetectEncoding() throws Exception {
String[] samples = new String[] { "平", "カ", "1", "ひ", "b", };
for (String s : samples) {
assertEquals("windows-31j", detectEncoding(s.getBytes("sjis")).toString());
assertEquals("UTF-8", detectEncoding(s.getBytes("utf-8")).toString());
assertEquals("EUC-JP", detectEncoding(s.getBytes("euc_jp")).toString());
assertEquals("ISO-2022-JP", detectEncoding(s.getBytes("jis")).toString());
}
}

2009年5月25日月曜日

timeit on java

SpringのAutoWireの有無による速度差と、CGLIBとProxyの速度差を調べたくなった。
AutoWireの有無による速度差はなし。
CGLIBとProxyも十分無視してよい速度差。

上の目的のためにtimeitみたいに、適当な回数分だけループして時間を計測してくれるツールを作った。
一定の時間を越えるまで回数を増やしてループし続けます。
最初は500msにしたら、テストスイートが終わらなくなったので、50ms。
比較用に交互に実行する機能があるといいかも。

public abstract class Timeit {
protected String name;

public Timeit(String name) {
this.name = name;
}

public void timeit() {
int before = 0;
int count = 1;
long sum = 0;
prepare();
while (true) {
int loop = count - before;
long start = System.nanoTime();
for (int i = 0; i < loop; ++i) {
this.invoke();
}
long end = System.nanoTime();
long current = end - start;
sum += current;
if (current > 50 * 1000 * 1000) {
break;
}
before = count;
count = count * 2;
}
long time = sum / count;
System.out.print(name + ": ");
if (time < 1000) {
System.out.printf("%dns\n", time);
} else if (time < 1000 * 1000) {
System.out.printf("%.3fμs %dtimes\n", time / 1000.0, count);
} else if (time < 1000 * 1000 * 1000) {
System.out.printf("%.3fms %dtimes\n", time / 1000.0 / 1000, count);
} else {
System.out.printf("%.3fs %dtimes\n", time / 1000.0 / 1000 / 1000,
count);
}
}

public void prepare() {

}

public abstract void invoke();

public static class TimeItTest extends TestCase {
public void testSimple() throws Exception {
new Timeit("simple") {
public void invoke() {
try {
Thread.sleep(2);
} catch (InterruptedException e) {
}
}
}.timeit();
}
}
}

Mockito

Mockito便利です。
jMockの変態構文に慣れない方は、EasyMockを使ってらっしゃると思います。
EasyMockのスタイルは素敵ですね。
ただ、人に勧めるときに、replayとかverifyとか、定義以外に文をはさむのってなんかいやですよね。
お勧めしづらいですね。
expectLastCallなんかもみっともない。

そこでMockito


// モック作成
@SuppressWarnings("unchecked")
List mockedList = mock(List.class);

// あらかじめ、何が起こるかを書く
when(mockedList.add("one")).thenReturn(true);
// voidはwhenの引数として受け取れないのでdo*で書き直す。
doNothing().when(mockedList).clear();

// モックに対して実行
assertEquals(true, mockedList.add("one"));
mockedList.clear();


事前に定義する方法のほかに、あとからVerifyする方法もあります。

// モック作成
@SuppressWarnings("unchecked")
List mockedList = mock(List.class);

// モックに対して実行
mockedList.add("one");
mockedList.clear();

// 検証
verify(mockedList).add("one");
verify(mockedList).clear();


呼び出し回数も限定しておきます。

@SuppressWarnings("unchecked")
LinkedList mockedList = mock(LinkedList.class);
// モックに対してメソッド呼び出し
mockedList.add("once");

mockedList.add("twice");
mockedList.add("twice");

mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");

// 回数指定で検証開始
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");

// 呼ばれてない確認。times(0)でもOK
verify(mockedList, never()).add("never happened");

// 細かい数は不要。一度でも呼ばれたらOK
verify(mockedList, atLeastOnce()).add("three times");


すでに存在するオブジェクトに対して、部分的にモックを割り当てます。
名づけて"spy"

// 部分的に書き換える。
LinkedList spyList = spy(new LinkedList());
when(spyList.size()).thenReturn(100);

// addやgetは本来の動き
spyList.add("one");
assertEquals("one", spyList.get(0));

// sizeは置き換えました。
assertEquals(100, spyList.size());

verify(spyList).add("one");


Mock作成が楽しくなりました。
Javaの構文による呼び出し順序を利用したマジックですが、
ここまできれいな構文で書けるなら、受け入れるしかないでしょう。
Mock内で引数に加工したり、詳しく検証する場合は、Anythingというインターフェースを使って実装するのですが、そこはまだありがちな構文。

PowerMockもMockitoに対応中。
まあ、PowerMockなんて、一時的に使うことはあっても、すぐ書き直してMockitoだけで通るように変更するのでいらないけど。

2009年4月8日水曜日

How Fast is JRuby

How JRuby Makes Ruby Fast

def tak x, y, z
if y >= x
return z
else
return tak( tak(x-1, y, z),
tak(y-1, z, x),
tak(z-1, x, y))
end
end

tak(24, 16, 8)を10回分の時間はいかほど?

user system total real
0.524000 0.000000 0.524000 ( 0.524000)
0.338000 0.000000 0.338000 ( 0.338000)
0.325000 0.000000 0.325000 ( 0.325000)
0.299000 0.000000 0.299000 ( 0.299000)
0.310000 0.000000 0.310000 ( 0.310000)

JRubyはええええええ。
と思ったけど、Rubyの速さをよく知らないので、わからない。
あまりベンチマークに興味を持ったことがない。

一応、手元のJRubyでも試したよ。
jruby --server --fast tak.rb

user system total real
1.281000 0.000000 1.281000 ( 1.219000)
0.906000 0.000000 0.906000 ( 0.906000)
0.906000 0.000000 0.906000 ( 0.906000)
0.922000 0.000000 0.922000 ( 0.922000)


とりあえずPythonと比較してみた。
ここはさくっと1秒をきってもらいたいところ。

def tak(x, y, z):
if y >= x:
return z
else:
return tak(tak(x-1, y, z),
tak(y-1, z, x),
tak(z-1, x, y))

python tak.py

4.68799996376s
4.67199993134s
4.71900010109s
4.70300006866s

。。。

気を取り直して、Jython。
まあ、期待してないっす。Pythonの倍ぐらい遅くてもぜんぜん気にしないっす。
だって、お金かかってないっすからね。
Pythonと同じソースが走るのは優秀っすね。
jython tak.py

12.2509999275s
12.1879999638s
12.2030000687s
12.1879999638s

。。。


Ironのじつりきに期待したいけど、ビルドしたことなかった。
いつか誰かがやります。


ところでさ、Javaだとどんなもんかな。
0.3秒が数倍遅いって言ってるんだから、0.08秒ぐらいか。
そりゃはやいね。

static int tak(int x, int y, int z) {
if (y >= x)
return z;
else
return tak(tak(x - 1, y, z), tak(y - 1, z, x), tak(z - 1, x, y));
}

java Tak.java

0.107289151s
0.107325477s
0.107177706s
0.10697179s

JRubyはえええええ!
しかし、ここはJavaのすごいところが見たい!
java -server Tak.java

0.094475588s
0.081646268s
0.081483518s
0.08147525s

やっぱりJRubyはええええ。
ええい、他にオプションはないのか。

というわけでJRubyの速さを際立たせるだけでした。
あ、そうだ。
Dubyが早いならGroovyも型を指定すれば早いんじゃね?
cp Tak.java Tak.groovy
groovy Tak.groovy

6.369433797s
6.266855432s
6.264844057s
6.277156358s

本気か?
事前にコンパイルしないとだめか、そうかそうか。

groovyc Tak.groovy
groovy Tak

6.336249071s
6.235964946s
6.235621352s
6.233301473s

ち、ちょっと早くなったのかしら。

型を指定してあげてるのにこんなに遅いって、Groovyは駄目な子なのか。
ちなみに型を指定しなかったらJythonより遅いのか。
SpringSourceしっかりしろよ。
怖いもの見たさでチェック

def tak(x, y, z) {
if (y >= x)
return z;
else
return tak(tak(x - 1, y, z), tak(y - 1, z, x), tak(z - 1, x, y));
}


3.599673454s
3.504575443s
3.504178545s
3.503208614s

おおおおおお!どうなってるんですか?
速くなりました。
倍近く早いよ。
ってか、Pythonより速い。
Groovyやるな!
ってか、なんで?

ここまできたらScalaも試そうか。

def tak(x: Int, y: Int, z: Int): Int = {
if (y >= x) z else tak( tak(x-1, y, z),
tak(y-1, z, x),
tak(z-1, x, y))
}

scala Tak_scala

0.191174899s
0.185557729s
0.18544463s
0.185426475s

思ったより少しだけ遅いけど、悪くないね。
serverモードはないのかな?


というわけでGroovyの変態さが伝わったかな?
本当はIokeでも試したんだけど、まったく答えが返ってこないのでなかったことにしました。

このまま終わると晩御飯をおいしく食べられないので、Pythonでもうチョイがんばります。
まずはご存知psyco

import psyco
psyco.full()

と先頭につけるだけ。

0.281000137329s
0.296999931335s
0.296999931335s
0.281000137329s

おお、いいね。JRubyに勝った!

つづいてCython

cdef int ctak(int x, int y, int z):
if y >= x:
return z
else:
return ctak(ctak(x-1, y, z),
ctak(y-1, z, x),
ctak(z-1, x, y))


0.155999898911s
0.171999931335s
0.156000137329s
0.155999898911s

よし、Scalaに勝った!


やっぱりPythonだね。

ごらんのスポンサーの提供でお送りしました。
java version "1.6.0_11"
Scala code runner version 2.7.3.final
jruby 1.3.0 (ruby 1.8.6p287) (2009-04-08 r)
Jython 2.5b1 (trunk:5903:5905, Jan 9 2009, 16:01:29)
Python 2.6.1
Groovy Version: 1.6.1

TODO
グラフを貼る


結論。
Groovyは型を指定すると遅くなる。

2009年3月31日火曜日

sax with HTMLParser

expatでShift_JISが扱えればベストなのですが、どうにもならない

<?xml encoding="Shift_JIS"?>

これが書いてあると引っかかるので、本文をutf-8にして回避することもできない。
んがー、どこもめ。
一行目削るしかないかなー。

2.6のxmlパッケージにはexpat以外のXMLパーサがないのが不思議。
xmllibはdeprecatedついてるけど、パーサはまだ生きている。
ココから引っ張るか、あるいはmarkupbaseからがんばるかな。
xml.sax.saxutils.XMLGeneratorを使いたいから、頭が痛い。

saxの構文は面倒くさいし、sax風のXMLFilterを書くのが一番早いかも。

つらい。
意外とPythonは不便。
Jythonに乗り換えたくなってきた。


あと、なんか知らんけどRailsの仕事が舞い降りてきそうだ。
すべて明示的に書いてきたPython文化から、暗黙文化に移るのはかなり抵抗がある。
でも1.9とcapとmerbには興味があるので、ひさしぶりにRuby触るのもいいのかもしれないな。

2009年3月27日金曜日

Real World Django

Real World Django

LJWorldcom運営における体験談か。
Modelをハードコーディングするなとか、拡張性にまつわる話。
スケーリングや構成にまつわる話など、興味深い。
PDFだけじゃなくて、お話も聞きたいなー。
Podcastとか公開してほしい。


昨日、駅前でホットペッパーの人が武富士の人に怒られていた。
何をしたのか知らんけど、駅前ばら撒きの一日の長というやつか。
けんかするなら端っこでしてほしかった。

2009年3月18日水曜日

parrot 1.0.0

http://www.parrot.org/news/2009/Parrot-1.0.0

きっと何かが起こっていると思うけど、何が起こっているのかわからない。
早くつながるといいなー

2009年3月12日木曜日

Seasar2

ためしてみた。
軌道に乗れば確かに結構早いですね。
HotDeployすばらしい。
しかし、開発はGroovyで進めて、
後にテストしながらJavaに移行すればHotDeploy同然なのではないかと思うのです。
ずっと書きやすいし。
Groovy大好きです。

サクサクとそこらじゅうに書いてあるけど、サンプルがちっとも見つからない。
さくさくならそれこそDjangoのように掃いて捨てるほどサンプルが出るはずなのに。
最初のマニュアルはあるけど、何もわからない人向けと、用語がわかる人のどっちつかずで
中途半端。
非常に読みづらい。
だれもサンプルを作ってくれなくても、サクサクならBlogやWikiぐらい作っておいておけばいいのに。

たとえば、EclipseのPluginで必要なものは何かを一望できる場所がない。
JEE対応Eclipse3.3以降で、
・Kijimuna
・Dolteng
・SAStrutsPlugin
・PropertyEditor
が必要と、さっと書いてあれば進めるのに、インストールの仕方を逐次読まなければならない。
初心者に親切だと思うかもしれないけど、書いてあるとおりに進めても完成しない。
突然、状況にあわせて書き換えろと書かれる。

どうせならEclipseもDIもわかる人にSeasarはどう便利なのか訴えるページを書けばいいのに。
ない。


Doltengで作ったプロジェクトはFreemarkerでコンパイルエラー発生しますね。

NetBeansのゆくえ

Tor Norbye's Weblog
JRubyもだめなのかなー。
やっぱりSunってJavaFXに本気なんだね。
NetBeansには期待してたんだけど、完成は遠いですね。
たまにアップデートして試用してますけど、重いし落ちるし。
Torがいなくなるのは残念です。

さて、いつかの安定のために、みんなつかってフィードバックしてあげてください。
dailyがHudsonから落とせて、すぐ試すことができます。
NetBeans Wiki: Python

2009年3月10日火曜日

good bye fabric

paramikoがCTRモードに対応していないので、別れることにした。

どうしてもparamikoを使いたい人、
あるいはIncompatible ssh serverと出てしまう人のための記録。

pycryptoを最新版にしましょう。

paramiko.transport._cipher_info.update({
'aes128-ctr':{ 'class': AES, 'mode':
AES.MODE_CTR, 'block-size': 16, 'key-size': 16 },
'aes256-ctr':{ 'class': AES, 'mode':
AES.MODE_CTR, 'block-size': 32, 'key-size': 32 },
})

Capistranoって便利なのかな。
でも、とりあえずbashでいくわ。

2009年3月2日月曜日

10 minutes topic

http://d.hatena.ne.jp/CortYuming/20090224/p1


def deal(member, data):
"""
>>> deal(3, "123123123")
['111', '222', '333']
>>> deal(4, "123123123")
['12', '23', '31', '12']
>>> deal(6, "012345012345012345")
['000', '111', '222', '333', '444', '555']
>>> deal(4, '111122223333')
['123', '123', '123', '123']
>>> deal(1, '012345012345012345')
['012345012345012345']
>>> deal(6, '01234')
['', '', '', '', '', '']
>>> deal(2, '')
['', '']
"""
from itertools import izip, cycle
a = [''] * member
for d, m in izip(data[:len(data) - len(data) % member],
cycle(range(member))):
a[m] += d
return a


見ないようにしたのに、似たような回答になってしまった。
本当は初期値を準備せずにうまいことやりたかったんだけど。
たとえばreduceは、どんな理論か知りませんが、適切に初期値を準備してくれますよね。
文字列の += もいやな感じ。
行数を増やさずにjoinにしたい。

ワンライナーがかけるようになりたい。

2009年2月17日火曜日

python code reading for WEB

Python力を高めるためのライブラリコードリーディング

私もDjangoからPythonに入った口ですが、
今もDjangoからPythonにはいる人が多いと思います。

まず読みたいのがこの記事。
Why so many Python web frameworks?

いかにフレームワークを作るのが簡単かがわかり、自分で作ってみたくなる。
「ここで、Djangoなんかたいしたことないじゃん。
もっといいのを私は作れるよ!」
といって色々便利なフレームワークを組み合わせてみると、
ほかのフレームワークのよいところがはじめて見えてくる。

こうして色々試すとようやく「仕事」で使う上でのDjangoの魅力をようやく理解できる。
もちろん、Djangoには欠点がある。
contrib.authの融通の利かなさとか、
modelsがややお間抜けとか、
Routingのエラーメッセージとか、
templatetagの仕様だとか、
動的なFormとか。
解決したライブラリををそれぞれ作ってみたけど、個人使用ならともかく公開はできない。
ただ、解決方法をIRCでねちっこくつぶやくだけ。
だって、メンテしたくないから。
もっといい方法を思いついたら飛び移りたいじゃん。


っと、脱線してしまったけど、
とりあえず、最初にDjangoを読むよりは、個別のライブラリを読むほうが入門しやすい。
フレームワーク以前のPythonの言語自体については、
Code Like a Pythonista: Idiomatic Pythonを読んでください。

みんな、Pythonを愛してください。

2009年2月13日金曜日

Tag Filesystem

stowを使って新しいパッケージなどを試験的に導入したりしていますが、
インストールやらアンインストールって面倒です。

ふと、ファイルシステムがタグベースならアンインストールが楽なのにと気づいた。
インストールして、依存ライブラリにタグをつけていけば明快です。
reference countingと同じ。

実行属性やPATHなどの環境変数、シンボリックリンクの問題も解決するのにな。

まあ、タグだけだとファイルの指定が大変になりそうなので、
適切な解決方法が出るまでは階層構造と併用になりそうですが。

TagFSなど、すでに実装もあるみたいですね。

2009年2月12日木曜日

Clojure

InfoQ: JRuby and Clojure - A Good Match?
Clojureの1.0が近い。





WSGIやRakeみたいなものがあると、俄然やる気が出てくる。
LancetはAntがLisp風になっただけ。
Rakeのインパクトには勝てません。
Ringを見てみたけど、サンプルも少ないのでいまいち。
あまりよさそうには見えない。

SinatraみたいにRouteが散ると、多人数でカオスになっちゃうんだよね。

Clojureはまだまだだと思っているけど、使ってみた人いるかな?


個人的にはJVM言語はGroovyが圧倒的にお勧め。
Pythonよりも好きかも。
Javaの大規模開発もGroovyで始めて徐々にJavaに移行するようにすればいいのにと思っている。
今度、Javaで仕事がきたらやってみたいな。

2009年2月3日火曜日

Django for PHP

Pluf
出ました。
いや、出てました。
すばらしい。

テンプレートエンジン以外はPure PHPと言うところがウリの一つです。

まずURL設定。

$ctl = array();
$base = Pluf::f('idf_base');

$ctl[] = array('regex' => '#^/p/([\-\w]+)/review/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Review',
'method' => 'index');

$ctl[] = array('regex' => '#^/p/([\-\w]+)/review/(\d+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Review',
'method' => 'view');

$ctl[] = array('regex' => '#^/p/([\-\w]+)/review/create/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Review',
'method' => 'create');
return $ctl;

うん、まあこんな感じだろうね。
重複も多いし、modelとmethodで分かれているから、少し長い。
あと、分割も標準の方法が見つからないのでもう一工夫ほしいところ。


viewは見慣れたコード。
名前付き引数がないので$match[1]になることと、
名前空間がないので、名前が長くなることがおしい。

public function updateItem($request, $match)
{
$item = Pluf_Shortcuts_GetObjectOr404('Todo_Item', $match[1]);
$new_data = $item->getData();
if ($request->method == 'POST') {
$form = Pluf_Shortcuts_GetFormForModel($item, $request->POST);
if ($form->isValid()) {
$item = $form->save();
$url = Pluf_HTTP_URL_urlForView('Todo_Views::viewList',
array($item->list));
return new Pluf_HTTP_Response_Redirect($url);
}
} else {
$form = Pluf_Shortcuts_GetFormForModel($item, $item->getData());
}
return Pluf_Shortcuts_RenderToResponse('todo/item/update.html',
array('page_title' => 'Update a Todo Item',
'item' => $item,
'form' => $form));
}



テンプレート

{extends 'todo/base.html'}

{block body}
<h2>{$list.name}</h2>

{if $items}
<ol>
{foreach $items as $item}
<li><a href="{url 'Todo_Views::viewItem', array($item.id)}">{$item.item}</a></li>
{/foreach}
</ol>
{/if}

<p><a href="{url 'Todo_Views::addItem', array($list.id)}">Create a new item</a> | <a href="{url 'Todo_Views::updateList', array($list.id)}">Update the list</a></p>
{/block}

すばらしい。
文句のつけようがありません。
まあ、テンプレートエンジンだから当然か。
ところどころPHPの流儀を守っているところがさすがです。

しっかりテストクライアントが付いているところが粋です。

$client->post('/login/', array('login' => 'toto',
'password' => 'secret'));
$client->get('/privatepage/');
$reponse = $client->get($url, array('optional_get_param' => 'toto'));
$this->assertEqual(200, $response->status_code);
// print $response->content;
// print_r($response->template);


モデル

class Todo_Item extends Pluf_Model
{
public $_model = __CLASS__;
// ここに定義を書く
function init()
{
// テーブル名。設定ファイルでprefix設定可能
$this->_a['table'] = 'todo_items';

// クラス名を書く。PHPって自分のクラス名を取れなかったっけ?
$this->_a['model'] = 'Todo_Item';

// フィールド定義
$this->_a['cols'] = array(
// 残念ながらID定義は必要
'id' =>
array(
'type' => 'Pluf_DB_Field_Sequence',
'blank' => true,
),
'item' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 250,
'verbose' => __('todo item'),
),
'completed' =>
array(
'type' => 'Pluf_DB_Field_Boolean',
'default' => false,
'verbose' => __('completed'),
),
'list' =>
array(
// おまちかね。関連設定。verboseがrelated_nameになる。
'type' => 'Pluf_DB_Field_Foreignkey',
'blank' => false,
'model' => 'Todo_List',
'verbose' => __('in list'),
'help_text' => __('To easily manage your todo items,'.
'you are invited to organize your todo items in lists.'),
),
);
}

get_ + lower case name of the related model + _list
で関連リストが取れます。
でもどうでしょうね。
定義も単純だし、Selectも弱いので、こればっかりは様子見か。


FormはDjangoに忠実。
すばらしいです。
これは使いやすい。
ちゃんとFormForModelがあるので安心。
このFormと他ORMのアダプタをいくつか書いたほうがいいんじゃないのかな。
clean_nameなどのValidationも健在。


middlewareもあるし、
Dispatcherも「おっ!」と思うほどシンプル。
さらになんと、CodeIgniterよりも速い!
残念ながら、Djangoとディレクトリ構成が違うため、
規模が大きくなると散らかってしまうかも。
このあたりはLoaderを書き直してほしい。


面白いことに、このソースが配布されている
http://projects.ceondo.com/自体がPlufで書かれています。
git,svn,hgに対応したSourceBrowser,CodeReviewerにIssueTracker。
非常によいサンプルになり、なおかつ、読みやすいことがわかるのでとてもよい。
http://projects.ceondo.com/p/indefero/
この規模がこれだけ読みやすいなら個人的な用途は十分まかなえそうです。
PHPのソースで読みやすいと感じたのは久しぶりです。

これが広まれば、きっとPHPからDjangoへ流れる人が増えるはず。
PHP派もDjango派も目いっぱい布教しましょう。
オススメです。

2009年1月27日火曜日

web2py

これほどまでに万能(変態)の名がふさわしいフレームワークがあるだろうか。

・フルスタック
・1からアプリケーションを開発できるエディタつき万能WEBADMIN
・GUIでER図を描くSQL Designer
・Stan-likeなHTML BuilderとDjangoとPSPのいいとこどりテンプレート
・とりあえずBEAUTIFY
・Railsのflash
・Model連動Form
・SQLAlchemyに対抗心メラメラなORM
・gae-likeモデル連動自動スキーマ変換
・GQL生成するので、GAEでもModel変更不要。
・ついでにCASも提供しておきます。
・Exceptionから自動Ticket発行
・Rails-likeのURL-RoutingにReverseがつき、Regex機能も併せ持つ完ぺき主義。
・request,responseなど、実用性だけを考えた大量のglobalオブジェクト
・ValidatorまでGlobalはマジでやめてくれ、IS_EXPR … 恐ろしい子

必要な機能はすべてそろっている。
後は勇気だけ。

変態だから人に勧めにくい。
GAEで使うフレームワークを探していたときに見つけた。
この豊富な機能は感動。

個人でいくつかGAEように作ったけど、会社では使いたくない。


------
ようやく書いた。
書こうと思ってメモ書きだけして、放置してあるのが多数。
ブログの使い方を間違えているな。

もういいやと思ってメモを消してしまったことも多いし、
メモでもいいからとりあえず公開して、その後追加、あるいはまとめを書くスタイルに変えなければ。


書きかけで放置してあるメモはあと5つ。
・django command拡張
・django-things
・Seasar2
・ipython
・本文抽出

2009年1月23日金曜日

download mass images 3

download mass images 2のつづき

前回で問題としてあがったこと。
1. タプル
2. 出力の乱れ

まず、q.put((download, urljoin(url, a[-1].get('href'))))を書き換えます。
今は引数ひとつしか受け入れられないので、
q.put(func, args, kwargs)

func, args, kwargs = q.get()
になればいいですね。


q = Queue()
def worker(func):
def inner(*args, **kwargs):
print func, args, kwargs
q.put((func, args, kwargs))
return inner

@worker
def download(url):
print 'down '+url
urllib.urlretrieve(url, url[url.rfind('/')+1:])

@worker
def parse(url):
print 'parse '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', href=re.compile(r'^/images/pub/\d+/\w+\.jpg$'))
download(urljoin(url, a[-1].get('href')))

@worker
def images(url):
print 'image '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', {'class':'image_a'}, href=re.compile(r'^/backgrounds/\d+$'))
map(lambda a:parse(urljoin(url, a.get('href'))), a[:4])

def pages(url):
print 'pages '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', href=re.compile(r'^/desktop/\w+\.php$'))
map(lambda a:images(urljoin(url, a.get('href'))), a[:4])

if __name__ == '__main__':
def loop():
while 1:
func, args, kwargs = q.get()
try:
func(*args, **kwargs)
except Exception, e:
print e
finally:
q.task_done()
for i in range(THREAD_MAX):
w = Thread(target=loop)
w.daemon = True
w.start()
pages(sys.argv[1])
q.join()

なんとなく思いついたので、デコレータにしてみました。
これなら、呼び出しがシングルスレッドと同じになります。
引数も可変です。
読みやすいですね。
欠点はシングルスレッドと同じと言うことでしょう。
似たようなものは似た書き方をすべきではないかもしれません。

表示の乱れに関しては、

from threading import Lock
lock = Lock()
def p(s):
lock.acquire()
try:
print s
finally:
lock.release()

ロックされた関数pを使って表示するようにします。
あとは、ご存知loggingモジュールはスレッドセーフなので、
loggingを使うのがもっとよい方法です。

ここまでを統合して、Workerクラスを書くとすれば、
このあたりでしょうか。

class Worker:
def __init__(self, num):
self.q = q = Queue()
def loop():
while 1:
func, args, kwargs = q.get()
try:
func(*args, **kwargs)
except Exception, e:
logging.exception(e)
finally:
q.task_done()
for i in range(num):
w = Thread(target=loop)
w.daemon = True
w.start()
def put(self, func, *args, **kwargs):
logging.debug(e)
self.q.put((func, args, kwargs))
def join(self):
self.q.join()

workers = Worker(THREAD_MAX)

workersになにかputして作業が始まったらjoinで終了を待ってください。

これも大きな欠点があります。
Ctrl+Cで終了させることができません。

さてと、最後にeventletバージョン

from eventlet import coros, httpc, util
util.wrap_socket_with_coroutine_socket()
pages = coros.CoroutinePool(max_size=THREAD_MAX)
def download(url):
a = httpc.get(url)
# aにデータ入ってます。
pages.execute(alljpg, n, depth - 1)
pages.wait_all()



ごめん、最後は面倒なんで手抜き。

2009年1月14日水曜日

django2php

PHPの仕事が来てしまったので、
Djangoと似た環境をPHPで探しました。

Djangoの基本要素は以下の4つかな?
・ORM
・Template
・Routing
・Form
DjangoのFormとRoutingは絶品だと思っています。
実際は両方とも書き換えてしまったので、絶品というのは変だけど、
基本設計は秀逸。今も感動しています。

あとの認証、セッション、Middleware(Filter)あたりは、
最初に書けば、あとはあまりいじらないから適当なライブラリから引用すればOK。

あとはディレクトリ構造。
appごとに分けられるDjangoは編集範囲が限定されていて見通しがよい。
rails風はコントローラとモデルが遠くて大変。

ORM


Propel
Doctrine
がいいらしい
Comparing Propel, Doctrine and sfPropelFinder
これはよい比較

YAMLでSchemaを書けて、HQLみたいなのもあるDoctrineがいい感じ。
でもやっぱりDjango-ORMがほしい。

途中、DoctrineでfindOneByTitleという、いかにもmethod_missingを使いました的な、メソッド名を発見。
調べてみたら、__callなんてあるんですね。
面白い。

Propelをメソッドチェーンで動くようにするなど拡張したsfPropelFinderもなかなか努力のあとがにじみ出ていていいですね。

php-django-like-db-abstraction/
「素晴らしい!」と思って、開いたら空っぽでした。
名前だけ取って手をつけない人は、それがどれぐらい迷惑な行為か早く学んでほしい。

template


生PHPが流行らしいです。
レイアウトとかどうするんだろう。
ヘッダとフッタを別々に読み込むの?

form


symfonyのformはDjangoと似ていていいですね。
まだ面倒に感じるのはMetaclassがないからかな?
単体で探したけど、似たものはないので、symfonyを使うか、単体用に取り出すか実装するか。
あるいは別の道を探すか。
inputタグなどを出力するためのHelper関数は多く準備されているようだけど、
ValidationやDBからの出力をこちらで考慮しなければならないのは面倒。
DjangoのFormは優秀。

Routing


Rails-likeが多いですね。
/controller/action/id
というやつね。
正規表現Routing、おまけに間接参照のやつがあれば素敵。
難しいものじゃないし、移植しようかな。

あと、ほしいのがpythonのimport
パスを考慮しながらのrequire_onceは難しい。
あと、まとめて数ファイルimportしたいときもある。

統合するとこんな感じか
urls.php

import('bloggy.views.*');
import('bloggy.form.EntryForm');

add_patterns(array(
'' => 'index',
));
add_patterns(array(
'new' => 'EntryForm.create',
'entry/(?P[0-9]+)' => 'EntryForm.edit',
), 'admin_required');

forms.php

class EntryForm extends Form {
var $conf = array(
'name' => Form::TextField(),
);
}

PHPの構文が分からない、これでコンパイルとおるかな。
EntryFormをimportしたときに継承して拡張したEntryFormを渡すことってできるのかな。
同名だから無理かな。
じゃあ、定義は_EntryFormか。

やだねー、ほかの言語仕様に縛られて無理やり持ち込む人。
もっと柔軟になったほうがいいと思います。
symfonyおぼえよっと。

あと、Kohanaも結構優秀でした。
程よいimport(loading)、routing

2009年1月13日火曜日

download mass images 2

download mass images 1のつづき

トップページから巡回して4ジャンルから3枚ずつのリストの取得。

def parse(url):
print 'parse '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', href=re.compile(r'^/images/pub/\d+/\w+\.jpg$'))
return urljoin(url, a[-1].get('href'))

def images(url):
print 'image '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', {'class':'image_a'}, href=re.compile(r'^/backgrounds/\d+$'))
return map(lambda a:parse(urljoin(url, a.get('href'))), a[:3])

def pages(url):
print 'pages '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', href=re.compile(r'^/desktop/\w+\.php$'))
return map(lambda a:images(urljoin(url, a.get('href'))), a[:4])

print pages(sys.argv[1])

前回の設定を引き継ぎ、上記のようなコードを書けば一覧が取得できます。
% python wally.py http://www.backgroundsarchive.com/desktop/|xargs -n1 -P6 wget
このコマンドがが動いて、取得してくるように変更することは難しいことではないでしょう。

しかし、このコマンドは遅いですね。
ダウンロードリストを生成する時間がかかりすぎです。
Python内でもスレッドを使いたい。


そして本題
これが書きたかっただけですが、前置き長い。

まずは、Queue。
Queue#getが呼ばれると待ちます。
中身が入るまで、死ぬまで待ち続けます。
中身が入ったら、嬉々として値を返します。
格納できる値はひとつずつです。
複数の値を入れたい場合は、タプルを使います。
これを利用して、Queueに呼び出してほしい関数と引数をタプルにして詰め込んでやります。
そして一定数のスレッドを、Queueから取り出しては実行する無限ループにします。
これでThreadPool完成です。
実装してみましょう。

import sys, re, urllib
from urlparse import urlparse, urljoin
from Queue import Queue
from threading import Thread, Lock
from BeautifulSoup import BeautifulSoup
THREAD_MAX = 6
class FFURLopener(urllib.FancyURLopener):
version = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5'
urllib._urlopener = FFURLopener()
q = Queue()

def download(url):
print 'down '+url
urllib.urlretrieve(url, url[url.rfind('/')+1:])

def parse(url):
print 'parse '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', href=re.compile(r'^/images/pub/\d+/\w+\.jpg$'))
q.put((download, urljoin(url, a[-1].get('href'))))

def images(url):
print 'image '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', {'class':'image_a'}, href=re.compile(r'^/backgrounds/\d+$'))
map(lambda a:q.put((parse, urljoin(url, a.get('href')))), a[:3])

def pages(url):
print 'pages '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', href=re.compile(r'^/desktop/\w+\.php$'))
map(lambda a:q.put((images, urljoin(url, a.get('href')))), a[:4])

if __name__ == '__main__':
def loop():
while 1:
func, args = q.get()
try:
func(args)
except Exception, e:
print e
finally:
q.task_done()
for i in range(THREAD_MAX):
w = Thread(target=loop)
w.daemon = True
w.start()
pages(sys.argv[1])
q.join()

これで4ジャンルから3枚ずつ最大サイズを取得します。

問題がいくつかあります。
1. タプル
2. 出力の乱れ

まずタプル。
読みづらいですね。
私はたいてい括弧を読み飛ばしてしまい、悩みます。
あと、引数の数の変更にも対応できません。

q.put((func, [a1], {}))

func, args, kwargs = q.get()
func(*args, **kwargs)
にすれば引数に対応できますが、煩雑です。
ここは、ラッパーを書きましょう。

次へ続く

rst2a

django-lockのトップページで使われていたのを見て知った。

rst2a

reStructuredTextをHTMLやPDFに変換してくれるサービス。

そこのAPIを使ってこんなブックマークがあると便利。
rst2html

2009年1月9日金曜日

download mass images 1

突然壁紙がたくさんほしくなったので、
壁紙がいっぱい集まっているところからちょいといただきます。

少し眺めていれば、以下のURLのように連番が発見できます。
http://www.backgroundsarchive.com/backgrounds/9124

まあ、ざっと見て20000以下なので、そこからbashを使います。
まずは10ほどで試してみましょう。
% echo http://www.backgroundsarchive.com/backgrounds/{1..10}
なんかたくさん表示されましたね。
OK、続けて
% echo http://www.backgroundsarchive.com/backgrounds/{1..10}|xargs -n1 echo
Bravo! これで分かりましたね。

あとはBeautifulSoupでいただいてしまいましょう。

import sys, re, urllib
from urlparse import urlparse, urljoin
from BeautifulSoup import BeautifulSoup

def download(url):
print 'down '+url
urllib.urlretrieve(url, url[url.rfind('/')+1:])

def parse(url):
print 'parse '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', href=re.compile(r'^/images/pub/\d+/\w+\.jpg$'))
map(lambda a:download(urljoin(url, a.get('href'))), a)

if __name__ == '__main__':
parse(sys.argv[1])

wally.pyとでも命名して即実行。
% time echo http://www.backgroundsarchive.com/backgrounds/{1..10}|xargs -n1 python wally.py
echo http://www.backgroundsarchive.com/backgrounds/{1..10} 0.00s user 0.00s system 42% cpu 0.001 total
xargs -n1 python wally.py 1.52s user 0.84s system 3% cpu 1:16.55 total
うーむ、信じられないほど遅い。
ブラウザだと少し早いからUAによる制限でしょうか。
まあ、しょうがない。

同時にダウンロードしちゃえば早いじゃんというのは自然な発想です。
コネクションが多すぎるとダメって聞いたことがあるので6つぐらいにしましょう。
% time echo http://www.backgroundsarchive.com/backgrounds/{1..10}|xargs -n1 -P6 python wally.py
echo http://www.backgroundsarchive.com/backgrounds/{1..10} 0.00s user 0.00s system 40% cpu 0.001 total
xargs -n1 -P6 python wally.py 1.48s user 1.45s system 11% cpu 26.645 total
これで六本同時に走ります。
うん、多少早くなりましたね。

念のため、User-Agentをかえてみます。
こんなのをimport urllibの下に書いておけばいいでしょう。

class FFURLopener(urllib.FancyURLopener):
version = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5'
urllib._urlopener = FFURLopener()

そして実行
% time echo http://www.backgroundsarchive.com/backgrounds/{1..10}|xargs -n1 -P6 python wally.py
echo http://www.backgroundsarchive.com/backgrounds/{1..10} 0.00s user 0.00s system 45% cpu 0.001 total
xargs -n1 -P6 python wally.py 1.52s user 1.32s system 82% cpu 3.428 total
げええ。
というわけで最適化に教科書なし。
遅いところに適切に対応しましょう。

さて、単純な連番ならこれでいいのですが、今回は全件取るつもりはありません。
序盤を集めたところマーブルばっかりで、動物とか風景がもっとほしい。
トップ画面からクロールしてほしい画像の一覧を作ったほうが好みの画像が集まりそうです。
トップページを見ると21ジャンルほどありますね。
巡回して21ジャンルから100枚ずつぐらいのリストを作るプログラムを書いて、
% python wally.py http://www.backgroundsarchive.com/desktop/|xargs -n1 -P6 wget
でうまくいきそうです。

予想外に長くなってしまってので次回へ続く。

2009年1月8日木曜日

Ajax file upload

byteflowのデコレータから引用していたけど、FileUploadに対応させました。

Ajaxでは
request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
が成り立つのですが、FileUploadはiframeを使いますのでつきません。
iframeでsubmitした場合、application/jsonにすると、ダウンロードが始まってしまうため、逃げます。

def ajax_request(func):
def wrapper(request, *args, **kw):
if request.method == 'POST':
response = func(request, *args, **kw)
else:
response = {'error': {'type': 403,
'message':'Accepts only POST request'}}
if isinstance(response, dict):
if request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest':
class JsonResponse(HttpResponse):
def __init__(self, data):
super(JsonResponse, self).__init__(
content=simplejson.dumps(data),
mimetype='application/json')
return JsonResponse(response)
else:
# for file upload
class IframeResponse(HttpResponse):
def __init__(self, data):
super(IframeResponse, self).__init__(
content=''%simplejson.dumps(data))
return IframeResponse(response)
else:
return response
return wraps(func)(wrapper)


jquery.form.jsを使うと、具合が大変よろしいです。

$('#image_form').ajaxForm({
dataType: 'json',
beforeSubmit: function() {if (!$('#icon').val()) return false},
success: function(data) { render(data) },
resetForm: true
})

dataTypeは'json'で。
beforeSubmitの第1引数のformDataはnameで引けないので不便。
第2引数のjqFormで、jqForm[0].icon.valueでもOK。
もっといい指定方法は知りたい。
clearFormがなぜか効かなかったのでresetForm。

jquery.blockUI.jsで画像選択やアップロード画面を出すときれい。

jquery.js必須

ブログ作成3個のヒント

10 Killer WordPress Hacks

1. 広告は検索エンジンからの訪問にのみ表示しろ
ブックマークから来る人は広告なんて見ない、押さない。
でもどうせRSSで読むから関係ないけど。

2. 連投を防げ
連投かっこ悪い。
でもどうせならタイトルをSlugFieldとかにして、同じタイトルのものがないほうがインデックスとかもろもろで気持ちいいな。

3. NextやPrevはやめて、Paginationにしよう。
1,2,3,...Lastみたいなやつね。
AutoPagerが効かなくなる可能性がありそうだけど、<link rel="next">とかに対応してくれたら幸せだね。

など。
ほかはどうでもよかった。