読者です 読者をやめる 読者になる 読者になる

zc.buildoutやろうとしてハマった話…もとい、ただのメモ

エキスパートPythonプログラミング

どうもみなさんこんばんは。なんとなくアドベントカレンダーに参加してみたところ、全然マークしていなかったid:mopemopeにいきなりキラーパスをもらってビックリして思わず社会の窓が開いてないかどうか確認してしまったkuenishiです。うーん、アルコール入ってないとなんかキレがないね。

日曜プログラマの僕がPythonネタで書くってーなると大したことはないのですが、最近docxbuilderとかFlaskとかいじってます。それを使い始める前にハマっていたのでその辺りのことです。

pipとかeasy_installとかpythonとか

私はPower Mac 8500とか言ってた頃からのマカーなのでMacOSを使っているのですが、MacOSにはプリインストールの/usr/bin/python(たいてい2.6系)と、MacPortsで入ってしまう/opt/local/bin/python(2.6~3.2までよりどりミドリ!!)があって、プリインストールのほうのeasy_installが/opt/local/lib/python2.6/site-packages/にモノを突っ込んだりしてすごく残念です。せっかく頑張ってpipで環境整えたら、Linuxで動かなくてaptitudeとpipとeasy_installのどれ使おう…いやそもそもどの組み合わせでもなんか動かねえぞこれ、とかいう残念な事態になってしまいます。「Pythonは進化が速いから仕方ないだろ、自分で何とかしろ」「ちょっと頑張ればできるでしょ」とスノッブ・クエニシ・ザ・マッチョが言うものの、これでは初心者に優しくありません。その辺り、初心者でも動かし始められるPHPの方が優れていると思います。さあlet's import php!!!…じゃない。いかんいかん。

問題はパッケージ管理

ひとくちにパッケージ管理といっても、MacPorts, ports, pkg_src, APT, yum, pip, easy_install, gem, pear, CPAN, .... ハァハァ、沢山ありますね。まあどれもユーザの同じ問題を解決してくれるわけです。プログラムの依存性とバージョン解決です。OSに特化したやつ、プログラミング言語に特化したやつ、どっち使ったらええの、となります。最初はOS特化のやつを使いますが、だんだん物足りなくなってプログラミング言語特化のやつを使います。が、これも、いろいろなバージョンのものを使いたい!となっただけですぐに破綻します。それを解決するのがvirtualenvもしくはzc.buildoutなわけです。virtualenvはちょと大げさなので(真剣に大げさにやりたいならKVMとか使おう!)、今回はzc.buildoutを使います。これはざっくりいって、

  • OSに入っているPython環境と隔離した環境をつくってくれる
  • Pythonライブラリの依存性とかバージョン解決をして適当に隔離環境にインストールしてくれる
  • OSの環境を汚さないでローカルに全部入れてくれる
  • 割と簡単、ポータブル

ものです。さらに素晴らしいのは専門家がTwitterにいて(@shibumizukawa)、しかも勉強会に顔を出せば彼らがすぐにつかまることです。気軽に日本語で尋ねることができます。素晴らしいです。ありがとう、しぶみずかわさん!

やっと本題

さて、実際にそういったbuildout環境を構築するには、やはりハマるところがいくつかあります。buildoutに辿り着くまでにバージョンとか依存性とかで散々ハマってきた私にとっては藁にもすがる思いであります。藁にすがったつもりがさらにハマっていくというのを防ぐためにいろいろとメモったので、ここに残しておきます。

Hello

適当にhelloというプロジェクトをつくることにします。

$ hg init hello
$ cd hello
$ emacs setup.py

最初はsetup.pyをつくっておきます。なくてもよい…みたい?なのですが、みんな置いているみたいなのでつくっておきます。

from setuptools import setup, find_packages
name = "hello"
version = "0.0.1"

setup(name=name)

これはminimal caseです。興味がある人はいろいろ見てみるとおもしろいです。そしてbootstrap.pyを入れます。これがないと何も始まりません。その設定ファイル、buildout.cfgもつくります。

$ wget "http://svn.zope.org/*checkout*/zc.buildout/trunk/bootstrap/bootstrap.py"
$ emacs buildout.cfg

こんな風にします。

[buildout]
parts = env start_server

develop = .

[env]
recipe = zc.recipe.egg
eggs =
     flask
     flask-sqlalchemy
     pysqlite
     hello

interpreter = mypy

[start_server]
recipe = zc.recipe.egg:scripts
eggs = ${env:eggs}

entry-points = start_server=hello.server:main
scripts = start_server

buildout.cfgの細かいルールを覚える必要はなさそうです。なんとなくmakefileみたいなもんだと思っておけばよいです。buildoutセクションがallで、そこのpartsがenv, start_serverに依存している。buildoutはそれを見て、envとstart_serverというターゲットを解決してくれます。envはその名の通り、環境。あんな風に書いておけばbuildoutが適当にeggsをpypiからとってきてパスを通してくれます。名前を適当に指定しているように見えますが、pypiで見つかった名前をきちんと入れてください。最後にあるhelloというのは自分のことですが、どうもこれはsetup.pyを見ながら作っている模様*1
interpreterは、パスを通したpythonインタープリターです。これを使うと、envで指定したeggsが使えます。start_serverセクションでは、実際にサーバーを起動するスクリプトを入れておきます。entry-pointsの指定が案外ややこしくて、いつも'.'で十分なはずなのにコロンとか使っちゃってるの。むーつかしー。eggs = で参照している ${env:eggs} はenvのセクションで指定したeggsのことになるようです。ふっしぎー。

次はserverのスクリプトを用意します。buildout.cfgで指定した通りのパスhelloにserver.pyというファイルをつくります。 最初のところからいうと、hello/hello/server.pyですね。

from flask import Flask
import flask, os, re

app = Flask(__name__)

@app.route('/')
def hello_world():
    return "hello world! (flask) hogehgo"

def main():
    app.run(debug=True) # ,ssl_context='adhoc')

if __name__ == '__main__': main()

このスクリプトは適当でよいです。あとMANIFEST.inとかいろいろ作るのですがまあよしなにやってください。そんなにハマりません。ここまでできたら、さて構築。

$ python bootstrap.py -d init
...
$ ./bin/buildout
...
$ ./bin/start_server
 * Running on http://127.0.0.1:5000/
 * Restarting with reloader...

buildoutのところがちゃんと動かないひとはbin/buildout -v などとやってみてください。大抵はproxyとかでハマってるんじゃないですかね。それでtimeoutしまくってて、うーんおかしーなーとかいいながら時間を無駄にするアホは僕だけで十分です。
Flask何がすごいって、これでserver.pyを書き換えたらreloadしてくれるところです。OMakeバリにヒャッハーですね。今のところDebianMacOSで、easy_installとか使ってできなかったことがzc.buildoutでできるようになりました。
参考になりましたか?これを読みながらやったところできっとハマるかと思います。そのハマりこそがあなたのチカラになります。Rubyistのみんな!彼にチカラを分けてくれーっ!!!

おわりに

次は、最近MacBook Airを買っておきながら中で結局仮想化したDebianを使うという外道っぷりに畏敬の念すら抱いた@tokibitoことid:nullpobugにしようかと思います(その節はいろいろ教えてくれて、ありがとう!)。よろしくです。
そうそう、しぶみずかわという人は実在しません。わたしがお世話になったのはしみずかわさんです。ありがとうございました。みんな!この人はプロだから、僕みたいにバカバカしい質問を気軽にしてはいけないよっっ!!質問する前に自分でちゃんと考えていろいろ試してみるんだよ!

*1:setup.pyにもちょと工夫がいるみたい