2007-08-30

[メモ][TODO]Django+SQLAlchemy調べ物

以下を調べること。

djangoをSQLAlchemyで使う場合、
* djangoのsqlalchemyブランチとやらは必須なのか?
* model.pyをいきなりsqlalchemyの記述に差し替えて、影響範囲はどれぐらい?
 * 認証とか大丈夫?
 * 汎用ビューはあきらめる?
 * adminツールもあきらめないといけないのか?
追記 ---
http://django.g.hatena.ne.jp/perezvon/20070211
この日記の記事が参考になる。
やっぱりユーザー認証は独自実装ですか、、、。
しかし、SQLAlchemyを使うことで、これほどまでに失うものが多いのであれば、いったいそもそもdjangoである必要があるのかどうかという疑問がでてきた。

sqlalchemyブランチを取ってきたがsqlalchemy使ってるかどうかさえ不明な感じ。(ひょっとしてただ本家をコピーしてきただけ?)
結局自分でなんとかしないといけないみたい。

2007-08-29

Directory Queue便利そう。

Directory Queue
ファイルベースのキュー実装。

ファイルベースなので、データはファイルの中に書く。
ファイルベースなので、途中でプログラムがクラッシュしても、キューの中身は残るんだろうか。(きっとそうだよね。。)
面白いのは「メタデータ」を別途持たせることができること。

これはJMSのMessageの仕様を参考にしているのかな?

こういうプロジェクトがきちんと育ってくれたら、自分が今の仕事で使っているJMS地獄からいくぶん解放されるんじゃないだろうか。

追記---
「ファイルベース」というよりは、「ディレクトリベース」というほうが適切か。
キュー内の1アイテムは、1ディレクトリで表されている。そのディレクトリの中に、データを表す"file.data"というファイルと、メタデータを表す"meta.data"というファイルを置くようだ。

キューからのアイテムの取得は、os.listdirでディレクトリ内の一覧を取得して、ディレクトリ名のソートで先頭を取得するようだ。
これはキュー内に大量のメッセージを格納したときにパフォーマンスが心配である。
とは言っても、メッセージが大量にならない場合には問題ないだろう。

[メモ]SQLAlchemyのPrecompiling a Query

Precompiling a Query

SQLAlchemyであらかじめSQLを構成しておいて引数だけを差し替える方法。
ループの内側でSQLをいちいち作り直す時間をはぶきたいときに使えそうだ。

2007-08-28

this.pyのソースは文字を巡回させているのに気づいたよ。

こんなふうに。

s = """Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
Pbzcyrk vf orggre guna pbzcyvpngrq.
Syng vf orggre guna arfgrq.
Fcnefr vf orggre guna qrafr.
Ernqnovyvgl pbhagf.
Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
Nygubhtu cenpgvpnyvgl orngf chevgl.
Reebef fubhyq arire cnff fvyragyl.
Hayrff rkcyvpvgyl fvyraprq.
Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
Abj vf orggre guna arire.
Nygubhtu arire vf bsgra orggre guna *evtug* abj.
Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"""

d = {}
for c in (65, 97):
for i in range(26):
d[chr(i+c)] = chr((i+13) % 26 + c)

print "".join([d.get(c, c) for c in s])

2007-08-24

django一日目の感想

今日は数時間ほどユーザー登録周りのコードを書いていた。
こうしてチュートリアル以外で、まともにdjangoをさわるのはほぼ初めてです。

基本的にはdjango-registrationを、公式リリースであるdjango0.96で使えるように改造する作業。

いくつか気づいた点

* djangoのデフォルトのテーブルauth_userのフィールドemailはuniqueではない。なので、同じメールアドレスのユーザーをじゃんじゃん登録できる。
* ↑これは別にいいんじゃないと思うが、こうなっていると、複数ユーザーが同じemailアドレスを使っている場合パスワードのリセット機能が動かない。(これはすでに公式のバグレポートに上がっているようだ。)
* なので、登録時にemailの重複チェックを追加実装する必要がありました。初心者なので汚い実装方針でしたが一応チェックできるようになった。

