2007年12月20日

Googleを使ってMD5を逆変換

Rauru Blog » Blog Archive » MD5破りにGoogleを活用という記事を見て、試してみました。
>>> import md5
>>> md5.md5("Python")

>>> _.hexdigest()
'a7f5f35426b927411fc9231b56382173'
確かに「a7f5f35426b927411fc9231b56382173」で検索してみるとPython関連のサイトがヒットしますね。

Pythonはまぁ蛇という意味で一般名詞としても使われるので「YRAV」で試してみたところこんなサイトが…:The MD5 Collision Database: Starting with YAR

「Python」の代わりに「Pyth0n」にしてみると何もヒットしませんでした。結局「十分長く」「辞書的単語でない」単語を使わなければ簡単に逆変換できてしまうと言うことですね。

2007年11月26日

自分がBruteForceBlockerに引っかかってしまったときの対処

どう書く?orgではBruteForceBlocker を使ってブルートフォース攻撃を仕掛けてくるIPアドレスからのパケットを捨ててしまうようにしているのですが、今日変なことをしていたら自分がブロック対象になってしまいましたorz

対処方法:

  • 別のIPアドレスからログインする
  • /var/db/ssh-bruteforceに記録されている自分のIPアドレスを削る(もしここにデータファイルがなければ、ps -xでbruteforcebrockerの場所を調べ、中を見て設定ファイルの位置を調べ、その設定ファイルを見ると書いてある)
  • /etc/rc.d/pf restart でパケットフィルタを再起動する

