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...
  • Django アプリサーバ、gunicorn と fastcgi の比較

    http://gunicorn.org/images/large_gunicorn.png

    概要

    最近、会社では、fastcgi より、 gunicorn を使うのがどう? といわれました。gunicorn を触ったことない僕は fastcgi のロードテストも実際やったことなくて、メソッドについて、(prefork がいいか、 threadedがいいか) の読んでいたものを元にした推測しかできない状態で、知識足りないと思った。

    gunicorn は何かというと、python で作られた WSGI に対応するウェブサーバーです。同期、非同期ウェブアプリ両方対応できますし、作りがよくてかなりスピーディーそうですし、Django アプリを簡単に組み込めますし、python で運用が楽というのがポイントですね。もちろん、エンドユーザーが直接 gunicorn に接続するのではなく、 nginx のローダーバランサーでプロクシーのが一般的だと思っています。

    テストアプリケーション

    ということで、ちゃんとテストしようと思いまして、gunicorn と fastcgi prefork と fastcgi threaded を比較できるテスアプリを作りました。 bitbucket にアップ しましたので、ご参考ください。

    まず、 buildout を使いましたので、以下のコマンドでテストサーバーの環境を作ります。 mysql と nginx も必要なので、インストールしておいてください。

    python bootstrap.py
    ./bin/buildout init -d
    ./bin/buildout
    

    プロジェクトディレクトリのなか、 fastcgi_nginx.conf と gunicorn_nginx.conf ができるので、それぞれのテストをするときに、リンクを/etc/nginx/sites-enabled/ においてください。アプリは同じポートを使っているので、両方のconf ファイルを同時に有効にすることができない。gunicorn を有効にするときに、まず、fastcgi のリンクを /etc/nginx/sites-enabled/ から削除してください。

    mysql の DB は以下のSQLで作れます。

    CREATE DATABASE gunicorn_test CHARACTER SET utf8;
    

    DBを作った後に、syncdb コマンドを実行してください。管理者は特にいらないので、 'no' を入力して大丈夫です。

    ./bin/syncdb
    

    同じポートを使いますし、同時に立ち上げることができないんですが、それぞれのアプリサーバーは以下のように立ち上げます。

    ./bin/runfcgi                         (fcgi prefork)
    ./bin/runfcgi_threaded                (fastcgi threaded)
    ./bin/run_gunicorn -w <# of workers>  (gunicorn)
    

    テストアプリはトップページしかなくて、一つのフォームを持って、一ページくらい(20個)のコンテンツを表示するような処理をします。

    クライアントは以下のように環境をつくれます。

    cd testing/httpclient
    python bootstrap.py
    ./bin/buildout init -d
    ./bin/buildout
    

    クライアントは以下のように実行できます。クライアントのプロセス数と遅延時間を指定できます。遅延時間でテストの期間を指定できます。 デフォールトはプロセス数 500 で、遅延時間 10秒。この設定で、 500 プロセスの処理を10秒の間に伸びます。1秒目は50プロセス、2秒目は50プロセス。。。と言うふうにテストを行います。つまり、1秒間のテスト数はプロセス数わり遅延時間 (500 / 10 = 50)

    ./bin/python run_test.py <host> <# processes> <wait time>
    

    クライアントが行う処理は、トップページを表示し、フォームにテキストデータを入れて、POSTする処理です。なので、1プロセスは3回リクエストをします。 トップページを表示、POST・リダイレクト、トップページを表示 と言う風なテストを行います。なぜなら、ページの閲覧とデータの登録が激しいテストをしたかったわけです。

    テストの環境

    私は今まで知っていたかぎり、theaded はメモリを節約してくれるけど、コアを使いこなせなくて、 メモリが足りるなら、prefork の方がいいという認識でした。 gunicorn もマルチプロセスモデルを使っているので、同じくthreaded より早いはずだが、http の解析は fastcgi より若干遅いかなと思いました。gunicorn の作りが全く別なので、なんとも言えないけど、作りが一緒んであれば、http よりfastcgi が若干早いはず。

    このテストは EC2 上で行い、 ハイCPU ミディアム インスタンス 5台 (サーバー1台、 クライアント4台)。 なぜかというと、複数のコアを使いこなすかどうかをテストしたかったわけです。サーバーインスタンスは2ギガくらいメモリを持っているので、かなりのリクエストを処理するには充分足りるかと思っていました。

    gunicorn は 5ワーカー(リクエストを処理するプロセス)を使ってテストしました。 gunicorn はコア数 * 2 + 1 のワーカーを使うのを おすすめしています

    毎回テストを行う前にDBをクリアしました。

    echo "delete from perftest_mymodel;" | mysql -u root gunicorn_test
    

    テストを行った時に、 1クライアント500グロセス、10秒間にしました。ようするに、 2000ユーザーを同時にアプリをアクセスして、サーバーの処理できるリミットをテストしました。

    ./bin/python runtest xxx.compute.internal 500 10
    

    テストの成果

    fastcgi (threaded)
    ---------------------------
    
    Min time: 0.0689101219177s
    Max time: 23.1616601944s
    Average Time: 8.089163658s
    Errors: 1002 / 2000 (50.1%)
    
    fastcgi (prefork)
    ---------------------------
    
    Min time: 0.0668342113495s
    Max time: 21.9225800037s
    Average Time: 4.586471732s
    Errors: 561 / 2000 (28.1%)
    
    gunicorn
    ----------------------------
    
    Min time: 0.0718009471893s
    Max time: 21.6963949203s
    Average Time: 4.199061027s
    Errors: 293 / 2000 (14.7%)
    

    Min time は最低処理時間 (1プロセス)、Max Time は最高処理時間(1プロセス)、 Average Time は平均処理時間(1プロセス)、Errors は途中でエラーが出て失敗して、処理が出来なかった率とプロセス数。エラーが出たプロセスは時間の計測に入らない。

    まとめ

    fastcgi は思ったより頑張ってましたが、メモリが充分あれば、プロセスモデルを使った prefork メソッドを使うべきでしょうね。平均処理する時間が半分になり、途中でエラーが出る率も半分になりますよね。

    gunicorn の場合は処理時間がfastcgi の prefork と少し早く見えますけど、最低処理時間が少し高くなって、あんまりかわらないんですが、エラー数が prefork よりさらに半分くらいになりました。要するに、gunicorn は fastcgi prefork より多くのユーザーを扱うことができました。 ということは、本当の運用しているアプリケーションにgunicorn がリソースをより効率的に使う可能性が高いですね。 BeProud ではもうちょっと検討するのですが、非同期アプリケーションの仕事も増えていますし、将来にgunicorn を使うのが良さそうに見えます。

    もし、誰かがこのテストを使ったら、他のハードウエア、環境などでは、どういう結果がでるかを聞きたいと思っています。もしくは、テストについてのコメントがあれば、ぜひ宜しくお願いします。

    Send feedback   このエントリーを含むはてなブックマーク はてなブックマーク - Django アプリサーバ、gunicorn と fastcgi の比較
  • Google App Engine 1.3.6

    今日、 Appengine 1.3.6 がリリースされました。今回の大きいなリリースは以下の4つです。元のブログポストは こちら

    1. データの名前空間API

    これは結構おもしろくて、データを名前空間を使うと、ユーザーのデータを完全に分けることができる。たとえば、xxx.jp と yyy.jp は自分が作ったアプリケーションを使っているとすると、 xxx.jp のデータと、 yyy.jp のデータを名前空間で分けて、そのユーザーは自分のバージョンを見ると、自分のデータしかみれない。というような仕組みが作れるようになりました。

    日本語のドキュメントがまだないみたいだけど、 PythonJava のドキュメントを見てみてください。

    2. 高速画像サービング

    Picasa のインフラーを使っていて、リサイズ、切れ抜きなどの自動変換した画像を高速でサーブできるようになった。Python の get_serving_url か、 Java の getServingUrl を使うと画像のURLを取得できる。 このURLを使うと別のインフラを使うので、アプリケーションのCPUクオータとダイナミックサービングロード、データストアアクセスクオータなどがかからないというのが特徴です。

    3. カスタムエラーページ

    オーバークオータエラーとか、システムエラーの場合、カスタムページを使うことができるようになりました。 JavaのドキュメントPythonのドキュメント をお読みください。

    4. クオータ

    ようやく、 クエリーと、カウントの 1000件リミットを外すことにしました。これから、莫大クエリーは早く返ってこないだろうけれど、1000件リミットではなく、自分のCPUクオータと、リクエスト時間がネックになります。無料アプリケーションは Burst クオータも、有料アプリケーションの同じレベルになりました。 クオータリミットのドキュメント

    Send feedback   このエントリーを含むはてなブックマーク はてなブックマーク - Google App Engine 1.3.6
  • 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 を同時に使う方法
  • 何で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 の使い方