* パスワードリセットフォームがadmin標準のものだったので、自分の用意したテンプレートに差し替えた。
* urls.pyの書き方が難しい、全てのリンクがうまくつながるようになるために、いろんな箇所を微調整。
* メールで送られてくるURLを正しいものにするには、siteテーブルを書き換える必要があることに気づくのにしばらくかかった。

djangoを使う人たちはこういうのは普通にすぐに自作してしまうんだろうか。
Turbogearsならこういうのはコマンド一発で完了してしまうのとは対照的。
しかしdjangoの勉強になったのでよかったと思う。

なんだかいろいろわかってきたよ。

[メモ]Intel Mac OSX 10.4でstackless pythonをビルド

stackless pythonのOSXバイナリもあるのですがインストール先を変えたりしたかったので、自分でビルドした。
途中1カ所つまずいたのでメモしておく。
(これが正しいのかはよくわからないですが。)
参考にしたのはこの記事。

./configure --prefix=/usr/local/stackless-python251 --enable-universalsdk --enable-framework

そして、できあがったMakefileにて -DSTACKLESS_FRHACK=0 という記述がある部分が1カ所あるので、そこを -DSTACKLESS_FRHACK=1 に書き換える。(←これがほんとにこれでいいのか自信ないのだが、上の参考記事と同様の効果を得るにはこのフラグの変更でいいようだ。)
そんで、
make
sudo make install

サンプルのfactorial.pyをダウンロードして、実行。
ayu@~% /usr/local/stackless-python251/bin/python factorial.py 
0.0117838382721

ひとまずうまく動いたみたい。

2007-08-22

[メモ]django-registrationの疑問

django-registrationは、djangoのSVN最新版でないと動かないようだ。
みなさんどうしているんだろう。自作?それともみんなSVN最新版を使うのが普通なんだろうか。。。

[メモ]【訂正】TurboGearsのwidgets-based formのpre-fill

昨日のエントリーに、TurboGears×Pythonatsさんからじきじきにアドバイスが!

以下のように、フィールド名:値という内容のdictを渡し、、

class SomethingFields(widgets.WidgetsList):
code = widgets.HiddenField()
query = widgets.TextField(label=u"検索")

class SomethingSchema(validators.Schema):
code = validators.String(not_empty=True, max=9)
query = validators.String(not_empty=True, max=5)

class SomethingForm(widgets.TableForm):
fields = SomethingFields()

something_form_obj = SomethingForm(fields=SomethingFields(), validator=SomethingSchema())

class SomeController(controllers.Controller):
@expose(template='kid:somepackage.SomeController.templates.detail')
def detail(self, code, **kw):
#........
return dict(form=something_form_obj, value={"code": code})


kidのほうで、value=でその辞書を指定してやるといいのだということ。
<span>
${form(action='search', submit_text = "Search", method="get", value=value)}
<p py:if="defined('message')">${message}</p>
</span>


やはり、正統なやり方はきれいです。
ありがとうございました!

2007-08-21

[メモ]TurboGearsのwidgets-based formのpre-fill

TurboGearsの公式FAQページで"Answers wanted for the following questions"という欄に未だ回答なしとして載っているFAQの一つに

「widgets-based formにあらかじめ値を埋め込むにはどうすればよい?」

というのがあがっている。

多分こんな感じでどうだろう。

class SomethingFields(widgets.WidgetsList):
code = widgets.HiddenField()
query = widgets.TextField(label=u"検索")

class SomethingSchema(validators.Schema):
code = validators.String(not_empty=True, max=9)
query = validators.String(not_empty=True, max=5)

class SomethingForm(widgets.TableForm):
fields = SomethingFields()

something_form_obj = SomethingForm(fields=SomethingFields(), validator=SomethingSchema())

class SomeController(controllers.Controller):
@expose(template='kid:somepackage.SomeController.templates.detail')
def detail(self, code, **kw):
#........
something_form_obj.fields[0].attrs["value"] = code
return dict(form=something_form_obj)

この最後から二行目のように、

フォームオブジェクト.fields[0].attrs["value"] = "あらかじめ入れたい値"

とすればいいようだ。fields[0]というふうに番号で指定するのが気持ち悪いけれど。

