もっと Python めたくらっす

From Evernote:

もっと Python めたくらっす

こんにちは。Python界のマニアック担当aodagです。
expert python読書会で、メタクラスの話題があったらしく、食いつかずにはいられない。
__metaclass__ に指定するのって、普通はtypeを継承するよね。
でも、別にtype継承じゃなくたって、ファクトリならなんでも使える。
実は、class構文は、func(name, bases, dct) を呼ぶだけの糖衣構文なんだ。
実際に単なる関数をメタクラスにしてclass定義で書いてみよう

>>> def dictish(name, bases, dct):
...     return dict((key, value) for key, value in dct.iteritems() if not key.startswith('_'))
...
>>> class MyDict(object):
...     __metaclass__ = dictish
...     a = 1
...     b = 2
...
>>> MyDict
{'a': 1, 'b': 2}

dictishは受け取ったdctから、名前が"_"で始まるもの以外を返すだけの関数だ。
__metaclass__にすると、dictishには、クラス定義の内容がわたってくる。
nameには、"MyDict"、baseには、"object"、そして、dctには、{"a":1, "b":2 ....} といったdictが渡される。
この関数の戻り値が、MyDictって変数に入るわけだね。
ということで、MyDictは、定義中の変数を含むdictになる。
ま、こんなことしてなんになるんだって疑問はあるだろうけど、このテクニックを使うとDSLを作るときのネタになるかもしれない。

勢いだけで書いた。反省はしていない。

Pylonsプロジェクトの新フレームワーク!? Pyramid

From Evernote:

Pylonsプロジェクトの新フレームワーク!? Pyramid

pyramidは、これまでrepoze.bfgとして開発されていたフレームワークが、repozeプロジェクトからpylonsプロジェクトに移動して名前が変わったっていうことらしい。

bfgからpyramidへの変更点(pypiからの抜粋)

  • makoテンプレートの管理がリソース管理に加わった
  • webob.exc をラップする pyramid.httpexceptions が追加された
  • makoテンプレートレンダラが標準サポートになった
  • configurator に pylonsのcontroller を扱う add_handler メソッドが加わった
  • configurator の引数に session_factory が加わった
  • configurator に set_session_factory メソッドが加わった
  • configurator に session_factory を設定した場合、 request.session で session オブジェクトを取得できるようになった
  • request に tmpl_context アトリビュートが追加された
  • pyramid.view.bfg_view は、 pyramid.view.view_config を正式な名前として扱う。bfg_view は今後も使える。
  • pyramid.session: signed_serialize と singed_deserialize API追加
  • pyramid.interfaces.IRendererInfo インターフェイス追加 レンダラファクトリのコンストラクタに渡されるようになる。
  • pyramid.interfaces.IBeforeRender イベントインターフェイス追加。
  • pyramid.response.Response クラス追加。webob.Response へのファサード(ドキュメント生成のためなので、既にwebobを使っていても直す必要はない)
  • zodb以外のpasterテンプレートを "imperative" な方法に変更
  • "declarative" な方法のテンプレートとして、 bfg_zcml を追加
pylons形式のハンドラが使えるようになったり、beakerを使ったセッションAPIやMakoのサポートなど、多くはpylonsからAPIを統合するような修正になっている。

さて、Pylonsフレームワークをベースにした、TurboGears2もあるわけで、(しかもTG2は、認証部分にrepoze.whoを使ってる) このあたりの事情どうなってるのかと。

Pythonでfinalなクラス

From Evernote:

Pythonでfinalなクラス

Javaだと、final class ってやると、継承できないクラスを定義できるよね。
Pythonでそんなクラス作る必要があるかどうかはおいといて、Expert Python読書会で作れるかどうかって話があったらしいから、メタクラスでやってみるよ

finalclass.py ::

class FinalClass(type):
    def __init__(cls, name, bases, dct):
        super(FinalClass, cls).__init__(name, bases, dct)
        for b in bases:
            if b.__class__ == FinalClass:
                raise TypeError, "%s: can't inherit %s" % (name, b.__name__)

class Foo(object):
    """
    """

class Bar(Foo):
    __metaclass__ = FinalClass

class Baz(Bar):
    """ """

FinalClassがメタクラスになってて、BarクラスやBazクラスが定義(メタクラスからクラスが生成される)ときにFinalClassのコンストラクタが呼び出される。
ここで、スーパークラスのリストが引数で渡されるので、その中にFinalClassのインスタンスが存在したら、エラーにしている。

実行結果
% python finalclass.py
Traceback (most recent call last):
  File "finalclass.py", line 16, in <module>
    class Baz(Bar):
  File "finalclass.py", line 6, in __init__
    raise TypeError, "%s: can't inherit %s" % (name, b.__name__)
TypeError: Baz: can't inherite Bar

