YAMAGUCHI::weblog

海水パンツとゴーグルで、巨万の富を築きました。カリブの怪物、フリーアルバイター瞳です。

Pythonを取り巻く開発環境(PyCon JP 2012資料 #pyconjp)

はじめに

こんにちは、Python界の情弱です。情弱ながらPyCon JP 2012で1セッション持たせてもらえることになりました。予め資料を公開しておきますので、当日は色々と質問・意見して頂ければと思います。

各トピックは各トピックでの総論になっていますので、細かい部分は本文最後の参照にあるリンクを見るとより理解が深まります。

「なおここに書いてある内容は所属する団体とは関係のない、私個人の見解ですので、予めご了承下さい。」テンプレ終わり。

イベント PyCon JP 2012
発表日時 2012-09-16 11:00-11:45
作者 @ymotongpoo
URL http://2012.pycon.jp/program/sessions.html#session-16-1100-room357-ja

スライド

(追記: 2012/09/16 23:50:00)
発表の24:00頃のpy.testに関する記述で「プラグインが書けない」という話は間違っていました。(詳しくは本文参照)

アジェンダ

  • 自己紹介
  • 概要
  • ローカル開発環境
    • コード編集
    • Pythonの管理
    • パッケージの管理
    • ワークスペースの管理
    • テストの管理
  • 統合環境
    • 継続テストの管理
    • ドキュメントの管理

自己紹介

概要

Pythonでサービスや製品開発をする際の土台作りについての考察をご紹介し、これからPythonでの開発を本格的に行う人の指針、あるいは他言語での開発に慣れた人がPythonでの開発を行う際のジャンプスタートとなることを目標としてお話します。
システム的に解決できる部分しか触れていないので、コーディング規約、レビュー制度などの運用的な話はしません。「PythonでWebアプリケーションを作るためのベストプラクティスがどうのこうの」という話もしません。
なお、発表時点ではPythonは3.3.0 rc1が最新版となっておりますが、本発表では特に断りのない限りPython 2系、特に2系の最新安定版であるPython2.7.3を基準にご紹介致します。

コード編集

好きなエディタ使って編集すればいいと思いますが、PythonではPEP8や、言語仕様としてブロックをインデントにて表現するため、エディタの支援を使わないと余計な苦労をするかもしれません。

Pythonの管理

まず開発の基準にするPythonをどう管理するかについて。OSによって管理方法が変わると思います。今回は一番一般的なPython実装であるCPythonについての説明をしますので、IronPython、PyPy、Jythonといった異なる実装に関しての環境設定については触れません。
CPythonの場合、そのインストール方法は大きく分けると

  • ビルド済みバイナリ
  • 自前ビルド

の2点です。
それぞれ長所短所があり、状況に応じてお好きな方法を選択されればいいかと思います。手軽さで言えばビルド済みバイナリですし、他システムとの連携や細かい設定を考えると自前ビルドでしょう。ビルド済みバイナリを用いる場合にはインストール先を指定できるか否かにも注意したいところです。

Windows

Windowsではソースからビルドするためには、Microsoft Visual C++が必要となります。通常はMSIパッケージを使ってインストールします。

  • MSIパッケージ

MSIパッケージを用いる場合にはインストール先を指定できるため、複数バージョンをインストールする際にはインストール先を統一しておいたほうが良いと思います。

Linux
  • apt-get, yum, YaST
  • make

Linuxに関しては、ディストリビューション付属のパッケージ管理ツールを使う、あるいは自分でビルドをするかという選択肢が2種類あります。

Mac OS X

Mac OS Xの場合OSに紐付いたハードウェアが限定されるという特殊な事情があります。homebrew, MacPortsの場合は各パッケージシステムのインストールルート以下の特定の場所に、dmgでは /Library/Frameworks/Python.framework にインストールされます。makeをする場合はXcodeにあるgccが必要になります。

pythonz (pythonbrew)

pythonzはPython専用のインストールマネージャです。
インストールマネージャと言っても、ビルド済みバイナリをダウンロードしてくるわけではなく、

1. tarballのダウンロード&展開
2. make

を自動で行うためのショートカットツールです。

元々はpythonbrewというツールをforkしたものでしたが、pythonzの方が機能はシンプルです。
ただしpythonbrew系ツールに関しては個人的にはおすすめはしません。
詳細は参考にある私見をご参照頂ければと思いますが、簡潔にまとめると次のとおりです。

  • Pythonのインストール自体頻度は高くない
  • 余計なエントリポイントを増やす必然性を感じない
  • ビルド自体は難しくない

パッケージの管理

  • setup.py (distutils, setuptools, distribute)
  • easy_install (setuptools, distribute, distutils2)
  • pip
setup.py

setup.pyはPythonモジュールを配布する際に必ず必要になるスクリプトファイルで、ファイル内にはモジュールのメタデータを含んだsetup関数が記載してあります。setup.pyの詳細は公式ドキュメントにゆずるとして、setup.pyの現状についてご紹介します。

setup関数自体はPython標準ライブラリのdistutils内で定義されていますが、依存ライブラリの解消や、エントリポイントなどの重要な機能がないため、setuptoolsがdistutilsの拡張として開発され、さらにその拡張としてdistributeが開発されています。

distutils -> setuptools -> distribute

歴史的な経緯はスライドを参照して頂ければと思いますが、とりあえずsetup.py使うときはdistributeを使いましょう。

easy_install

easy_installコマンドでややこしいのは、これ自体はsetuptoolsモジュールを叩くためのスクリプトであって、setuptoolsモジュールが複数のパッケージで実装されているということです。歴史的な経緯はスライドにあるとおりですが、setuptoolsよりもdistributeの方が後発で、開発も活発であり、多機能です。そしてそのdistributeの機能を取り込んだPython3.3以降の標準機能をバックポートしたものがdistutils2です。

発表時現在で現状有姿でPython3に対応しているのはdistributeのみであり、distributeが現時点でデファクトスタンダートとなっています。distributeで使えるeasy_installのオプションは次の通り。

--upgrade,-U 強制アップグレード
--always-unzip,-Z zipは展開してインストール
--multi-version,-m 複数バージョンのインストールもしくはアンインストール

他のオプションは easy_install --help で確認してください。

pip

パッケージ管理ツールとして、リリース当初easy_installにはなかったパッケージのアンインストール機能を始め、多くの機能があるツールです。

install パッケージをインストール
uninstall パッケージをアンインストール
bundle pybundleを作成する(複数のパッケージを含むアーカイブを作る)
freeze 現在インストールされているパッケージを標準出力に表示
search PyPIを検索
zip 個々のパッケージをzipする
unzip 個々のパッケージをunzipする

pipの場合、requirements.txtと呼ばれるファイルにsetup.pyのinstall_requestsと似た形式でパッケージを指定しておくと、次のようにまとめてパッケージのインストールができます。

$ cat requirements.txt
MyApp
Framework==0.9.4
Library>=0.2

$ pip install -r requirements.txt

pipは便利なのですが、easy_installとの互換性がないので、使うのであればどちらか一方にしたほうがいいでしょう。またpipはeggを扱えないなどの制限があるため、例えばIPythonのようなモジュールはインストールできないことに注意しましょう。

ワークスペースの管理

Pythonでは、開発しようとする製品に応じて、利用するPythonやライブラリのバージョンが異なることが往々にしてあります。
OSのすぐ上にあるPython環境でこれらの切り分けを行うことは現状では難しいため、3rd partyツールを利用して各プロジェクト用のワークスペースを作成/管理します。

  • virtualenv (+virtualenvwrapper)
  • zc.buildout
  • venv (pyvenv from Python3.3)
virtualenv (+virtualenvwrapper)

virtualenvはPython仮想環境の管理ツールです。ここでいう仮想環境というのは、システムインストールされたPython本体から独立した、site-packagesを構築できるという意味です。

  • 仮想環境の作成

virtualenvパッケージをインストールしたら、 virtualenv コマンドで仮想環境を作成します。ここでは spam という環境を作成します。

$ virtualenv spam

virtualenvではデフォルトではpipが標準のパッケージ管理ツールとして選択され、仮想環境にインストールされるわけですが、distributeのeasy_installを利用することもできます。その場合は次のように仮想環境を作成します。

$ virtualenv --distribute spam

distributeをデフォルトにする場合は環境変数 VIRTUALENV_DISTRIBUTE を true にしておきましょう。

$ export VIRTUALENV_DISTRIBUTE=true
$ virtualenv egg
  • 仮想環境の利用

仮想環境 spam を利用するには作成した仮想環境の activate スクリプトを実行します。

$ source spam/bin/activate
(spam) $ easy_install -UZ bucho
(spam) $ deactivate
$

PS1に仮想環境名が表示されたら仮想環境がアクティブになっています。このときPATHの先頭に spam/bin が追加されているはずです。deactivate を実行すると環境変数が元に戻ります。

  • virtualenvwrapper

virtualenvではワークスペースの管理や仮想環境の切り替えなどを手動で行う必要がありましたが、それらを使いやすくvirtualenvのラップしてくれたのがvirtualenvwrapperです。
インストール方法等は公式ドキュメントやブログエントリ等に任せるとして、仮想環境の作成や切り替えなどが非常に簡単になります。

$ mkvirtualenv spam
(spam) $ mkvirtualenv egg
(egg) $ workon
spam
egg
(egg) $ workon spam
(spam) $ deactivate
$ rmvirtualenv egg
spam
$ workon spam
(spam) $
zc.buildout

昨日のセッションで北崎さんが説明されていたので資料等はそちらもご参照ください。zc.buildout(以下、buildout)はPython製のビルドツールで、本来であればワークスペースの管理用のツールではありません。しかしながら、十分ワークスペース管理に便利に利用できるため、ここでご紹介します。なおbuildoutは2.0は現状buggyなので1.6を使うことをおすすめします。

buildoutを利用する方法は次の2通りあります。

  1. packageとしてzc.buildoutをインストールする
  2. bootstrap.py単体を取得する
  • buildout初期化

まずbuildoutで環境を作成する場合は、専用のディレクトリを作成します。

1.で取得した場合は次の通り。

$ easy_install zc.buildout
$ buildout init

2.で取得した場合は次の通り。

$ curl "https://raw.github.com/buildout/buildout/master/bootstrap/bootstrap.py -o /tmp/bootstrap.py
$ python /tmp/boostrap.py init --distribute
  • buildout.cfg設定

この時点でディレクトリは次のようになっています。

  .
  ├── bin
  ├── buildout.cfg
  ├── develop-eggs
  ├── eggs
  └── parts

buildoutはデフォルトでターゲットディレクトリ内のbuildout.cfgというini形式ファイルを設定ファイルとして認識します。ここにビルドツールとして機能させるための設定を書くわけですが、今回は独立したsite-packagesを作るために利用します。

[buildout]
parts =
  dev

[dev]
recipe = zc.recipe.egg
eggs =
  sphinx
  sphinxcontrib-blockdiag
  sphinxcontrib-networkdiag
  PIL
interpreter = py
  • buildoutの実行

buildout.cfgの作成が完了したらbuildoutで環境を構築する。先ほどbootstrapした際に作成されたbinディレクトリに必要なコマンドが入っていますので、そちらから実行します。

$ bin/buildout
...
zip_safe flag not set; analyzing archive contents...
Got funcparserlib 0.3.5.
pyvenv (venv module)

pyvenvはvirtualenvに刺激を受けてPEP 405として提案された標準コマンドです。Python3.3より導入された機能ですので現時点ではまだ実用では使われないと思いますが、今後は上記のような3rd partyツールではなく、標準で使われる可能性も高いので、一応ご紹介しておきます。

  • 仮想環境の作成

仮想環境の作成は非常に簡単です。

$ pyvenv /path/to/workspace

virtualenvによく似ていますね。

  • 仮想環境の利用

作成した仮想環境のディレクトリに行き bin/ ディレクトリにある activate スクリプトを実行するだけ。

$ cd /path/to/workspace
$ source bin/acivate
(workspace) $ 

仮想環境内のパッケージの管理に関しては pip や easy_install をインストールする必要があります。

テストの管理

単バージョンテスト

単体バージョンテストはテストフレームワークを利用します。

  • unittest
  • doctest
  • nose
  • py.test
unittest, doctest

unittestとdoctestはPythonの標準ライブラリとして提供されるテストツールです。後述のnoseやpy.testほどの機能はありませんが、簡単なテストを書くだけなら十分に利用できます。
unittestでは名前の通りユニットテストを行うのに必要な基本的なツールは揃っていて、setup(), teardown()なども当然利用できます。
doctestは更に便利で、コード中にdocstringとして書いたものがそのままテストになります。簡単なスクリプトなどでわざわざ別にテスト用ファイルを作成するのが面倒なときには、ドキュメントついでにテストを書いておくと非常に便利です::

def square(n):
    """ square given number n

    >>> square(2)
    4
    >>> square(-5)
    25
    >>> square('one')
    Traceback (most recent call last):
      ...
    TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
    """
    return pow(n, 2)

if __name__ == "__main__":
    import doctest
    doctest.testmod()
nose vs py.test

noseとpy.testはどちらも似た思想で作られているので、どちらでもいいかなと思っていますが、個人的にはpy.testを推奨します。その理由は次のとおり。

  1. xUnit形式の出力がプラグインなしで可能(→Jenkinsとの連携が楽)
  2. 後述のtoxが依存しているため余計にインストールする必要がない
  3. テストデータとロジックを分けて書けるためすっきり書ける

あくまで個人的な印象です。noseのほうが先発だったこともあり、ユーザも多く多くの3rd partyプラグインやドキュメントが出ていて既存資産も多い方がたくさんいると思います。また、プラグインの機能はnoseにしかないので、特定の製品を長い期間メンテしていく上ではnoseの方が良いかも知れません。また情報も多いです。一方で後発のpy.testは必要な機能をデフォルトで実装していたため、単体で非常に利用しやすいテストランナーになっていると思います。

(修正: 2012/09/16 23:50:00)
コメントでid:Surgoより指摘を頂いて、py.testでもプラグインの実装ができることを知りました。いつの間にか出来るようになっていた!

こうなると今日話していた内容で考えるとnoseかpy.testの選択は既存資産が有るか否か、という点に落ち着きますね!

複数バージョンテスト

ライブラリの作成などに於いては、複数のバージョンに対応するために、複数のバージョンでのテストを行う必要があります。

  • tox
  • zc.buildout(一応紹介)
tox

toxは設定ファイルにしたがってテスト環境ごとにvirtualenvを使って仮想環境を自動作成し、さらに作成した仮想環境でテストランナーを走らせて、各テスト結果をまとめてレポートしてくれるという大変優れたツールです。さらに設定ファイルの書き方もそれほど難しくないのが素晴らしい。
toxの設定ファイルである tox.ini は次のように書けます。

[tox]
envlist = py27, py32

[testenv]
deps =
  pytest
  requests

commands =
  py.test \
    --junitxml=junit-{envname}.xml \
    test

これで、py27, py32という仮想環境を作成し、xUnit形式のテスト結果を返してくれます。py.testを使っていますが、toxとpy.testは開発プロジェクトが同じで、py.testはtoxに同梱されています。そのため連携も非常に良く、これもpy.testをおすすめする理由の1つです。

zc.buildout

先ほどワークスペースの管理でも利用しましたが、テストランナーとしてもbuildoutが利用できます。 使うrecipeは次のどれか。

  • zc.recipe.testrunner (通常のテスト)
  • pbp.recipe.noserunner (noseを使う場合)
  • z3c.recipe.scripts
  • collective.recipe.template

継続テストの管理

継続テストでは、継続的インテグレーション(CI)ツールを利用します。

  • Jenkins (+plugin)
  • Travis CI
  • Buildbot(紹介だけ)
Jenkins

Jenkinsは言わずと知れたJava製の継続的インテグレーションシステムです。単純に動作させるだけであればインストールは非常に簡単です。コミュニティベースの開発も盛んで、数多くのプラグインが開発されており、それらもダッシュボードからインストールが可能になっています。

Pythonプロジェクト用にもいくつかプラグインが存在していますが、その中でもShining Pandaはtoxが使いやすくなるので便利です。あるいは普通にPython Pluginを使えばよいでしょう。

Travis CI

Travis CIはGitHubでホストしているレポジトリを継続ビルドしてくれるサービスです。発表時現在はGitHubしかサポートしていないため、社内プロジェクト等だと利用は難しいかもしれません。
Pythonを含め14種類の言語が利用可能で、設定は .travis.yaml ファイルに書くだけ。まずはTravis CIの機能としてpy.testを走らせる例。

language: python
python:
  - "2.6"
  - "2.7"
  - "3.1"
  - "3.2"
install: 
  - pip install -r requirements.txt --use-mirrors
script:
  - py.test test

またtoxを利用している場合には次のようにtoxを走らせる指定をするだけ。

language: python
install:
  - pip install tox
script:
  - tox

ドキュメントの管理

ドキュメントはPythonコードとの連携のしやすさや、既存のreStructuredText資産を活かす意味でSphinxを利用します。手元では上記で触れたローカル開発環境に入れたSphinxでビルドを行います。Sphinxのインストールに関してはSphinx-Users.jpのサイトが詳しいので割愛しますが、``sphinx`` パッケージをインストールするだけです。
統合環境ではCIツールにてビルドを行うか、別途ドキュメント用ビルドツールを利用します。

  • Jenkins
  • ReadTheDocs
Jenkins

Jenkinsを利用する場合には、継続テストの場合と同様にドキュメントに更新があるごとにビルドを行うのが良いでしょう。Jenkinsの設定次第ですが、一番簡単な方法はJenkins用のワークスペースをそのまま公開することです。
大人数で閲覧する可能性がある場合はJenkinsでドキュメントをビルドする際に、専用Webサーバのドキュメントルートに
コピーするようなフローを用意してもいいでしょう。

ReadTheDocs

ReadTheDocsは2010年のDjango Dashで作成されたドキュメントホスティングシステムです。Sphinxプロジェクトを検知し、有名なVCSと連携してドキュメントがプッシュされると自動でビルドを行います。PythonだけでなくPHPC++など多くの言語で開発が行われているプロジェクトのドキュメント公開に利用されており、PSFも出資しています。

現在、サービスとしての http://readthedocs.org/ と、パッケージとしてのReadTheDocsが存在します。オープンソースのものであれば前者を利用するのが良いでしょう。後者はまだ不完全ではありますが、自前でホスティングすることも可能です。執筆現在は最新版をビルドするとsyncdbするところでコケますので注意。

参照

参考資料
  • 書籍

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

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


原著はこちら
Expert Python Programming

Expert Python Programming


Pythonプロフェッショナルプログラミング

Pythonプロフェッショナルプログラミング