2007-08-17

[メモ]Turbogearsのidentity.currentはthread safeなのか?

turbogears/identity/__init__.pyのモジュールのグローバルに以下の記述がある。


current= IdentityWrapper()

この変数currentはthread safeでいてくれるんだろうか。
IdentityWrapperの中では、cherrypy.request.identityを参照していて、cherrypyまでは追ってないけど、cherrypy.request.identityがthread localな仕組みになっていたとすれば大丈夫なんかなあ。。。
なんか自信ないなあ。調査してみるか、、、大変。

追記---
大丈夫のようだ。
cherrypyのコードを追うのが面倒だったので、テスト的なアプリを作って実験をし、変数currentがthread safeらしいことを確認した。

というか自分はなぜ週末の夜の自宅で、仕事で出会ったバグの原因つぶしなぞしているのだろう。こういうのはまずい。

[メモ]TurboGears + SQLAlchemyはthread safeなんだろうか?

SQLAlchemyのSessionはthread safeではない。
thread safeにするには、SessionContextを使いなさいとのこと。

だが、Turbogearsのソース中にSessionContextを使っている箇所がないのはどういうことでしょう。。。。

この件調査中。

追記---
使っているのをturbogears/database.pyに発見。

from sqlalchemy.ext import activemapper, sessioncontext
....
session = activemapper.Objectstore(create_session)

Objectstoreはsqlalchemy/ext/activemapper.pyで以下の定義。
class Objectstore(object):
def __init__(self, *args, **kwargs):
self.context = SessionContext(*args, **kwargs)
def __getattr__(self, name):
return getattr(self.context.current, name)
session = property(lambda s:s.context.current)

たしかにこれならthread safe。安心した。

追記---
この記事投稿後15分でGoogleのインデックスに入っている。Google恐るべし。(Bloggerだからか?)

[メモ]名前付きlet

全エントリの反省を受けて勉強。

Scheme入門 7. 繰り返し

名前付きletの解説に、「Schemeで繰り返しを表す標準的な方法です。」とのことだけどとても気持ち悪い。
素人考えだけど、繰り返しは再帰関数一本にしてしまったほうがいいんではないだろうか。
再帰で書いた方が一行少なくなりそうな例しか自分には思いつかないが、こういった仕様があるということは、この記法が自然だったり役立ったりする場面があるということだろう。奥が深いなあ。

2007-08-16

[メモ]Pythonによる英字の半角全角変換

↓これもっと簡潔できれいな書き方はないものだろうか。

from itertools import izip
#半角英字->全角英字変換
HAN_CHARS = map(chr, range(ord('A'), ord('Z')+1) + range(ord('a'), ord('z')+1))
ZEN_CHARS = map(lambda x: unichr(0xff00 + x),range(0x21, 0x21+ord('Z')-ord('A')+1) +
range(0x41, 0x41+ord('Z')-ord('A')+1))
def han2zen(word):
"""
Unicodeで与えられた文字列の半角英字を全角英字に変換する。
"""
for c, cc in izip(HAN_CHARS, ZEN_CHARS):
word = word.replace(c, cc)
return word



追記----
多少ましなものになった
import re
HAN_UPPER = re.compile(u"[A-Z]")
HAN_LOWER = re.compile(u"[a-z]")
def han2zen(word):
word = HAN_UPPER.sub(lambda m: unichr(ord(u"A") + ord(m.group(0)) - ord("A")), word)
word = HAN_LOWER.sub(lambda m: unichr(ord(u"a") + ord(m.group(0)) - ord("a")), word)
return word


追記----
調子に乗って一般化してみた。上のような変換関数を大量生産できる関数だ。
Unicodeの列を平行移動するようなイメージの変換を生成します。
#!/usr/bin/python
# _*_ coding: euc_jp _*_
import re

