Google Apps を使っている人たちが便利に使えるコードレビューツールを翻訳することを始めた。ずっと英語のままだと会社にあまり使えるものにならないだろうと考えていて始めた。コードは bitbucket に
-
rietveld コードレビューツールの翻訳を始めた
2010-07-26 10:43:56Send feedback
-
Python 温泉 (夏 2010)
2010-06-27 09:23:14Python 温泉 in 熱海に行ってきました。ま、まだ熱海の旅館なんだけど、早速 Blog を書こうと思っていました。
結構いい感じで、進んで来ました。会社の AE35 、 と akisutesama と電車に乗ってきました。電車の中、ずっと iPhone 触りつつ
増田さん が持ってかえってきた Singaporeで行った PyCon APAC グッズをジャンケンで割り当てた。後、 清水川先生 が寄付した、 「エクスパート Python プログラミング」の本をジャンケンで配った。
今回の Python 温泉、割と効率が高くて、いろなことができました。
Django メールAPIの文字コード周りは余りがあるので、すこし改善しようと思いました。EmailMessage では、エンコーディングを指定したら、その文字コードを使いますけど、 send_mail() 関数はまだメールの文字コードを指定できない。ということで、EMAIL_CHARSET のデフォールトのメール文字コードの設定を増やして、send_mail() に encoding 引数を増やして、パッチを作って、 投稿した 。
次に localflavor.jp モジュールにバグ修正 and 機能追加して、パッチを チケットに投稿した 。
localflavor.jp.jp_prefectures に Select ウィジェットの選択肢が定義されているのですが、順番は ISO-3166-2 とずれがあったので、直しました。それに、 HiraganaField、KatakanaField、FullWidthField、HalfWidthKatakanaField のフォームフィールドを追加した。普通にフォームに使えるCharField.
from django import forms from django.contrib.localflavor.jp import forms as jp_forms class MyForm(forms.Form): name = forms.CharField(u'名前') kana_name = jp_forms.KatakanaField(u'かな')
Django のパッチポリシーに応じて、もちろん、両方とうもテスト付き
その後に、僕が作った python-disqus-client のパッチを他のデベロッパーからもらったので、新しいバージョンを作って、 pypyにつっこんだ 。
その後に、仕事のライブラリの Django 1.2 対応をしたり、 buildbot を立てたりしました。
皆さん、お疲れ様でした。
-
Python StringIO と cStringIO のもう一つの違い
2010-06-23 16:50:30C で作られた cStringIO は ピュア Python で作られた StringIO モジュールと違うのをみんな知っていると思いますけど、今日、私が知らなかった違いをもう一つ見つけました。
StringIO では、StringIO のコンストラクターに文字列を渡せば、その文字列に書き込みすることができる。
>>> from StringIO import StringIO >>> writer = StringIO('a') >>> writer.seek(1) >>> writer.write("b") >>> writer.getvalue() 'ab'
だが、 cStringIO の場合、コンストラクターに文字列を渡せば、StringIO.StringI オブジェクトになって、write メソッドがそもそもないオブジェクトになります。
>>> from cStringIO import StringIO >>> writer = StringIO('a') >>> writer <cStringIO.StringI object at 0xb74b3530> >>> writer.write("b") Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'cStringIO.StringI' object has no attribute 'write'
解決するのがそんなに難しくないけど、AttributeError が出て、ビックリしました。
解決するのはこう書けばいい。
>>> from cStringIO import StringIO >>> x = 'a' # 既存文字列 >>> writer = StringIO() >>> writer.write(x) >>> writer.write('b') >>> writer.getvalue() 'ab'
私は具体的に、やろうとしていたのは、 Django の django.core.files.base の ContentFile を使おうとしていた。だが、どうしても、StringIOのコンストラクターに文字列を渡すので、書き込みには使い物にならなかった。
>>> from cStringIO import StringIO >>> from django.core.files.base import File, ContentFile >>> f = ContentFile(None) >>> f.write('a') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/ian/.virtualenvs/django-test/lib/python2.5/site-packages/django/core/files/utils.py", line 24, in <lambda> write = property(lambda self: self.file.write) AttributeError: 'cStringIO.StringI' object has no attribute 'write' >>> f = File(StringIO()) >>> f.write('a') >>> f.seek(0) >>> f.read() 'a'
-
Django 1.2 マルチ DB と master/slave レプリ
2010-06-04 16:49:05Django 1.2 はマルチDB対応ができまして、master/slave レプリにも対応しているのですが、詳しく見るといろな問題が出てきます。
Django 1.2 のマルチDB対応は どのDBから、読み込むか、どのDBに書き込むかがDBルータで決める。しかし、そのルータで決める時点でリクエストオブジェクトにアクセスできないので、レプリラグのどを自動的に対応するのが難しい。
レプリケーションを使っている場合、マスターDB に書き込んだ時に、データがスレーブDBまで流れるラグがあるので、DBに書き込んだデータがすぐDBから取れない場合がある (eventual consistency)。
obj = MyModel.objects.create(content="Hello World") obj.content = "Hello World!" obj.update() # 失敗する可能性あり obj2 = MyModel.objects.get(pk=obj.id)
こういう場合に対応するには、 QuerySet の using メソッドを使わないとダメです。
obj = MyModel.objects.create(content="Hello World") obj.content = "Hello World!" obj.update() # OK obj2 = MyModel.objects.using("master").get(pk=obj.id)
しかし、view コードの中に、DBの仕組みに依存しないといけなくなる。環境 (開発、関連サイトなど)によってDBの仕組みが違う場合もあるだろう。第三者アプリも動かない場合があります。( Django 1.2 自体の contrib モジュールの標準アプリも保証なし)
上の問題と同じく、POSTして新しく追加したレコードも次のGETリクエストが来た時点では取れない可能性が高い。本当はPOSTした後のGETリクエストは マスター DBを使いたいんですが、 Django 1.2 の仕組みだとそういう処理を導入することが難しい。上と同じく view の中に using() で対応する必要があります。
どのDBを使うかは QuerySet 単位で、デフォールトDBの名前も固定で、リクエストごとデフォールトDBを変更することもできません。
今のところは monkey patch しなければ、 Django 1.2 のマルチDBでちゃんと master/slave レプリを使えないのかな。やっぱり微妙ですね。
-
Django 1.2 の変更のまとめ
2010-05-24 11:07:18先週、 Django 1.2 が出ました。新しくて、良い機能がいっぱい入っているけども、1.1 からの変更をご紹介しようかと思っています。
マルチDB
1.2 では、一番大きい変更は明らかに マルチDB対応 ですね。
settings.pyのDATABASEオプションはDATABASESになりました。それで python 辞書で複数のDBを設定する。以下のように MySQL、sqlite、PostgreSQL、それぞれ違ってても構いません。
DATABASES = { 'default': { 'NAME': 'app_data', 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'USER': 'postgres_user', 'PASSWORD': 's3krit' }, 'users': { 'NAME': 'user_data', 'ENGINE': 'django.db.backends.mysql', 'USER': 'mysql_user', 'PASSWORD': 'priv4te' } }
どのデータベースをどの場合に使うかをデータベースルータで決める。 モデルの読み込みの場合のDB、書き込みの場合のDB、リレーションを許可するかどうか、syncdb (テーブル定義)の許可を実装する。
class MyAppRouter(object): """myappアプリケーションのモデルを別DBに保存し、 DBの操作を制御する""" def db_for_read(self, model, **hints): "'myapp'の場合、'other'というDBを使う。" if model._meta.app_label == 'myapp': return 'other' return None def db_for_write(self, model, **hints): "'myapp'の場合、'other'というDBを使う。" if model._meta.app_label == 'myapp': return 'other' return None def allow_relation(self, obj1, obj2, **hints): "両方のモデルが'myapp'に入っている場合のみにリレーションを許可" if obj1._meta.app_label == 'myapp' or obj2._meta.app_label == 'myapp': return True return None def allow_syncdb(self, db, model): "'myapp' を 'other' DB のみに入れるようにする。" if db == 'other': return model._meta.app_label == 'myapp' elif model._meta.app_label == 'myapp': return False return None
モデル検証
モデルのデータを検証すること ができるようになりました。それに、新しいモジュール django.core.validators ができました。
フォームと同じように
full_clean(),clean_fields(),clean(),validate_unique()の 4つのメソッドが追加されました。clean_fields(exclude=None)を呼び出すとモデルのフィールドのデータを一個一個検証する。clean()はカスタム検証の処理を実装するためのメソッド。このメソッドをサブクラスで実装すれば、カスタム検証ができます。validate_unique(exclude=None)はユニークフィールドの検証を行う。full_clean(exclude=None)を呼び出すとすべての検証を行います。excludeパラメーターで検証を行わないフィールドを指定できる。CSRF 対策
1.1の CsrfMiddleware は正規表現でCSRF用フィールドをフォームに突っ込むことができたが、実装が微妙でJSなどに使えなかったので、Django 1.2でもっと綺麗なAPIが揃えました
django.middleware.csrf.CsrfViewMiddleware が新しくできました。 このミードールウエアでCSRFのトーケンが生成され、ポストの場合、検証が行われ
{% csrf_token %}テンプレートタグでフォームに入れることができている。Middleware を使いたくない場合は、 CSRF対策をしたい viewのみに
django.views.decorators.csrf.csrf_protectデコレータを使うことができます。messages API
以前、Djangoのユーザメッセージは 「変更しました」とか、「削除しました」とか、一時的なメッセージなのに DB に保存しましたので、微妙だった。
それで、Django 1.2 では新しい messages API ができました。 DB のかわりにセキュアクッキーとセッションを使えるようになりました。後、メッセージタイプ、info、warning、errorなどが使えるようになりました。
from django.contrib import messages messages.add_message(request, messages.INFO, 'Hello world.')
messages.success(request, u'プロフィールを更新しました。') messages.warning(request, u'サービス契約が後3日で切ります。') messages.error(request, u'レコードが削除されました')
メールバックエンド
クラウドサービスでは、メールがAPI で提供されているのが多くて、Django 1.1 では
send_mailは使えなかったんですけど、 Django 1.2では メールバックエンド が使えるようになりました。標準に入っているのは、SMTP、コンソールに出力するのみ、メールを無視するバックエンドが揃えている。
カスタムバックエンドを作るために、
django.core.mail.backends.base.BaseEmailBackendを継承して、send_messages(email_messages)を実装する。 永続コネクションを使う時はopen()とclose()メソッドを実装すれば良い。smart if テンプレートタグ
Django 1.1 以前では、 if テンプレートタグは boolean しか使えなくて、複雑なコンディションが書けなかった。 Django 1.2 から、
==、>、<、and、orなどが使えれるようになりました。{% if a != b %} ... {% if c == d and e >= f %} ... {% endif %} ... {% endif %}まとめ
ちょっとしたバグのため、Django 1.2.1 がすぐ出ると思いますが、Django 1.2 は良い新規機能が多くて、是非使って見てください。
-
mercurial .hgrc include
2010-05-19 19:43:52僕は 僕のシェル環境設定ファイル をbitbucket で管理している。 新しいサーバーで作業する時にこのレポジトリからクローンして、ファイルを設定するけど、ローカル環境のみの設定が必要な場合が多い。今まで、bashrc等のスクリプトの中でローカル設定ファイルがあれば、sourceして、適用したんですけど、 mercurial の hgrc はそういうのができなかった。
と思ったら、 mercurial 1.3 からできるらしい です。下のコードを hgrc に入れると include ができる。超便利
%include .hgrc.local
ファイルの場所は include したファイルの場所からの相対パス
でも、このファイルな存在しなければ、エラーが出るので、
touch ~/.hgrc.localを一回やらないとうるさい。 -
ipython と virtualenv を同時に使う方法
2010-05-08 10:26:32概要
python の皆さんはみんな使っている ipython は virtualenv を使う時に virtualenv に入っているモジュールをインポートできないことが起こります。 ipython は特に virtualenv に対応していないわけです。 ipython をグローバルじゃなくて、virtualenv 毎にインストールすると解決できるのですけど、 ipython を落とすのが重いし、PIP_DOWNLOAD_CACHE ( pipを使うべきだぞ ) を使わない限り、絶対にイライラする。
ipython と virtualenv を使う方法第一
ということで、第一方法は PIP_DOWNLOAD_CACHE を設定して、virtualenv を作る時に、virtualenvwrapper ( virtualenvwrapperも使うべきだぞ ) のフックを使って ipython を自動的にインストールする。自分の virtualenv ディレクトリ (WORKON_HOME) に
postmkvirtualenvというスクリプトを入れると、環境を作った後に実行してくれます。それで、作るときに、毎回 ipython 入れます。自分の .bashrc かどこかで、 PIP_DOWNLOAD_CACHE を設定する。これで、一回ダウンロードしたら、毎回ダウンロードしなくてもいい。
PIP_DOWNLOAD_CACHE=~/.pip_cache
それから、$WORKON_HOME/postmkvirtualenv にこう書きます。
# virtualenv毎に pip をインストールする場合 #easy_install pip pip install ipython # pudb も便利かもよ #pip install pudb
それで、mkvirtualenv naninani を実行するときに virtualenv をちゃんと使う ipython をインストールしてくれます。
ipython と virtualenv を使う方法第二
ipython はpython で書くユーザ設定ファイル機能があります。それを使えば、PYTHONPATHをいじれたりすることができるので、それで virtualenv を使うこともできます。 ユーザ設定は
~/.ipython/ipy_user_conf.pyに入っています。こう書きます。
# Most of your config files and extensions will probably start with this import import os import IPython.ipapi ip = IPython.ipapi.get() # You probably want to uncomment this if you did %upgrade -nolegacy # import ipy_defaults def main(): # Handy tab-completers for %cd, %run, import etc. # Try commenting this out if you have completion problems/slowness # import ipy_stock_completers # uncomment if you want to get ipython -p sh behaviour # without having to use command line switches # import ipy_profile_sh o = ip.options # An example on how to set options #o.autocall = 1 o.system_verbose = 0 import_all("os sys") execf('~/.ipython/virtualenv.py') # some config helper functions you can use def import_all(modules): """ Usage: import_all("os sys") """ for m in modules.split(): ip.ex("from %s import *" % m) def execf(fname): """ Execute a file in user namespace """ ip.ex('execfile("%s")' % os.path.expanduser(fname)) main()
main() の中、
virtualenv.pyという設定ファイルを呼び出す。virtualenv.pyというファイルはこう書きます。import site from os import environ from os.path import join from sys import version_info if 'VIRTUAL_ENV' in environ: virtual_env = join(environ.get('VIRTUAL_ENV'), 'lib', 'python%d.%d' % version_info[:2], 'site-packages') site.addsitedir(virtual_env) print 'VIRTUAL_ENV ->', virtual_env del virtual_env del site, environ, join, version_info
これで、
VIRTUAL_ENVという環境変数が設定してある場合、それを python のサイトディレクトリとして登録する。これで、ipython を実行するときに こうなるはず。
土 5月 08 10:22:57 ian@macbook-ian:~$ workon django-hgwebproxy (django-hgwebproxy)土 5月 08 10:23:15 ian@macbook-ian:~$ ipython VIRTUAL_ENV -> /home/ian/.virtualenvs/django-hgwebproxy/lib/python2.6/site-packages Python 2.6.4 (r264:75706, Dec 7 2009, 18:45:15) Type "copyright", "credits" or "license" for more information. IPython 0.10 -- An enhanced Interactive Python. ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. help -> Python's own help system. object? -> Details about 'object'. ?object also works, ?? prints more. In [1]:
ここに、
VIRTUAL_ENV -> /home/ian/.virtualenvs/django-hgwebproxy/lib/python2.6/site-packagesが出て、virtualenv を使っていることが確認できる。それでは、それでは、
-
mixi アプリのキャンバスが狭くなるぞ
2010-04-28 16:52:22昨日、mixi アプリのキャンバスサイズ (run_appli.pl (canvas) の表示サイズ) が狭くなる ニューズ が出た。mixi の公告プラットフォームのために、キャンバスの幅を狭くし、右側に公告を出すようにするらしい。10月までにアプリを修正しないとだめだ。これはひどい。Flash のサイズ限定アプリはほぼ作り直しになるかも。
6月~9月に、945pxと760px 両方使えるのですけど、760px にすると、945px に元に戻すことができなくなる。後、10月から、945pxのサポートが終了になる。ということで注意しろ
-
何でObjective-Cが好きじゃなくなったの?
2010-03-01 16:02:31今日、 Facebook iPhone アプリの作者 Joe Hewitt の発言がtwitterに流れた。
joehewitt: I think for a while there I was actually convinced that I liked Objective-C. (Objective-C が好きとおもったんだけどな)
markhammonds: @ joehewitt What changed your mind? (何で気変わったの?)
-
growltestrunner の pynotify 対応 / pynotify の使い方
2010-02-09 16:33:48最近、会社の AE35 さんが growltestrunner を作っていて、 modipyd を使って、ファイルを更新したタイミングで自動テストを自動的に実行してくれて、growlで通知するように素敵な環境を設定した。
俺はlinuxなので、当然 growl がないけど、Mac OS の growl みないな libnotify があって、pynotify と言うpythonバインディングがあるから、pynotifyでも使えるように、 fork を作った。
ちなみに、pynotify はこういう使い方
import pynotify pynotify.init("My App") n = pynotify.Notification("Title", "Message", "/path/to/my/icon.png") n.show()
