Ian Lewis
Ian Lewis is a web developer living in Tokyo Japan. His current interests are in Django, python, alternative databases and rapid web application development. About Me...
  • rietveld コードレビューツールの翻訳を始めた

    Google Apps を使っている人たちが便利に使えるコードレビューツールを翻訳することを始めた。ずっと英語のままだと会社にあまり使えるものにならないだろうと考えていて始めた。コードは bitbucket に

    http://bitbucket.org/beproud/rietveld/

    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - rietveld コードレビューツールの翻訳を始めた
  • Python 温泉 (夏 2010)

    Python 温泉 in 熱海に行ってきました。ま、まだ熱海の旅館なんだけど、早速 Blog を書こうと思っていました。

    結構いい感じで、進んで来ました。会社の AE35 、 と akisutesama と電車に乗ってきました。電車の中、ずっと iPhone 触りつつ

    http://desmond.yfrog.com/Himg35/scaled.php?tn=0&server=35&filename=5r8u.jpg&xsize=640&ysize=640 http://farm2.static.flickr.com/1105/4731986975_91dea2ba68.jpg

    増田さん が持ってかえってきた Singaporeで行った PyCon APAC グッズをジャンケンで割り当てた。後、 清水川先生 が寄付した、 「エクスパート Python プログラミング」の本をジャンケンで配った。

    http://farm5.static.flickr.com/4120/4735457768_8ae67e7c39.jpg

    今回の 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 を立てたりしました。

    皆さん、お疲れ様でした。

    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - Python 温泉 (夏 2010)
  • Python StringIO と cStringIO のもう一つの違い

    C で作られた 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'
    
    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - Python StringIO と cStringIO のもう一つの違い
  • Django 1.2 マルチ DB と master/slave レプリ

    Django 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 レプリを使えないのかな。やっぱり微妙ですね。

    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - Django 1.2 マルチ DB と master/slave レプリ
  • Django 1.2 の変更のまとめ

    先週、 Django 1.2 が出ました。新しくて、良い機能がいっぱい入っているけども、1.1 からの変更をご紹介しようかと思っています。

    マルチDB

    1.2 では、一番大きい変更は明らかに マルチDB対応 ですね。 settings.pyDATABASE オプションは 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 から、 ==><andor などが使えれるようになりました。

    {% if a != b %}
        ...
        {% if c == d and e >= f %}
        ...
        {% endif %}
     ...
    {% endif %}
    

    まとめ

    ちょっとしたバグのため、Django 1.2.1 がすぐ出ると思いますが、Django 1.2 は良い新規機能が多くて、是非使って見てください。

    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - Django 1.2 の変更のまとめ
  • mercurial .hgrc include

    僕は 僕のシェル環境設定ファイル をbitbucket で管理している。 新しいサーバーで作業する時にこのレポジトリからクローンして、ファイルを設定するけど、ローカル環境のみの設定が必要な場合が多い。今まで、bashrc等のスクリプトの中でローカル設定ファイルがあれば、sourceして、適用したんですけど、 mercurial の hgrc はそういうのができなかった。

    と思ったら、 mercurial 1.3 からできるらしい です。下のコードを hgrc に入れると include ができる。超便利

    %include .hgrc.local
    

    ファイルの場所は include したファイルの場所からの相対パス

    でも、このファイルな存在しなければ、エラーが出るので、 touch ~/.hgrc.local を一回やらないとうるさい。

    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - mercurial .hgrc include
  • ipython と virtualenv を同時に使う方法

    概要

    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 を使っていることが確認できる。

    それでは、それでは、

    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - ipython と virtualenv を同時に使う方法
  • mixi アプリのキャンバスが狭くなるぞ

    昨日、mixi アプリのキャンバスサイズ (run_appli.pl (canvas) の表示サイズ) が狭くなる ニューズ が出た。mixi の公告プラットフォームのために、キャンバスの幅を狭くし、右側に公告を出すようにするらしい。10月までにアプリを修正しないとだめだ。これはひどい。Flash のサイズ限定アプリはほぼ作り直しになるかも。

    6月~9月に、945pxと760px 両方使えるのですけど、760px にすると、945px に元に戻すことができなくなる。後、10月から、945pxのサポートが終了になる。ということで注意しろ

    run_appli.pl (canvas) の表示サイズ変更について

    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - mixi アプリのキャンバスが狭くなるぞ
  • 何でObjective-Cが好きじゃなくなったの?

    今日、 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? (何で気変わったの?)

    joehewitt: @ markhammonds Python

    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - 何でObjective-Cが好きじゃなくなったの?
  • growltestrunner の pynotify 対応 / pynotify の使い方

    最近、会社の 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()
    
    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - growltestrunner の pynotify 対応 / pynotify の使い方