def replace_sequence(*transform_rules):
"""
Unicode文字コードで連続した文字の列を対応する別の連続する文字に変換する関数を作る。

引数:
transform_rules 変換ルールを表す文字列を任意個の引数で与える。
一つの変換ルールは3文字のUnicode文字で表現されます。

変換ルール記述例
u"AZA" 一文字目は変換元の文字列の先頭、二文字目は変換元文字列の末尾、三文字目は変換先文字列の先頭を表します。

この変換ルールを引数に並べます。

"""
rules = map(lambda x: (re.compile(u"[%s-%s]" % tuple(x[:2])), x[2], x[0]), transform_rules)
def henkan(word):
for pattern, start, src_start in rules:
word = pattern.sub(lambda m: unichr(ord(start) + ord(m.group(0)) - ord(src_start)), word)
return word
return henkan

if __name__ == "__main__":
num_han2zen = replace_sequence(u"090")
num_zen2han = replace_sequence(u"090")
alpha_han2zen = replace_sequence(u"AZA", u"aza")
alpha_zen2han = replace_sequence(u"AZA", u"aza")

print num_han2zen(u"0120-116")
print num_zen2han(u"188−0013")
print alpha_han2zen(u"ABCを習いました。")
print alpha_zen2han(u"世界のSONY")

この関数replace_sequenceを使うと、上で必死こいてつくってた半角英字->全角英字関数はこの一行で作成できる。

alpha_han2zen = replace_sequence(u"AZA", u"aza")

使う時はこのalpha_han2zenを関数として呼び出し、

print alpha_han2zen(u"ABCを習いました。")

で行ける。

上のプログラムの実行結果。
ayu@~/work% ./sample.py
0120-116
188−0013
ABCを習いました。
世界のSONY

これはちょっと便利かも。会社に持っていこう。
こういうプログラムを書いていると、今更関数型言語を勉強しなくても、Pythonで、まあいいのかなという気にもなり、またしても自分の進歩が阻害されてしまう気がする。

2007-08-11

Turbogearsピンポイントドキュメント一覧

前エントリで触れたんだが、「ドキュメント重要」ということなので、自分が目を通すことの多いTurbogears関連のドキュメントの目次だけでも作っておこう。(日本語訳までできないのが残念ですが。)
Turbogearsはコードの大半をツールが自動生成してくれますので、ちょっとした作法なんかは自動生成された部分をチラ見するだけで、自分なりに追加機能を書き始めることも可能です。
ですが、やはり一貫した知識も必要なので、そのときはこういったドキュメントを読みます。

* Python Workshop the Edge 2007での柴田さんのハンズオン資料(PDF/日本語です)
入門記事です。自分がTurbogearsを使うきっかけになったのがこれでした。

* Turbogears公式リファレンス
Turbogearsの公式リファレンスです。Turbogearsでは各種デコレーターを使いますのでその使い方はここでよく見ることになります。

以下、各論。
* ユーザー登録機能のためのregistrationパッケージ
このトップページに文書が掲載されている。最近のWEBアプリでは、まずユーザー登録はどうしようかと考えるので、このすぐ使える機能一式のパッケージは重宝。
(登録、メールでの認証、ログイン、ログアウト、パスワード忘れ対策全部入り。)

* Modelを作る際に使うSQLAlchemyのドキュメント
モデルの作成は奥が深い。そのぶん、ドキュメントもちょっと規模が大きめです。
 * 低レベルなSQLを意識した使い方
  SQL脳から完全に脱却できない自分のための機能。。。

 * 高レベルなMapperを使った操作
  テーブルのリレーションを事前に定義して楽したい場合はこちら。
  パフォーマンスを改善するには、
  lazy=True
  deferred設定
  

* 各modelのCRUDを作るためのtgcrud
モデルを定義したら、これで一気にCRUDを作成する。

* Formのバリデーターについて
Formの扱いは、上に挙げているregistrationやtgcrudが生成するコードを見るとだいたいわかるのですが、ちゃんと知りたいならこのページがおそらくいいのでは。

* Kidテンプレート書式一覧
* その日本語訳を発見!
一通り仕組みができてきたら、実際のモデルのデータをページに埋め込んだりする段階にくるはず。その頃には、このテンプレートの書式一覧が必要です。
(柴田さんの日本語訳があることを今調べてて知った。。。。)