追記
つづり間違いの指摘があったので修正 inherite -> inherit

続Python開発環境

From Evernote:

続Python開発環境

http://aodag.posterous.com/python で書き忘れたこと

個人設定

~/.buildout/default.cfg が個人用のデフォルト設定
ここで書くべきは、eggs-directoryとdownload-cacheの設定。

[buildout]
eggs-directory = /var/buildout/eggs
download-cache = /var/buildout/cache
といった感じ。

eggs-directoryを設定しない場合は、各プロジェクトにeggsディレクトリが作られて、そこにeggパッケージがインストールされるのだけど、みんな同じファイルだしね、1つのPCにいくつも同じファイルあるのはいやなので、1活して置く場所を設定しておける。

download-cacheは、sdist(zipとかtarボールのやつ)をキャッシュするディレクトリ。これを設定しておけばpypiが落ちててもなんとかなるかもしれない!

バージョン

まあ、例えばこういうことができるわけです。

[project1]
recipe = zc.recipe.egg
eggs =
    WebOb==0.9.8
    project
interpreter = py1

[project2]
recipe = zc.recipe.egg
eggs =
    WebOb==1.0.0
    project
interpreter = py2

bin/py1だとwebob-0.9.8を使うし、bin/py2なら、1.0.0を使う環境を同居させられる。
こういった互換環境も作成できるというわけです。

Python開発環境

From Evernote:

Python開発環境

Djangoはどうでもいいけど、Pythonで開発するときに準備しとくとよさそうなことまとめ

Pythonバージョン

2.6を使う。
なぜ3じゃないか。
WSGIの対応とかWeb3が出てきたり、distutils2がまだアルファってこともあるしね。
3.2が出るまではおあずけと思ってる。
追記
なぜ2.7じゃないか。
各種ディストリビューションにパッケージが出回ってないこともあるし、それほどメリット感じないので2.6。
2.7でも同じことはできます。

パッケージ管理
eggを基本にしましょう。
distutils2からはdist-infoとしてパッケージメタデータが正式採用になります。
今のうちに慣れておこうね。

  • virtualenv
  • setuptools/distribute
  • pip
  • buildout
環境構築
virtualenvで仮想環境を作って、buildoutをインストール(virtualenvすればpipとdisutributeはインストール済)

virtualenvですらシステム領域のsite-packagesに入れたくないので、virtualenv.pyをパッケージから取り出して使います。

http://pypi.python.org/pypi/virtualenv からダウンロード
解凍した中の virtualenv.py をホームディレクトリにでもおいときます。
新しく作るプロジェクトをnewprojectとして環境構築していきます。

$ python ~/virtualenv.py --no-site-packages --distribute newproject
$ cd newproject
$ bin/pip install zc.buildout
$ bin/buildout init
UNIXライクな環境を前提にしたけど、Windowsであれば、bin が scriptsになる。

Pythonパッケージを書いていく

プロジェクトテンプレート
PasteScriptはsetuptools/distribute前提のプロジェクトを展開するのに便利です。
buildout.cfgに書いて環境に導入します。

[buildout]
parts =
    paster

[paster]
recipe = zc.recipe.egg
eggs = PasteScript

と、buildout.cfgに書いたら、bin/buildoutを実行して環境を更新します。
zc.recipe.eggというのはbuildoutのレシピで、その名のとおりeggを管理するもの。
今作った環境にeggパッケージをインストールしますが、その影響範囲はparts([paster]とかで区切られているセクション)内に限られます。
新しいパッケージを作るには、以下のコマンドでベースを生成します。

bin/paster create newproject

この時点で、カレントディレクトリがnewproject そのしたにパッケージのディレクトリとしてnewproject、その中にpythonパッケージのnewprojectができあがっています。
きもいですね?
とりあえず、二番目のパッケージ用ディレクトリはsrcとでも変えておきましょう。
mv newproject src

このnewprojectもeggとして管理するためbuildout.cfgに追加します。


[buildout]
parts =
    paster
    newproject
develop = 
    src

[paster]
recipe = zc.recipe.egg
eggs = PasteScript

[newproject]
recipe = zc.recipe.egg
eggs = newproject
newprojectを追加したら、再度bin/buildoutを実行して反映。

Webアプリにでもしてみる
とりあえずwsgiアプリなぞ作りたければ、werkzeugやflask, repoze.bfgあたりが手軽。

src/setup.py の install_requires に "Flask" を追加して、再度bin/buildout。
ここで、[newproject]の環境にしか影響を与えないようにFlaskが導入されますが、コマンドがないので確かめようがありません。
[newproject]
recipe = zc.recipe.egg
eggs = newproject
interpreter = py

このように、環境専用のインタプリタを設定します。
くどいようですが、buildout.cfgを編集したので、bin/buildoutを実行します。
すると bin/py ができあがるので、実行してみます。
通常のインタプリタですが、sys.pathには、newprojectとFlask、その他必要なパッケージが追加されています。

