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...
  • 何で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 の使い方
  • BPStudy #29 テスト駆動開発

    BPStudy #29 のテスト駆動開発の話でペアプログラミングで、Last Recently Used キャッシュ (LRU)を自動テストやりながら、実装しようという部分がありました。

    最初に僕は二つのリストで10分くらいで実装したんですけど、やっぱりパフォーマンスが出ないと思ったから、時間が終わったまでに、pythonの辞書で書き直した。

    最終版はこれでした。

    lru.py

    #:coding=utf8:
    
    class LRU(object):
    
        def __init__(self, size=2):
            self.size = size
            self.name_list = []
            self.value_dict = {}
    
        def put(self, name, value):
            if name not in self:
                self.name_list.append(name)
            self.value_dict[name] = value
            if len(self.name_list) > self.size:
                old_name = self.name_list[0]
                del self.name_list[0]
                self.value_dict.pop(old_name)
    
        def get(self, name):
            if name in self.name_list:
                self.name_list.remove(name)
                self.name_list.append(name)
                return self.value_dict[name]
            else:
                return None
    
        def __contains__(self, key):
            try:
                self.value_dict[key]
                return True
            except KeyError:
                return False
    
        def __setitem__(self, key, value):
            self.put(key, value)
    
        def __getitem__(self, key):
            return self.get(key)
    
        def __len__(self):
            return len(self.name_list)
    

    lru_test.py

    from unittest import TestCase
    
    from lru import LRU
    
    class LRUTestCase(TestCase):
        def test_put(self):
            lru = LRU()
            lru.put("monjudoh", "monju")
            self.assertEquals(lru.get("monjudoh"), "monju")
    
        def test_multi_put(self):
            lru = LRU()
            lru.put("monjudoh", "monju")
            self.assertEquals(lru.get("monjudoh"), "monju")
            lru.put("monjudoh", "monju2")
            self.assertEquals(lru.get("monjudoh"), "monju2")
            self.assertEquals(len(lru), 1)
    
        def test_loss(self):
            lru = LRU()
            lru.put("monjudoh", "monju1")
            lru.put("ian", "monju2")
            lru.put("test", "monju3")
            self.assertEquals(lru.get("monjudoh"), None)
    
        def test_use(self):
            lru = LRU()
            lru.put("monjudoh", "monju1")
            lru.put("ian", "monju2")
            self.assertEquals(lru.get("monjudoh"), "monju1")
            lru.put("test", "monju3")
            self.assertEquals(lru.get("ian"), None)
            self.assertEquals(lru.get("monjudoh"), "monju1")
    
        def test_size(self):
            lru = LRU(size=4)
            lru.put("monjudoh", "monju1")
            self.assertEquals(len(lru), 1)
            lru.put("ian", "monju2")
            self.assertEquals(len(lru), 2)
            lru.put("test", "monju3")
            self.assertEquals(len(lru), 3)
            lru.put("test2", "monju4")
            self.assertEquals(len(lru), 4)
            lru.put("test3", "monju5")
            self.assertEquals(len(lru), 4)
            lru.put("test5", "monju6")
    
        def test_bigger(self):
            lru = LRU(size=4)
            lru.put("monjudoh", "monju1")
            lru.put("ian", "monju2")
            lru.put("test", "monju3")
            self.assertEquals(lru.get("monjudoh"), "monju1")
            lru.put("test2", "monju4")
            lru.put("test3", "monju5")
            self.assertEquals(lru.get("ian"), None)
            self.assertEquals(lru.get("test"), "monju3")
            self.assertEquals(lru.get("test2"),"monju4")
            self.assertEquals(lru.get("test3"),"monju5")
    
    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - BPStudy #29 テスト駆動開発
  • pytyrantはpython-tokyotyrantよりずっと速い

    夏のPython温泉Bob様が作ってくれたピュアーパイソンクライアントpytyrant酒徳さんpython-tokyotyrantより速いという話を聴いたとmoriyoshiさんに言った。それで、moriyoshiさんはprofileのテストを作ってくれたけど、結果として、pytyrantとpython-tokyotyrantはあまり変わらないのが出た。

    でも、このコードは一つのスレッドでテストしている。複数のクライアントが同時に接続している場合はどうかと思って、今日テストを作ってみた。

    これを実行するとpytyrantのほうがずっと速い.

    python-tokyotyrant
    ********************
    Running 20 threads took 6.755 seconds
    Running 20 threads took 5.392 seconds
    Running 20 threads took 5.516 seconds
    Running 20 threads took 27.191 seconds
    Running 20 threads took 30.575 seconds
    Running 20 threads took 34.699 seconds
    pytyrant
    ********************
    Running 20 threads took 1.748 seconds
    Running 20 threads took 1.736 seconds
    Running 20 threads took 1.716 seconds
    Running 20 threads took 8.922 seconds
    Running 20 threads took 8.716 seconds
    Running 20 threads took 8.746 seconds
    理由が分からないけども、python-tokyotyrantはPyRexを使ったわけですかね?
    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - pytyrantはpython-tokyotyrantよりずっと速い
  • virtualenv, virtualenvwrapper, pip を使う方法

    あるプロジェクトの依存は特定なバージョンじゃないとダメな場合が結構多いと思いますけど、最近、pythonの仮想環境を簡単に作れるようになりました。virtualenv と virtualenvwrapper と pip の組み合わせを紹介します。

    virtualenvは環境を作ってくれるライブラリで、virtualenvwrapperはその作った環境を簡単に管理してくれるツールになります。

    まず、virtualenv と virtualenvwrapper をインストール.

    easy_install virtualenv
    easy_install virtualenvwrapper
    

    これで、virtualenvwrapperのコマンドを使うには、bashスクリプトを設定しないといけない。以下の行を .bashrc に追加

    export WORKON_HOME=$HOME/.virtualenvs
    source /usr/local/bin/virtualenvwrapper_bashrc
    

    コンソールを再起動して終わり。仮想環境を作りましょう。

    mkvirtualenv myproj
    

    これで、プロジェクトの仮想環境ができました。 これで、pythonのバージョンも確定し、仮想環境にインストールするライブラリのバージョンも確定になる。mkvirtualenvを実行すると、作った環境に入る。また、次に使う時は workon myproj を実行して、環境に入る。

    しかし、よく使われてるeasy_install は virtualenv と連携できなくて、どうしても、システムのpythonディレクトリにパッケージをインストールしてしまう。

    pip が助かる

    easy_install が簡単すぎて、分けわかんないメッセージも出したりして、virtualenvと相性悪くて、何とかできませんか?って話があったきかっけ、pipが生まれた。pipをインストールすればvirtualenvにすいすいとパッケージをインストールできる。virtualenvを使っている間にインストールする。

    wget http://pypi.python.org/packages/source/p/pip/pip-0.4.tar.gz
    tar xzf pip-0.4.tar.gz
    cd pip-0.4
    python setup.py install
    

    Update: pipはちゃんと、virtualenvに入ってくれるので、easy_install でもインストールできる。

    workon myproject
    easy_install pip
    

    これで、pip が仮想環境にインストールする。これから、pip install で仮想環境にパッケージをインストールできるようになった。それで、プロジェクトに必要なパッケージをインストールし、pythonのsite-packagesが汚くならないので、安心

    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - virtualenv, virtualenvwrapper, pip を使う方法
  • Python温泉 06/2009

    Python温泉に参加しに行ってきました。今回はいろいろ話が上がりましたけど、特に未来的な話が一番面白かったのかなと思います。ウェブなど日本なサービスがどうなるのかが特に気になります。アメリカでは、クラウドサービスやマイクロペイメントサービスが色々栄えそうだが、日本では頭が固いから、そのまま栄えない可能性が高いので、オリジナルサービスやアイディアを考えないと行けない。多言語化がやっぱり複数の言語をサポートすることのみではなくて、文化や地元の事情を考えながら、地元の機能を作ること。

    あと、 酒徳 さんが作ったtokyotyrant ライブラリのC バインディング python-tokyotyrantBob が作ったピュアPythonクライアント実装 pytyrant に意外と負けるという話が上がって、 moriyoshi さんがどうしても信じなくて、 プロファイリングテスト を作ってくれた。やっぱりローカルでCバインディングのほうが早い。ネットを通るとどうなるのが気になりますけど。

    僕の作ったものは 前と書いた通りdisqus のAPIの Python クライアント を作って見ました。アップデート実装がまだ出来ていないので、ご注意。

    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - Python温泉 06/2009
  • 明日からPython温泉

    明日からPython温泉に参加しに行ってきます。課題は下記のとおり

    どこまでできるかわかんないけど、たぶんあまり眠らないでしょう。

    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - 明日からPython温泉
  • Python 例外のひどい仕様

    Pythonの例外オブジェクトは苦手です。例外のメッセージが何でもasciiとして扱われることがひどい。

    In [1]: t = ValueError("テスト".decode("utf8"))

    In [2]: print t
    ---------------------------------------------------------------------------
    UnicodeEncodeError                        Traceback (most recent call last)

    /home/ian/src/project/<ipython console> in <module>()

    UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

    In [3]: t = ValueError(u"テスト") # Unicode object's don't work either

    In [4]: print t
    ---------------------------------------------------------------------------
    UnicodeEncodeError                        Traceback (most recent call last)

    /home/ian/src/project/<ipython console> in <module>()

    UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

    Djangoのテストフレームワークで、doctestを使おうと思ったら、例外のメッセージがasciiじゃないとダメというのが判明

    u"""
    >>> test(-1)
        Traceback (most recent call last):
            ...
        ValueError: エラーですよ!
    """

    とやっても、うまくうごかない。以下のUnicodeDecodeErrorがでる

    ======================================================================
    ERROR: Doctest: app.tests
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/home/ian/.virtualenvs/test/lib/python2.5/site-packages/django/test/_doctest.py", line 2175, in runTest
        test, out=new.write, clear_globs=False)
      File "/home/ian/.virtualenvs/test/lib/python2.5/site-packages/django/test/_doctest.py", line 1403, in run
        return self.__run(test, compileflags, out)
      File "/home/ian/.virtualenvs/test/lib/python2.5/site-packages/django/test/_doctest.py", line 1291, in __run
        got += _exception_traceback(exc_info)
      File "/home/ian/.virtualenvs/test/lib/python2.5/site-packages/django/test/_doctest.py", line 269, in _exception_traceback
        return excout.getvalue()
      File "/usr/lib/python2.5/StringIO.py", line 270, in getvalue
        self.buf += ''.join(self.buflist)
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 24: ordinal not in range(128)
    
    ----------------------------------------------------------------------
    

    Update: moriyoshiさんにより、Python 2.6 でちゃんと動くみたい

    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - Python 例外のひどい仕様
  • GAE Hackathon Disc. 3 全文検索

    GAE Hackathon Disc. 3 に参加してきました! 私とid:a2c (Twitter) がGoogle Appengineの日本語が対応する全文検索エンジンを作ってみました。

    GAE では、データストアが Entity と言う概念で作られてるけど、Entityを検索する時に、データを完全一致しないと、データを取れないので、全文検索が難しくて、以下の状況になってる。

    • 一応、SearchableModelというクラスを継承すれば、英文検索が出来るけど、日本語テキスト検索が全くできない。(英語でも結構ひどい)
    • SearchModelで、英語検索しても、スペース文字で単語単位で切るので、単語を完全一致しないといけない。(つまり、informationがテキストに入ってるけど、infoで検索しても出てこない)
    • SearchableModelでは、3文字以下の単語はインデクスしてくれないので、ほとんどの日本語はアウト
    • 上の点の関係で、3文字以下の検索キーワードもアウト
    • 検索キーワードが無視された場合、何でも、引っかかるので、検索結果が分かりにくい

    a2cさんが以前に、いろいろ調べたり、試してみたりしてくれたので、いろいろ助かった。いい情報を取れたので、すごくいい話が当日にできました。

    SearchableModelのAPIは基本的にいいと思ったので、SearchableModelと同じように、日本語対応できるSearchableModelを使いたいなと思いました。こう書けば、わりと簡単に検索できる。

    from google.appengine.ext.search import SearchableModel
    from google.appengine.ext.db import *

    class Document(SearchableModel):
      title = StringProperty(u"Title")
      text = TextProperty(u"Body Text")
    ...
    Document.all().search(keyword)
    ...

    まず、SearchableModelがいろいろ、自分を参照したので、継承するのが難しかったから、別のモジュールにコピーして、forkすることにした。それで、この辺に単語の分け方を a2cが作ってくれた ngram実装に切り替えた。(ngramとは?) それで、SearchableModelのモジュールを変えるだけで、googleの実装と同じように使える。

    検索キーワードをngramで分けて、インデクスを検索すると、ちゃんと部分的にデータが引っ張ってくる。 http://saichugen.appspot.com/ でテストアプリを見れる。コードはbitbucketで公開されてる。

    これから、a2cさんともっと検索結果を取れるようにするのと、インデクスの容量をへらしたりするのと、分け方を自分で実装できるapiを導入したいと思うので、ぜひ期待してください :)

    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - GAE Hackathon Disc. 3 全文検索
  • Werkzeugのreverse URL処理

    ほぼ汎用イベント管理ツールの改善をしようと思ってて、実際にコードを見るとURLを使ってるのが気になった。

    WerkzeugのURLルーティングでDjangoのreverse関数みたいにURLの名前からURLに変換できるのかなと調べて、ある方法がありました。名前からじゃなくて、endpointから変換するけど。。。

    from werkzeug redirect as wredirect
    from urls import url_map

    def reverse(**kwargs):
      c = url_map.bind('')
      return wredirect(c.build(**kwargs))

    ...
       return reverse('form', dict(key=key, slug=slug))
    ...
    Send feedback このエントリーを含むはてなブックマーク はてなブックマーク - Werkzeugのreverse URL処理