自分はSQLAlchemyとKidを使っていますので、上のようなラインナップです。
でもKidはXML的に妥当なテンプレートを書く必要があって、ちょっと厳しいなと思う。できれば差し替えたいところ。
SQLAlchemyには大満足。やりたいことが全てきれいにできる。パフォーマンスの調整も思いのまま。このツールはすばらしい。(普段仕事でJAVAのS2Daoも使うが、SQLAlchemyを知ってからはS2Daoがしんどくてしょうがない。。。)

自分もTurbogearsはまだここ1ヶ月ぐらいしか使ってませんので全くの素人レベルです。今後ここに役立つ文書のページを追記していきます。。。

ドキュメントが重要

最近職場でTurbogearsでつくった社内ツールを公開している。
いろいろな機能を次々追加しているので、けっこうな規模のツールになる予感がしてきた。

やはりこういうフレームワークを使うと仕事がとても早いので、うちの職場全体でもこういうPythonのフレームワークをつかっていったらどうだろうかという話も出てきた。

ただそうなるとTurbogearsの文書のラインナップでは仕事に使っていくにはちょっときついかなと思った。

まず職場の全員が英語に慣れているわけでもないので、公式文書の日本語訳がないのはつらい。

次に、Turbogearsのような「詳細は他の(KidなりSQLALchemyなりの)文書を見てね」という文書体系では、ドキュメント内の探索の仕方を、各部品ごとに変える必要がある。

こういうのは慣れなので日々Pythonに興味を持って情報を自ら集める人にとってはなんてことない。
文書が英語だろうと、ちょっとふぞろいだろうと、サンプルコードやいろんな人のblog記事を探索しながらなんとかなる。
ただ、職場では「本来はJAVAが一番好きだけど、仕事なのでしょうがなくPython使うよ」という人も当然のように存在する。
そういった人のための情報を用意できたときに初めてフレームワークが「実務に使える」という形で浸透していくんじゃないかなあと思った。

こういったことを考えるとdjangoの文書の日本語訳の存在はすばらしいことと思う。
自分の場合はこれから勉強していかないといけないけれど。

(休日に仕事のことを考えるのはほどほどにしなければ、、、)

2007-08-09

[メモ]SQLAlchemyのMSEnum型

MSEnumは引用符内にさらに引用符を書かねばならないという罠。


Column('myenum', MSEnum("'foo'", "'bar'", "'baz'"))

[メモ]最近気に入っている方法

1. Gnu Screenの0番目に常にemacsを開いておく。
2. 仕事の進捗はemacsのChangelogでメモ。
3. 一日の仕事が終わるときにもChangelogにメモ。
4. Screenのデタッチで閉じて帰宅。
5. 次の日出社したらscreen -r でChangelogが即開いて見える。

こういうのっていいですね。
サーバーがずっと動きっぱなしの会社だから可能な方法。
emacsはまだまだ便利に使えそうなのでもっと勉強していこう。。。

2007-08-08

SQLAlchemyでMySQLのyear型が使えない

MSYearというmysql用の型のクラスがあるのだが、実際にcreate table文を作ると、VARCHAR型として作られてしまう。

この件調査中。

追記----
これがSQLAlchemyの方針にとって正しいのか不明なんだけど、sqlalchemyのソースのsqlalchemy/databases/mysql.pyのMSYearに以下のget_search_list(self)メソッドを追記すれば一応解決する。


class MSYear(sqltypes.String):
"""MySQL YEAR type, for single byte storage of years 1901-2155"""

def get_col_spec(self):
if self.length is None:
return "YEAR"
else:
return "YEAR(%d)" % self.length

def get_search_list(self):
return [self.__class__]

year型がMySQLの型の中でもすごく特殊な実装なので、非常に難しい部分なんだろう。
ちょっとSQLAlchemyの作者の方々にメールを送ってみようかなあ。

追記---
メールを送ってみた。
こういうのを送るのはどきどきするなあ。英語きっとぐちゃぐちゃだし。通じればいいんだけど。

追記---
返事がきた。
0.3のメンテナンスブランチ&0.4では修正済みとのことです。
あああ、こういう最新版をチェックせずに指摘メールを送ったり、(さらには自分で修正を試みたり)というのはいけないことだなあと思った。