あんまり変なことをしないように気をつけないと…(^^;

2007年11月22日

Python4JS and KEMURI Interperer on it

OreScript時代の幕開け - yukobaの日記はとても面白い。

ので、

Pythonで書かれているKEMURI のインタプリタを<script type="text/python4js">で囲ってHTMLソースに書いておくと、それがJavaScriptに変換されて実行される、というデモを作ってみました。

Python4JS and KEMURI Interperer on it

ラボでは他にも JSRuby - Javascript による Ruby(っぽい)実装 (nakatani @ cybozu labs) などが公開されていますが、僕のこのデモは深夜ノリで作ったため、Python2.5の全機能をサポートしているわけではありません。今後改良していく予定もありません。ごめんなさいごめんなさい。ソースコードを見て石を投げないで!

2007年11月12日

大きなテーブルへのForeignKeyにはraw_id_admin=True

どう書くorgは Djangoを使って作られていて、 下のような管理画面が

下のようなモデルの定義から自動的に生成されます。

class Comment(models.Model):
    topic = models.ForeignKey("Topic", null=True, blank=True)
    parent = models.ForeignKey("self", null=True, blank=True)
    create_date = models.DateTimeField()
    author = models.ForeignKey(User, null=True)
    body = models.TextField(blank=True)

すごく楽ちんだったのですが、どう書くorgがにぎわってコメント数が増えてくるに従って どんどん重たくなってきました。

理由はとても簡単で、この自動生成されたリストの部分で、 数千件あるすべてのコメントをデータベースから取ってきて文字列にする処理が行われるからです。

↑数千件がリストの中に…

このせいでページを開くだけで30秒程度かかっていました。 この問題はどうすれば解決できるか。 実はすごく簡単で、フィールドの定義にrw_id_admin=Trueをつけるだけでした。

-   parent = models.ForeignKey('self', null=True, blank=True)
+   parent = models.ForeignKey('self', null=True, blank=True, raw_id_admin=True)
このオプションをつけるとすべてを読み込んでリスト表示する代わりに、 下のようなIDを入れる欄と虫眼鏡アイコンが表示されます。

虫眼鏡アイコンを選ぶとアイテムの一覧が別ウィンドウで開きます。

テーブルが大きくなってくるとリスト表示では探すのも面倒になるので、 大きくなりそうなテーブルへForeignKeyを張っているなら今のうちにraw_id_adminをつけておくのも手かもしれません。

2007年10月29日

「どう書く?org」(ベータ版)を公開しました

どう書く?orgというサービスを公開しました。

どう書くorgについてオフィシャルに言及するのは初めてなのですが、 アルファ版の時点ではてなブックマークのホットエントリーになったり、 オレンジニュースやじうまWatchで言及していただいたりしたので すでにご存じの方もいらっしゃるかもしれません。 そこでこの記事では今まで語ってこなかった「どうして『どう書くorg』を作ろうと考えたのか」について解説したいと思います。

そもそものきっかけはマルチリンガルなシソーラス検索 - rubyco(るびこ)の日記でした。 言語Xを使っている人が新たに言語Yを使うときになにが障害になるか、というと、 やはり「言語Xでならどうやるか0.1秒で思いつくような簡単な内容を、 言語Yではドキュメントをひっかきまわして探さないといけない」というフラストレーションなわけです。 そのフラストレーションを避ける気持ちが、プログラマを「自分が一番慣れている言語」という檻に閉じこめるのです。 プログラミング言語間の対訳があれば、殻を破ってマルチリンガルの大空に羽ばたくことができるのに。

さて、この対訳マルチリンガル・クックブックを作る上で、 まずは「Wikipedia的なアプローチで言語間の対訳データが集められないか」 と考えPukiWikiで集積サイトを作りました。 しかし「これは絶対に盛り上がらない」と判断して公開しませんでした。 単なるWikiではコードを書いてもらうインセンティブに欠けると思いました。

その後、どういう流れがあったのかは正確には覚えていません。しかし、いつしか キミならどう書く 2.0 - ROUND 1 - Lightweight Language Ring のようなアプローチをもったいないと感じるようになりました。 お題に対してコードを書いている人のほとんどは、そのコードの再利用を禁止したいとは思っていないはずです。 なのに、許可を明示的に取らないせいで実質的に再利用できずに散逸させてしまっています。 せっかく無償でいろいろな言語のコードを書いてもらうことができているのに。これはとてももったいないことです。

このようにして「どう書くorg」の三本柱「マルチリンガル志向」「お題にチャレンジ」「再利用可能なライセンス」ができました。 みなさんのおかげで、7月頭にどう書くorgを公開してから今までの4ヶ月弱で、2796件のコードが投稿されています。 1日平均20件以上という計算になります。

今後の課題は、こうやって集まったコードをどう加工するかだと思います。 指定したコード中から特定の文字列を検索して、同じお題の別の言語のコードを表示する機能 を3ヶ月も前に実装したのですが、これは現状の「お題と解答」というシステムではコードの粒度が大きすぎてなかなか望み通りのものが得られません。 ソースコードを静的に解析することで使われているモジュールを推定する機能(どう書く?org Python 使用モジュール一覧) がついたので、次は「同じお題で使われることの多いモジュールは似た機能のモジュール」というような推定でしょうか。 それとも、お題にするには粒度が小さすぎるような内容を 文字列中のアルファベットを大文字にする どう書く?org のような公開トピックとしてどんどん作ることで粒度の小さいコードを増やすのがいいのでしょうか? まだまだ先が見えません。

しかし、たとえどう書くorgプロジェクトが道半ばで頓挫しても、 集まった「自由に再利用できる」コードは他のプロジェクトの肥やしになるはずです。 進めるところまで進んでみるのが正解かな、と思っています。

長々と昔話や妄想を書きつづってきましたが、まとめたいと思います。 どう書くorgは今まで「出されたお題をいかに解くか競い合う、プログラマのためのコロシアム」と名乗っていました。しかし「プログラマのためのコロシアム」なのは手段であって目的ではありません。どう書くorgの目的は「マルチリンガル・クックブック」を自然発生させることなのです。

2007年10月17日

Pythonで関数名を安全に変更する

Pythonのような動的な言語では、Javaのように「メソッドの名前を呼び出しも含めて変更」というリファクタリングが簡単ではありません。整合性をチェックするフェーズがないので、不用意に名前を変更すると実行時に「そんな名前の関数はない」と怒られてしまいます。

そこで「一度deprecated(非推奨)にする」という方法を考えました。

まず下のようにfooという関数と、それを読んでいるcallerという関数を作りました。今からこのfooをbarという名前に変更したいと思います。 しかし、実際にはcallerは大量のソースの中に散らばっているものとします。

>>> def foo():
	return 1

>>> def caller():
	return foo()

fooの名前を変更します。ここではbar = fooとしていますが、もちろんdef foo...をdef bar...に書き換えるのでもOKです。実際は後者になるでしょう。 そして、fooという名前で改めて警告メッセージを出してbarを呼ぶ関数を定義します。
>>> bar = foo
>>> def foo():
	import inspect
	print "'foo' is deprecated: called from", inspect.stack()[1][3]
	return bar()
こうすると、うっかりcallerの中のfooの呼び出しをbarの呼び出しに変え忘れても:
>>> caller()
'foo' is deprecated: called from caller
1
変える値は変わらず、警告メッセージだけが表示されるようになります。警告は今はprintで出していますが、ログに出すなりなんなりするといいと思います。
追記: 毎回こういうことを書くのも面倒なので関数にまとめました。
>>> def deprecated(f):
	def deprecated_func(*args, **kw):
		import inspect
		print "'%s' is deprecated:" % f.func_name,
		print "line %s in '%s'" % tuple(inspect.stack()[1][2:4])
		return f(*args, **kw)
	return deprecated_func

>>> def nibai(x):
	return x * 2

>>> def foo():
	print nibai(10)
	print nibai(20)

	
>>> foo()
20
40
>>> nibai = deprecated(nibai)
>>> foo()
'nibai' is deprecated: line 2 in 'foo'
20
'nibai' is deprecated: line 3 in 'foo'
40

2007年10月16日

Pythonの辞書をドットでアクセス

Pythonの「辞書」はとても便利なデータ構造ですが、「x」というキーに関連づけられている値を取り出すのには「['x']」とキー以外に4文字も書かなければ行けなくて面倒です。
>>> p = dict(x=1, y=2)
>>> p
{'y': 2, 'x': 1}
>>> p['x']
1
JavaScriptみたいにp.xでアクセスできると便利です。 そこでp.xでアクセスできるようにする関数を作りました。
>>> p = modulize(p)
>>> p.x
1
実装はこちら:
>>> def modulize(dictionary):
	import imp
	m = imp.new_module("")
	m.__dict__.update(dictionary)
	return m
なお、modulizeの時点で辞書がコピーされているので、modulize済みのオブジェクトのメンバを書き換えても元の辞書は変化しません。
>>> p = dict(x=1, y=2)
>>> p2 = modulize(p)
>>> p2.x = 100
>>> p
{'y': 2, 'x': 1}
変更済みの辞書を取得するには下のように__dict__を使います…ってモジュールにしたせいで__doc__とかが勝手に追加されていますね…
>>> p2.__dict__
{'y': 2, '__name__': '', '__doc__': None, 'x': 100}
…すなおに下のように書いた方がいいかも。
>>> class MyDict(dict):
	__getattr__ = dict.__getitem__
	__setattr__ = dict.__setitem__
	__delattr__ = dict.__delitem__

	
>>> MyDict()
{}
>>> d = _
>>> d.x = 1
>>> d
{'x': 1}
>>> d.x
1
>>> del d.x
>>> d
{}

2007年09月19日

続:typoを素早く察知する方法

先日公開した Pythonでtypoを素早く察知する方法の続編です。 前回公開したものは 「codeオブジェクトを破壊的に書き換えるCodeHackモジュール」 を流用して作りましたが、typo察知にCで書かれたモジュールは必要ないので、 コードオブジェクトの解析部分だけを切り出してPure Pythonのモジュール 「Coda」を作成しました。

前回は 「関数を与えると、その関数を呼び出さずに、その関数が起こしそうなNameErrorを警告する」 というものでしたが、今回はモジュールとネストしたコードオブジェクトをサポートしました。 ファイル名を指定して、そのファイルが起こしそうなNameErrorを警告させることが可能です。

たとえば下のような内容のファイル(tests/test_target.py)があった場合に

def some_func():
    pass

def foo():
    print some_finc()

find_name_error.pyを実行すると下のように 「5行目のsome_fincは、それより外側のスコープでLOADされていないよ、typoじゃない?」 と表示されます。
C:\Python25\Lib\site-packages>find_name_error.py coda\tests\test_target.py
line 5: some_finc

使い方:まずScriptsフォルダに入っているfind_name_error.pyを C:\Python25\Scriptsなどのパスの通っている場所に移動してください。 このとき、名前は適当に変更しても構いません。

適当なフォルダに移動して-hというオプションで起動してヘルプが出ればOKです。

C:\>find_name_error.py -h
Usage: find_name_error.py [options]

Options:
  -h, --help            show this help message and exit
  -r DIR, --recursive=DIR
                        find *.py under the directory
  -w, --watch           watching target continuously(not implemented yet)
表示されているヘルプが多少事実と異なりますが(直しておきます)、 引数にファイル名を与えるとそのファイルを、-rとフォルダ名を与えるとそのフォルダ以下の*.pyファイルを走査します。-wをつけるとファイルの更新を監視します。

ソースコードは共有リポジトリ CodeRepos::Share で公開中です。 Subversionがインストール済みであれば、たとえば 下のようにしてPythonのsite-packages内でチェックアウトするだけでOKです。

C:\Python25\Lib\site-packages>svn co http://svn.coderepos.org/share/lang/python/coda/trunk/coda

Subversionを入れたくない人のために一応ZIPで圧縮したバージョンも置いておきます。

Coda rev 228

既知の問題点はこちら: /lang/python/coda/trunk/PROBLEMS_JA.txt - CodeRepos::Share - Trac

鮎川さんからご指摘のあったユニコード文字列のstrでエラーになる件は、 使っているエンコーディングによってエラーになったりならなかったりするので、 近いうちにユーザがエンコーディングを指定できるようにします。

2007年08月14日

Pythonでtypoを素早く察知する方法

先日「スクリプト言語は便利だけど、 長時間計算した後で綴り間違い(typo)で落ちたりすると やる気がなくなるよね」 という話を聞きました。 たしかに、綴り間違いで落ちるのは悲しいです。 せめて「代入されたけども読み出されていない変数」とか 「代入される前に読み出されている変数」が出力できれば、 早めに綴り間違いに気づくことができますね。

そこで「先日公開したCodeHackモジュールを使えば簡単にできるよ」と言いながら作ったのが今回紹介するモジュールです。 CodeHack 0.05に同梱しました。 CodeHack 0.06に同梱しました。

使い方は簡単です。 たとえば下のような関数fooを定義したとします。

def foo(left, top, right, bottom):
    "function for test"
    width = right - left
    hieght = top - botom
    print height, width
よく見ると2カ所ほど綴り間違いがあるのですが、 この関数をcodehack.var_warningの中のcheckfuncに渡してみると 下のようにその綴り間違いの変数が表示されます。関数fooを実行していないので、たとえ実行に1時間かかるような関数であってもチェックは一瞬です。
>>> from codehack.var_warning import checkfunc
>>> checkfunc(foo)
variable 'botom' is global
variable 'height' is global
variable 'hieght' is not used
variable 'bottom' is not used
実は綴り間違いの判定は何もしていなくて、 単純に「値が代入されたのに読み出されていない」 という変数を「not used」と表示しているだけです。 また、逆の「値が代入されていないのに読み出されている変数」は Pythonでは「グローバル変数と見なして外のスコープを見に行く」 という仕様になっているのでglobalと表示しています。(注1)

ただ、そのままですと'range'や'set'なども警告されたり、 自分で定義したグローバル関数を使うとそれも警告されたり、 とうっとうしいことになるので、 警告しない名前のリストをsurpressという引数で指定できるようにしてあります。 デフォルトでは組み込みの名前(rangeとか)がsurpressに入っています。

def checkfunc(func,
          print_globals = True,
          print_not_used = True,
          print_func_name = False,
          surpress = _default_surpress):

CodeHackモジュールを使っている部分は、実はたいしたことはしていません。 下のようにLOAD_FAST(ローカル変数のロード)という オペコードの引数(変数名になっている)を集めているだけです。 codehack.util.analyzeはオペコード間の依存関係(どのオペコードが積んだ値をどのオペコードが消費するか)のトレースをするのですが、それも必要ないのでわざわざ外しています。

    import util
    opcodes = util.analyze(func, do_trace=False)

    loaded_vars = set(op.argstr
                for op in opcodes
                if op.opname == "LOAD_FAST")
下のようにして、モジュールの中の全ての関数をチェックすることもできます。 すでに自分で書いたモジュールがあれば試してみると面白いでしょう。 checkallはprint_globalsをFalseにしてあるので「代入されているけど読み出されていない」変数だけが出ます。
>>> from codehack.var_warning import checkfunc, checkall
>>> import urllib
>>> checkfunc(urllib.urlopen)
variable 'FancyURLopener' is global
variable '_urlopener' is global
>>> checkall(urllib.__dict__)
* function splitnport
* function getproxies
* function retrieve
variable 'msg' is not used
variable 'garbage' is not used
(snip)
ぜひ試してみて、ご意見ご感想やパッチを頂けるとうれしいです。

(注1) 実際はもちろんグローバルの名前空間にいきなり行くわけではなくて、 スコープがネストしている場合には1枚外のスコープへ行きます。 globalと呼んでいるのは単にバイトコードがLOAD_GLOBALだからだったりします。 いい名称があれば変えたほうがいいかもしれません。 また、関数がネストしている場合は内側の関数で使われる変数を 「使われる」と判断することができずに警告を出してしまいます。 これはdisしてみるとわかるのですが、内側の関数はコードオブジェクトとして 定数テーブルに埋め込まれているため、 外側の関数のバイトコードを見ただけでは内側で何を使うかがわからない ことが原因です。定数テーブルにコードオブジェクトがあるかどうかをチェックして 再帰的にたどるようにすればいいだろうと思いますが…それは今後の反響次第と言うことで。

2007年06月12日

Pythonコード添削道場

来る6月30日、東大駒場キャンパスでPython Workshop the Edge 2007が開催されます。

「Pythonコード添削道場」はこのワークショップのセッションの一つで、私、西尾 泰和増田 泰さん柴田 淳さんの3人で投稿されたコードを添削するという企画です。

この企画はみなさんにコードを投稿して頂かないと始まりません。 コードの内容は自由なので、ぜひご投稿ください。

また「投稿してといわれても、ちょうどいいコードがないなぁ」 という人のために、お題が7つ用意してあります。 お題に挑戦してみて結果のコードを投稿するのもよいでしょう。

コードの投稿の際に名前を書く必要はありません。 完全に匿名でも、自分にしかわからないような ハンドルネームでもOKです。 その方が添削する我々も、容赦なく添削ができます。

自分のコードが容赦なく添削される機会は、 そうそうあるもんじゃないですよ! ぜひこのチャンスをものにして 理解を深めるきっかけにしてください。

お題は以下の7問です。

  • お題1:ファイルの同期
  • お題2:単語数カウント
  • お題3:シングルトン
  • お題4:入れ子リストの中身を順に表示
  • お題5:行列の回転
  • お題6:名簿の並び替え
  • お題7:整数とビット列の相互変換
詳細はこちらからどうぞ→Pythonコード添削道場