とりあえず、Flaskのページにあるサンプルを、src/newproject/__init__.py に記述します。

bin/py src/newproject/__init__.py
とすれば、このwsgiアプリは動きます。

が!
なんでegg作ってるのにスクリプト実行するのかと。

ということで、まずmain関数を作ります。

def main():
    app.run()
ま、スクリプト実行されたときの内容を関数にするだけ。
次、これをsetup.pyのentrypointでコンソールコマンドとして実行可能にします。

entry_poinsts="""
[console_scripts]
serve_newproject=newproject:main
"""

console_scriptsセクションの中に、
コマンド名=モジュール名:関数名
と書きます。

さて、ここでbuildout.cfgを編集したわけではないですが、eggの情報を更新したので、ここでもbin/buildoutします。
反映させると、bin/serve_newprojectが作成されています。
実行すれば、さきほどと同じようにHelloアプリケーションが動きます。

っということで、OSやプラットフォームに依存せずにbuildoutに閉じた環境下で作成できます。
ソースとbuildout.cfgがあれば、同じ環境を一発で再構築できます。

まあ、eggやbuildoutの活用が、javaのjarとmavenみたいでなんか敬遠される感じがなきにしもあらず。
お手軽さを求めるなら、virtualenv + pipの環境作ればいいんじゃないかな。
そういうかたは、http://blog.mitsukuni.org/2010/10/25/python-development-environment によくまとまった文書があるから読んでみるといいよ。

Facebook盛り上がってきた?

From Evernote:

Facebook盛り上がってきた?

ここ2,3日くらいFacebookが活気付いてる。
それはそれとして、XMarksどうなるの?いまさらdeliciousとか戻りたくないよ。
ということで、Facebook のリンク投稿どうなんですかね。deliciousでは、まあ見たければ見ればいいよってくらいのスタンスで、ほぼ個人ツールとして使ってた。XMarksに移したのは単にブラウザブックマークがそのまま使えればいいかなというところ。

Facebookにリンク投稿するとフィードに流れるのが見える。twitterにリンクつきでPOSTするのと変わらないかもしれないけど、Facebookの場合はメタデータでちゃんとURLを持ってるし、あとからリンクだけ見ることもできるわけで。長期使うかどうか分からないし、結局収容つかなくなるだけなのかもしれないが、とりあえずshareブックマークレットは用意した。

そして、friendfeedの存在を思い出した。friendfeed, twitter, facebookで連携させてるWebサービスも、そろそろ収容つかなくなってるよなぁ。

Pavor使ってみた

From Evernote:

Pavor使ってみた

virtualenv, pip, setuptools, distribute, buildoutなどPythonの環境構築は結構あるが、さらにPavorを教えてもらったので試してみた。

Pavorはタスクをこなすためのスクリプトを一元的に管理するようなもの。
buildoutのレシピとどう違うんだという気がするが、Pavorのタスクはレシピよりも小さな感じで、関数を書いてtaskデコレータで登録するだけで使える。
タスクは、pavement.pyというファイルに書く。

from paver.easy import *

@task
def hello():
    """ Say hello
    """
    print "Hello, world!"

これで、helloタスクを使えるようになる

paverには標準でsetuptools互換のタスクを作るファクトリ、ファイルを操作するためのユーティリティなどがついている。

from paver.easy import *
from paver.setuputils import setup

setup(
    name="hoge",
    version="0.1",
)

これで、bidst_eggやsdist, register タスクが使えるようになる(中でsetuptools呼んでるだけなんだろうけどね)

さらに、generate_setupタスクがデフォルトで用意されていて、これを実行すると、paverを実行するsetup.pyが生成される。
minilibタスクでpaver-minilib.zipを生成させておけば、paverのインストールも不要になる。
この状態だといつものsetup.pyと同じように使えて、かつpavement.pyで定義したタスクも使えるようになる。

なんだかんだでプロジェクト固有のタスクを書いたりするわけで、そういったタスクをbuildoutのレシピを作るより簡単に作れるのは魅力かもしれない。
buildoutは、結構大掛かりで汎用的な感じなので、うまく補完してくれそうな気がする。
連携して使うのはまた今度試してみよう。

zope.interface

From Evernote:

zope.interface

最近Zopeが気になる。
僕がPythonを使い始めたきっかけの半分を占めている(もう半分はHow to become a hackerだ)わりには、Zope2.5以降ほとんど触っていなかった。
Zope3がいわゆるアプリケーションサーバーの形態から、コンポーネント集になり、がぜん興味がもどってきた。
というわけで、そのZope3のなかでも根幹と思われるZope Component Architectureを成す、zope.interface, zope.event, zope.componentあたりを調べてみようと思う。