2007-08-05

[メモ]Mac OSXにgtagsをインストール


$ sudo port install global

これで、/opt/local/bin/gtags にインストールされる。

[メモ]LL魂感想

今思い出せるものだけメモ。

== Language Update ==
Python::
「Pythonは10年使える言語」というポイントは、今まで意識していなかった。
これまで5年ぐらいPythonを使っていますが、ずっと安定して使えていたり、新バージョンが出た時に、当たり前のように後方互換だったりしたことに、陰ながら支えてもらっていたことに気づき、感謝の気持ちを覚えた。

Ruby::
Lazy collectionの話は思わず「おおお!」と思ってメモを取ったのだが、その後手厳しい修正が。でもああやって言及したということは、実装したいってことだからこれから数年以内に実現するんだろうか。

PHP::
何もあそこまで「どんより感」を全面に出さなくてもよかったのではないだろうか。(LLが初めて開催された年もそうであった。)
GreeとかSidefeedとかcheckpadのような華々しい実績に言及しないと。
数年前は自分もPHPを使っていましたが、言語の仕組みとしての難点はほんとうに多々あれど、「とにかくWEBが作れる」というのは事実。

Perl::
Perl6か、、、。大変そうだなあ。
Perl5.10で"//"という記法が新登場とのこと。こういう方向性はPythonとは正反対でおもしろい。Pythonではありえない。

R::
今会社の隣の席の人がしきりにオススメしてくる言語なので興味があった。scheme+S言語という点が興味を引く。schemeか、、去年途中まで勉強してやめてるな。。。

Clean::
速度が速いということに興味を持った。
昨年仕事でサンプル的にHaskellを使ったことがあるが、700万行(20G)のテキストファイルを処理するのに速度的にまったく不足していた。が、この言語ならいけるんじゃないだろうか。Haskellにそっくりなのですんなりつかえるかもしれない。Intel Macのサポートが次バージョンからだそうなので心待ちにしよう。

Lua::
「俺様言語」のコーナーで各種自作言語がライバル視していたのが特徴的。

== 俺様言語の作り方 ==
Xtal::
あちこちのブログで話題になっているが、プレゼンが(というか作者が)抜群に天然。
C++との連携を主眼にしているということは、C++の資産が全て使えるということだから実はすごく便利かもしれない。ただ自分の場合はPytonとC++の連携が今のところうまく行っているので、使い道を考えにくい。
Pythonと簡単に連携できないだろうか。Pythonでパフォーマンスが欲しいところをC++で書くのではなく、Xtalで書ければ、Pythonとのギャップがまだ小さいだろうから、簡単にならないだろうか。

ひまわり、なでしこ::
会場で最も注目を浴びていたと思う。自分も強く惹かれた。
ほんとうに読みやすそうだ。(日本人Only)
文書の充実もすごい。関数定義はできないんだろうか。。。

== VM魂 ==
プレゼンがどれもおもしろかったが、なんだか各実装の対決みたいなピリピリした空気がでていてちょっと苦手だった。


以後用事のため途中退席。ライトニングトーク見たかった。。。

最近クラスを使うことが減ってきている自分

なぜオブジェクト指向は嫌われているのか?

上の記事を見て、ふと思ったんだが最近自分はクラスを嫌っている。

GOFを勉強したてのころは喜々としてVisitorパターンなどを使って、後になって、「メチャクチャ読みにくいな、このプログラム」などと思ったことがあった。

こないだ仕事でWorkerThreadを作った時は、サーバーとワーカーの二つだけをクラスにした。が、その他本来JAVAならコマンドパターンを使う部分では、関数を引数に渡すので、クラスを使う必要はまったくなかった。むしろPythonの場合、そうしないほうが柔軟な気もする。

で、ちょっと気になったので、TurboGearsやPylonsや他のチュートリアルを見ながら作った標準的なプログラムたちと、自分が0から書いたプログラムの中での、クラス/関数のカウントをしてみた。

まず、標準的なものでのカウント。

ayu@~/work% ./cfcount.py BlogTutorial BlogTutorial2 wsgitutorial 
Class total: 54
Function total: 19
Class rate: 0.739726027397

