おかげさまで、うちの会社 も二歳になり、会社メンバーでお祝いに 鍋々&炭火七厘焼 マタギが街へ降りてきた に行ってきました。
主旨は、とにかくさまざまな動物の肉を喰らうというもので、 実際かなりいろんな肉を食べることができました。 ここまでたくさん食べたのは、フランスでジビエ・パーティをやったとき以来だったかもしれません。 豚、猪、鴨、蝦夷鹿、雉、牛、羊、熊、鶏、馬ぐらいは食べたでしょうか。 私の記憶では、どう考えても旨いとは言えない月の輪熊を一番美味かったとほざいていたJ氏の挙動不審しか残ってませんが。
いずれにしても、これまで支えてくださった会社スタッフ、並びに、応援してくださったジャーナリストのみなさまや同業者のみなさま、寛大な気持ちで見守ってくれている株主のみなさま、及び、うちを使ってやろうと考えてくださったお客様のみなさまに感謝いたします。 会社の状況も随分安定してきましたので、次の一年では今まで少し控えていた新しいことにもチャレンジしていきたいと思っています。 今後ともよろしくお願いいたします。
現代的な開発と言えば、Webベースですが、 残念ながら、既存のソフトウェアはファイルシステムに固執するコードが大量にあって、なかなか一筋縄では済まないことがたくさん生じてきます。 例えば、DSL的な考え方で、設定から簡単にクラスを作成して、動作をカスタマイズしたくなったりしますが、その場合、次のような障害にぶちあたります。
最初の問題は、必殺evalという手段もありますが、もうちょっと器用に操作したいこともあります。
二番目の問題は、コード中でインポートするのを遅らせるという手段もありますが、それだとプログラマが常に気をつけないといけないので、ちょっと大変です。
実のところ、Pythonなら、そう大した苦労をしなくても対処することができるので、簡単な実装を試してみました。 まず最初に問題に対しては、動的にオブジェクトを作ってインポートできるモジュールを。
from types import ModuleType import sys class DynamicModule(ModuleType): """This module may generate new objects at runtime.""" def __init__(self, name, factory, doc=None): super(DynamicModule, self).__init__(name, doc=doc) self._factory = factory def __getattr__(self, name): if name == '__path__': raise AttributeError('%s does not have __path__' % (name,)) obj = self._factory(name) if hasattr(obj, '__module__'): obj.__module__ = self.__name__ setattr(self, name, obj) return obj def dynamicmodule(name, factory): d = DynamicModule(name, factory) sys.modules[name] = d return d
そうすると、こういうことができるようになります。
def factory(name): return name dynamicmodule('dynamic', factory) import dynamic print dynamic.foo
foo
というオブジェクトはどこにも静的に定義していませんが、自動的に作成されます。
これで第一段階終了。
次に、二番目の問題。 すぐにロードできるとは限らないので、 本当に使うまで初期化を遅らせ、 後からクラスの継承関係やクラス自身の属性をいじれるような、 幽霊クラスを作ります。
def lazyclass(name, factory): class Ghost(object): def __getattribute__(self, attr): if attr[:2] == '__' and attr[-2:] == '__': return super(Ghost, self).__getattribute__(attr) klass = super(Ghost, self).__getattribute__('__class__') baseclasses, attributes = factory(klass.__name__) klass.__bases__ = baseclasses for key, value in attributes.iteritems(): setattr(klass, key, value) return getattr(self, attr) klass = type(name, (Ghost,), dict()) return klass
こうすると、例えば、こんなことができるようになります。
class Base(object): pass def factory(name): return (Base,), dict(name=name) Foo = lazyclass('Foo', factory) foo = Foo() print foo.foo
foo.foo
は最後の行に辿り着くまでは作られません。
ちなみに、わざわざBase
を作っている理由は、
Issue 672115: Assignment to __bases__ of direct object subclasses
に書いてあるPythonのバグのせいです。
最近のPythonでもまだ直ってないので、object
を直接使うことができません。
この二つを組み合わせれば、ファイルシステムに存在せず、かつ、静的定義なしに、新しいクラスやその他のオブジェクトをインポートしたり、使ったりすることができるようになります。
ちなみに、最初の問題に対しては、 plone.alterego でも同じようなことができますが、 そのままだとpickleできないオブジェクトができてしまうので、 自分で書いてしまいました。 また、二番目の問題に対しては、 ZODB にも似たようなコードが入っています。 実際、ZODB依存でよければ、zodbcode を使うと、いろいろ変なことができるようになります。
Zope/Ploneアドベントカレンダー 2009 のトリ--に選ばれたので、buildout関連のネタを少々。
みなさんご存知の通り、 buildout は、本来Zope3のために作られた構築ツールですが、 Zope2の完全egg化が2.12で果たされたので、 Plone や ERP5 等のZopeベースのプロジェクトでも積極的に採用されたり、 Pylons、Django、Repoze等、数多くの有名プロジェクトでも活用されたり、 今最もホットなPythonプロジェクトの一つと言えるでしょう。
さて、前振りはここまでとして、 buildoutを日常的に使うようになってくると、 もっと速く快適に使いたいという欲求がたくさん出てくるはずです。 そこで、buildout中毒になっている人がもっと気持ち良くなれる方法をいくつか紹介します。
まず、buildoutを使うと、簡単にローカル環境が構築できるので、複数バージョンを保持する機会が増えるでしょう。 普通にやると、似たようなbuildoutを使っていても、 eggは別々にダウンロードされて、インストールされます。 でもこれは大抵無駄ですよね。
そう感じたら、自分のホームディレクトリに
.buildout/default.cfg
というファイルを作り、
その中に、
eggs-directory
を定義します。
$ test -d ~/.buildout || mkdir .buildut $ test -d ~/eggs || mkdir ~/eggs $ cat <<EOF >~/.buildout/default.cfg [buildout] eggs-directory = $HOME/eggs EOF
こうすれば、デフォルトでeggの置き場所がホームの下のeggs
というところになって、
すでにあるeggをまたインストールし直したりしなくなります。
同じような話で、複数の環境で同じようなbuildoutを使っている場合、わざわざ PyPI から取ってくるのは無駄ですよね。 負荷が高くて遅いですし、たまに落ちていることもありますし。
そういう時には、 collective.eggproxy を使いましょう。 このツールは、PyPIのプロキシーとして動作し、 eggとかindex情報をキャッシュしてくれます。 使い方はとても簡単。
$ easy_install collective.eggproxy $ mkdir /tmp/eggs $ eggproxy_run
デフォルトで、localhost:8888を使用しますので、
あとは
buildout.cfg
で index
を指定すれば、大丈夫。
[buildout] index = http://localhost:8888/
さて、ますますbuildout中毒になってくると、いかにしてbuildoutの設定ファイルを共有するかが問題になってきます。
buildoutは extends
を使って、設定ファイルを継承させることができるので、当然こんな感じのことをしたくなります。
[buildout] extends = http://www.example.com/buildout/base.cfg
しかし、このままだと、buildoutを走らせる度、毎回アクセスしに行ってしまうので、遅いし、落ちていると機能しなくなります。
そこで簡単な方法は、extends-cache
を使うことです。
このパラメータを使うと、extends
の対象がキャッシュされ、毎回ダウンロードしなくて済むようになります。
[buildout] extends = http://www.example.com/buildout/base.cfg extends-cache = cache
しかし、それでも頻繁にbuildoutを走らせるとなると、やっぱり遅いって感じることはあります。
そういうbuildout依存症の人は、-N
を付けて走らせましょう。
$ bin/buildout -N
こうすれば、新しいバージョンのチェックを行わなくなるので、完全にアップデートする必要がない場合、圧倒的に速くなります。
このオプションをほとんどいつも使うようであれば、
~/.buildout/default.cfg
に書いてしまいましょう。
[buildout] newest = false
ただし、本当に新しくしたい時には、このオプションが設定に入っていると、明示的に -n
を付ける必要が出てきますので、注意しましょう。
$ bin/buildout -n
後もう一つだけ。 buildoutの動作の高速性はともかく、 設定ファイルをいじるのを楽にしたい人、 そんな人は mr.developer を見てみましょう。 きっと役に立ちます。
以上。
現実逃避がてらに、(第5回)Zope/Plone開発勉強会に行ってきました。 以下は報告です。
今回は、Python 2.6で追加された メソッド・キャッシュによる最適化を Zope でも享受したいということで、作業しました。
このバグ に記述されているように、既存の拡張が壊れる可能性があるため、 このオプションはデフォルトでは拡張モジュール内では有効になりません(コアに入っているタイプでは有効になってます)。 それゆえ、手作業で確認しながら修正していくしかありません。
ターゲットにしたのは、
の4つのパッケージです。 これでZope2で使うタイプとしては、大体カバーしていると思います。
成果としては、一応動くようにして、本家にパッチを投げました。
対応させて、テストを完了させるだけで精一杯だったので、これでどれだけ高速化されるのかは確認してません。
昨日「Web開発者に贈る昨今のPython事情」の発表を終えました。 たくさんの方に来ていただき、ありがとうございました。 発表資料を公開しましたので、ご自由にご利用ください。
終了後は、何人かの関係者(?)を無理やり付き合わせて、呑み会に突入してました。 付き合ってくださってありがとうございました。 大変楽しかったです。
補足:面倒くさいだろうと思うので、いくつか関連リンクを貼り付けておきます。
Before...
_ 山田太郎 [(20090812に対して)ゴチャゴチャ言うなら人に頼らんとお前が訴えてみろ。小者めが。]
_ irriliaeffede [hi, good site very much appreciatted]
_ mes [3周年も、おめでとうございます!]
_ workclothes [Thank You all for your help and perspectives.]
_ b0SamueL0b [+z$8frZdyL%68pSU/:>w<:E3.lG-!XIB]