ごちゃっとしたプロジェクトで、setup.pyみたいなファイルだけでそのプロジェクトのビルドや配置が完結できない場合に役に立つのが paver。
でも、そのためにみんなに paver をインストールしてもらうのが気が引ける、という場合、 paver minilib というコマンドを実行すると、 paver-minilib.zip というzipファイルが生成され、Paverの基本的な機能はこれで全部カバーされる。
このファイルは23KBなので、チェックアウトしたツリーに突っ込んで配布しても良いし、いっそのこと直接バージョン管理システムに突っ込むのもありだろう。
なんだけど、 paver をインストールしないと、 paver コマンドを実行できない。なので、 minipaver とかいう名前でこんな起動スクリプトを付けると良いだろう。
#!/usr/bin/env python import sys sys.path.insert(0, 'paver-minilib.zip') import paver.tasks paver.tasks.main()
で、どうせなら、これを paver のタスク定義をしている pavement.py にくっつけてしまおう。
$ cat pavement.py #!/usr/bin/env python # coding: utf-8 from __future__ import with_statement # paver.easy.pushd の為 import sys if __name__ == '__main__': # モジュール検索パスに paver-minilib.zip を追加. sys.path.insert(0, 'paver-minilib.zip') import paver.tasks paver.tasks.main() sys.exit() # 以下、タスク定義 from paver.easy import * @task def hello(): '''Say hello''' print 'hello' $ ./pavement.py hello ---> pavement.hello hello
で、実は今日の本題は、 sys.exit
Python のモジュールのロードは、そのモジュールを定義しているソースファイルの実行に他ならない。
むしろ、Python スクリプトの実行と思っているものが、そのソースファイルを '__main__' という名前のモジュールとしてロードしていると考えた方が良い。
さて、 pavement.py 自体を実行可能なスクリプトにした場合、 これは '__main__' としても読みこまれるが、paverの設定モジュールとして 'pavement' という名前のモジュールとしても読みこまれる。
完全名('paver.easy' みたいに、そのモジュールを含むパッケージをドットで繋げた名前)が同一の名前のモジュールであれば、その中身が実行されるのは最初のロードだけだが、複数の名前でロードされると実行も複数回になってしまう。
$ cat foo.py import foo print 'hello from', __name__ $ python foo.py hello from foo hello from __main__
通常は、pavement.py の直下には副作用のある処理は書かず関数内に書くので、ファイルが2回実行されても同じ関数が __main__ と pavement の2つのモジュール内に生成されるだけで、実害はない。
が、意図しない名前空間に勝手に関数を定義したり変数を定義したりするのは気持ち悪いので、モジュール内の変数定義や関数定義の前に if __name__ == '__main__': sys.exit() を書くことを推奨したい。
$ cat bar.py #!/usr/bin/env python # coding: utf-8 import sys if __name__ == '__main__': import bar sys.exit() print 'Hello from', __name__ $ ./bar.py Hello from bar