次に自分のスクリプトでのカウント。(自分が公開している、mword, cmecab, clitter, extbodyでの集計)
ayu@~/work% ./cfcount.py dev_pub                                
Class total: 4
Function total: 55
Class rate: 0.0677966101695

これを見ると、結果は明らかに自分がクラスを書くことが少ない。
標準的なほうは、「フレームワークに従わねばならない」という事情があるからしかたないにしても自分はあまりにも書かなすぎではないだろうか。

これは普通の傾向だろうか。他のPythonistaのみなさんもこんなものなんだろうか。

ちなみにカウントに使ったプログラムはこれ。
(このプログラムもクラスなど使わない。もしこれをJAVAで書くなら"class XXCounter{...}"とか書かなきゃならないんだよね。。。)
#!/usr/bin/python
# _*_ coding: utf-8 _*_
import os
import re
from itertools import chain

def class_and_func_enumerator(fname):
fp = file(fname)
for line in fp:
m = re.match("^(def|class) ", line)
if m: yield m.group(1)
fp.close()

def count_in_directory(directory):
for dpath, dnames, fnames in os.walk(directory):
for fname in fnames:
if fname.endswith(".py"):
yield class_and_func_enumerator(dpath + os.sep + fname)

def count_all(directories):
counter = {"def": 0, "class": 0}
for directory in directories:
for label in chain(*list(count_in_directory(directory))):
counter[label] += 1

print "Class total:", counter["class"]
print "Function total:", counter["def"]

total = float(sum(counter.values()))
if total != 0:
print "Class rate:", counter["class"] / total

def main():
import optparse

parser = optparse.OptionParser(u"""
Usage:
cfcount.py dir1 [dir2 dir3 ...]

指定したディレクトリ以下にある*.pyファイルのクラス、関数を数えます。
""")

(options, args) = parser.parse_args()

count_all(args)

if __name__ == "__main__":
main()

2007-08-04

[メモ]今週末のTODO

* Google code apiを使って、"create table"を含むコード(ファイル名が*.sqlだけでもよい)を収集。
* SQLのcreate文を抽出するスクリプトを書く。
* create文のParserを書く。

追記---
Google code apiは検索クエリ、結果開始番号、取得最大件数の指定しかできない。ファイルタイプが"SQL"とかの指定は画面からしかできないようだ。
また、正規表現が使えるということらしいが、(のエスケープがうまくいかず、"create¥stable.*¥("のようなクエリを送ることはできない。

追記---
1完了。700ファイルぐらい収集できた。もっとたくさんほしいところだがこれが限度か。。。
2,3は合わせて解決できないだろうか。

2007-08-03

LL魂

明日はLL魂にいってきます。
第一回に行って以来久しぶりなので楽しみです。

2007-08-02

Turbogearsを仕事で使った感想

詳細は企業秘密で書けませんが、うちはWEBを作ること自体が仕事ではないのでWEBの技術に関する部分だけはあまさずメモしておきます。
何をどう配置しようかという考えをめぐらせた部分も書いています。

== 背景 ==
最近自分が会社で、コマンドラインから起動するタイプの、とある分析ツールを作った。
* 元々これはある一人の分析担当者のために作ったものだった
* が、これを他の複数名の担当者でいくつもの案件で使いたい。
* これまでのように自分が依頼を受けてコマンドを打って結果を一つ一つ提出するのでは間に合わない。

そこで、この分析ツールをWEBから使えるようにしよう。

== 束縛条件 ==
* 分析の入力は数千行のCSVやタブ区切りファイル複数。(ファイルアップロード機能がいる)
* ユーザーごとに入力データを保持しておき、複数回の分析で使いまわす。
* 分析ツールでの分析は数秒~数分。将来複雑な分析を増やすなら数時間もありえる。

== 使用する技術 ==
* すでにある分析ツールはPython製。

* 画面はTurbogearsで作ろう。
  * DBはMySQL。SQLAlchemyでDAOを作ろう。
  * モデルの操作はtgcrudで生成。
  * ユーザー登録はregistrationで生成。

* 分析ツールはどうしよう。。。
  * 分析ツールの起動から終了まで、ブラウザの画面で待たせるわけにはいかない。
  * しかも同時に10も20も分析ツールのプロセスが動くのはマシンの負荷上まずい。
  * WorkerThreadパターンを使おう。
  * 処理が終わったらユーザーにメールで結果ファイルを送ろう。(参考資料)
  * そんなマルチスレッドの時間がかかる処理をTurbogears上で行っていいの?
  * よく知っているPyroでTGとは別プロセスにしてWorkerThreadをまわすようにしよう。
   (PyroはRubyで言うところのdRuby相当の分散オブジェクト環境構築ツールです。)

== 参考にした資料 ==
(前節で挙げたもの以外)
* TGでのファイルアップロード入門
* Pyroのサンプルプログラム(Pyroはこの最初の例をまねるだけで十分!)

== はまったとこ ==
Turbogears-1.0.2.2で最初作って完成させ、それをTurbogears-1.0.3.2で動かしたら、SQLAlchemyのMany-To-Manyで結びついているレコードの生成がうまくいかない部分があることを発見した。
おそらくなんかのバグが潜んでいそうなので、それを明らかにする簡単なコードを書いて調査しようと思う。

SQLAlchemyのどこかのサンプルを写して、lazy=Falseを指定していたが、これはいつでもjoinするという意味だそうだ。(Eager Loading)これをTrueにしたら、レコードの一覧表示が速くなった。

さらに、一覧表示画面が遅くなる原因として、Mapperが毎回全てのフィールドをDAOに取得してきているというものがある。これを防ぐにはSQLAlchemyのMapperのオプションでdeferred設定をするとよいことが分かった。
これを指定すると、特定のフィールドの値を、必要になったときだけDAOに格納してくれるようになる。

== 成果、感想 ==
昨日の夕方に作業を初め、今日の夜には一通り動くものができました。
(もちろん夜は早めに自宅に帰って次の日の昼過ぎに(遅刻して)出社しました。同僚のみなさんすいませんでした。)

その間、会議や各種ミーティング、過去に作ったJAVAのプログラムのバグ修正などもあり、今回のWEB作りだけに集中していたわけではありません。
Pyroには慣れていたのですが、TGはそうでもないので、いろいろ資料を調査しながらの制作になりました。
そんな状況なのに、一通り出来上がってしまって、自分でもとても驚いています。

明日以降は、
* セキュリティ関係の入力値チェックを実装する。(社内ツールとは言っても、やらなければ)
* マスターデータの運用体制、運用スクリプトを作る。
* 担当者とのレビュー作業。
* インストールマニュアル、操作マニュアルなどの作成。
というやや重い作業も残っています。

自分はここ数年WEB開発から離れていました。
数年前はMVCのフレームワークと言ったらStrutsぐらいしか普及しておらず、自分はわりと生のPHPでガリガリ書いているタイプの開発者でした。
そういうWEB開発に造詣のあるわけではない開発者でも、こうして簡単に業務に使えるレベルの画面を作れるとは、ほんとうに開発技術の進歩はすごいと思いました。

ウワサの「codeなにがし」にすごいノウハウが!

これはすごい。

PHPで超効率的にプログラミングする方法

2007-08-01

Pythonでdocstringを書くとちょっとパフォーマンスが落ちる件


昔からそうだったんだけど、Python2.5の今になってもこの件はかわってない
みたいだ。


#!/usr/bin/python
# _*_ coding: euc_jp _*_

def withoutdoc(n):
return n

def withdoc(n):
"""ここにすごおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおおく丁寧なドキュメントを
たーーーーーーーーーーーーーーーーーーーーーーーーっぷり書きます。"""
return n

if __name__=='__main__':
from timeit import Timer
t = Timer("withoutdoc(1)", "from __main__ import withoutdoc")
print t.timeit()

t = Timer("withdoc(1)", "from __main__ import withdoc")
print t.timeit()

結果

ayu@~/work% ./performance_test.py
0.238394021988
0.246536016464

100万回呼び出して、0.01秒の違いなので、まあ、気にならないと言えば気に
ならないんだけど。