pytest, mock, testfixture!

(´・ω・`)はい。こんにちは。パーフェクトPythonも無事?発売されて一安心なaodagです。 まあ、なんかテスト関連でよくいろいろ言ってる気がするのですが、最近ツールを変えたので、覚書を。 これまで:

  • nose
  • unittest2
  • mock

という組み合わせでした。 unittest2の豊富なassert系メソッドとnoseの豊富なプラグインってところです。 テストケース自体はunittest2で書いて、noseのテストランナーを使うと。   が、最近もうこの2つはpytestに変えました。 だいたいいつも使ってるようなのは、pytestでまかなえるし、pytest-covがあれば十分かな。 追加で使うツールとして、mockは続投、testfixtureが便利な気がするので、使い始めてます。 それぞれのツールの使い方はだいたいそこらへんのマニュアルとか見てくれればいいと思うので、pytestでどのようなことをしてるかってのを。 zopeやpyramid界隈では、テスト用のダミーやフィクスチャ(データフィクスチャじゃないよ。まったく某フレームワークのせいでフィクスチャっていうとデータのことばかり...)を testing というモジュールにまとめておくのが慣例です。 で、pyramidにもtestingモジュールがあり、 testing.setUp, testing.tearDownというのが、テスト用のコンポーネントレジストリを初期化するフィクスチャです。 pytest的なフィクスチャにしてみます。

@pytest.fixture

def config(request):

    from pyramid import testing
    config = testing.setUp()

    def fin():
        testing.tearDown()

    request.addfinalizer(fin)

    return config
 

pytest.fixtureデコレータでフィクスチャの登録をします。 なかでやってるのは、testing.setUpの呼び出しと、testing.tearDownをテスト後に呼び出すための設定です。 requestがそういうフックを設定するためのオブジェクトです。 このフィクスチャを使うには、テストケースの引数にconfigという名前をつけます。 pytestが、フィクスチャの名前と同じ引数のところに自動でフィクスチャの戻り値を渡してくれます。

from .testing import config

def test_it(config):

    config.include('test.under.the.module')
 

楽ですね。 testfixtureは、汎用的なフィクスチャがそろっています。 日付やログ、ファイル出力、ファイルシステムの操作など、ユニットテストでよくあるめんどうな部分をカバーしてくれてるので、便利に使えるようになりたいものです。 テストツールのセットアップ テストツールが増えてくるとそれをインストールするのも簡単にすませたいところです。 テストツールようのrequirementsを用意しといてもいいのですが、 setup.pyに書く場所があるので、そっちを使います。

setup(name="...",
      ....
      tests_require=["pytest", "mock", "testfixtures"],
      ....
     )
 

このように書いておくと、 python setup.py test とすれば、現在のディレクトリにインストールされます。 うざいですね。 extras_requireを使うと、現在の環境にインストールできます。

tests_require = ["pytest", "mock", "testfixtures"]

setup(name="test.under.the.package",
      ....
      tests_require=tests_require,
      extras_require={"testing": tests_require},
      ....
      )
 
このようにしておくと、  python setup.py develop easy_install test.under.the.package[testing] などのコマンドでテストツールまで、インストールできます。
長くてめんどうなので、 setup.cfg にエイリアスを作ります。
[aliases]
dev = develop easy_install test.under.the.package[testing]
 
最近のpip (1.2以上のバージョン)も extrasをサポートしています。
pip を使う場合は pip install -e .[testing] というコマンドで、ほぼ同じようになります。
これらのツールはpython3対応もしてるので、長い付き合いができそうです。

新年的なアレ

From Evernote:

新年的なアレ

新年的なアレ

  • coffescript,dart,typescriptのどれか1つを勉強する
  • 自炊がんばる
  • ポモドーロとかGTDとか、なんかそんなので改善なやつをする
  • (´・ω・`)ブログは30エントリ目指す

New Year's Python Meme 2012 #2012pythonmeme

From Evernote:

New Year's Python Meme 2012 #2012pythonmeme

New Year's Python Meme 2012


1. What’s the coolest Python application, framework or library you have discovered in 2012 ?

obviel, that is zopish javascript framework.

2. What new programming technique did you learn in 2012 ?

  • ZCA
  • Client Side MVC

3. Which open source project did you contribute to the most in 2012 ? What did you do ?

pyramid, repoze. Mainly, i've committed to support python3.

4. Which Python blog or website did you read the most in 2012 ?

reddit, stackoverflow

5. What are the three top things you want to learn in 2013 ?

async programming: gevent, tornado, zeromq
HTML5 and Mobile Web


6. What is the top software, application or library you wish someone would write in 2013 ?

  • pip with new generation packaging
  • acceptance testing tool with sphinx documentation
  • issue tracker that replaces Trac and Redmine.

2012の新年のあれがどうなったか

  • Pylons/Pyramid勉強会を4回開く

PyConJP の併設イベントを入れて4回開催した!達成!

  • Pyramidベースのフレームワークを作る

(´・ω・`)なう、検討中。

  • Web関連ライブラリのPy3対応にコミットする

PyramidとかRepozeに貢献できたと思うので達成!

  • 海外のPyConその他のカンファレンスに1回は行く

EuroPython行ったので達成!

  • 英語力強化(アウトプット方面)

(´・ω・`)がんばってる。

  • ブログを50エントリ書く

(´・ω・`)50どころか2桁いってない事実..

 

(´・ω・`)と、いうことで、3/6達成でした。

英語とか

PySPAアドベントカレンダー

From Evernote:

PySPAアドベントカレンダー

PySPAアドベントカレンダー

はい。皆勤賞にして、すべてのいただきますを唱和したaodagです。

公式には全10回、4食ずつで40回の「いただきます」「ごちそうさまでした」をしてきたことになりますが、
(´・ω・`)どうでもいいですね。

「いただきます」の動画が残ってました。

(´・ω・`)はい、ほほえましいですね。

半年に1回ペースで10回あったということは、5年以上たったわけですね。
その間の僕のPythonへの関わりはどのように変わったか考えてみました。

興味の対象
TurboGears,WSGI,Pylons,Repoze,buildout,pyramidなどなど

ひたすら強力なものを追い求めた結果ですが、まあ、僕が選ぶものがどれだけ流行らないのかという歴史です。

というわけでなんか振り返りとしてもぐだぐだなので、この辺で。

IT業界Heavy Metal Advent Calendar 2012 22日目

http://www.adventar.org/calendars/50 こちらからのアドベントカレンダー。

 

眠気を吹き飛ばすメタルを紹介ということらしいので

SavatageのJesus Saveです。

アルバムがロックオペラってことで全体通して聞いてると眠気が襲ってくることになりますが:D

 

次は速弾き野郎Impellitteli の kindom of light

その前のアルバムでブレそうだったこともあり、このアルバムでは吹っ切れたようにひたすら速弾き。

コーディングのお供に最適です。

で、ロブ・ロックのボーカルが地味にはまってきたりもするわけで。

 

という2枚を紹介しときます。

(あ、両方Chrisだ。

2012 Pythonアドベントカレンダー 18日目 PasteDeployを知ってるかい?

From Evernote:

2012 Pythonアドベントカレンダー 18日目 PasteDeployを知ってるかい?

PasteDeployを知ってるかい?

さて、2012 Pythonアドベントカレンダーの18日目です。

一応Webフレームワークに関連してればよいということで、普段あまり言及されないPasteDeployの話をしておきます。

PasteDeployとは?
ああ、プロジェクトのテンプレートを展開するやつですよね?とかしたり顔で聞いてくる奴は自分で穴掘ってもぐっといてください。
PasteScriptのpaster createのことではありません。
paster serveのほうです。

PasteDeployは、iniファイルでWSGIアプリケーションやミドルウェア、サーバーの設定や構成を管理するものです。
で、そのiniファイルを paster serveに渡せば WSGIアプリケーションが実行されるわけですね。

WSGIがわからない人は、 http://shomah4a.bitbucket.org/advent_calendar/2012/wsgi.html を読んでね。

ちょっとだけWebOb
まあ、とりあえずHelloなWSGIアプリケーションでも作って動かしてみましょう。

helloapp.py

from webob.dec import wsgify

@wsgify
def hello(request):
    return u"Hello, world!"

shomah4a の例と違うのはWebOb使ってるところ。

ではこれを動かすので、 webob と pastescriptをインストールしましょう。

$ pip install webob pastescript

pasterコマンドが使えるようになりますが、これでいきなりWSGIアプリケーションを動かせるわけではありません。
WSGIアプリ、ミドルウェア、サーバーの構成を定義する ini ファイルが必要です。

PasteDeployしてみよう
ひとまず最少の構成では、WSGIアプリとサーバーを指定すればOKです。

hello.ini

[app:main]
paste.app_factory = helloapp:main

[server:main]
use = egg:paste#http
host = 0.0.0.0
port = 5000

さて、今あるのは単に helloというアプリケーションだけです。
pastedeployでは、直接アプリケーションを参照するのではなく、アプリケーションを返す関数(ファクトリ)を指定するようになっています。

helloapp.py

def main(global_conf, **app_conf):
    return hello

今のところ特に設定を必要としてないので、さっきの hello をそのまま返すようにします。

$ paster serve hello.ini

で動きますね。

設定を使う
実際のところWebアプリケーションだと、データベースへの接続だとかそういった設定が必要です。
が、ここでSQLAlchemyの話を出すと説明しきれないので、messsageとか渡すことにしましょう。

設定をもとにアプリケーション作成するので、クラスにします。

class HelloApp(object):
    def __init__(self, message):
        self.message = message

    @wsgify
    def __call__(self, request):
        return self.message

def main(global_conf, message, **app_conf):
    return HelloApp(message)

で、 hello.ini は以下のように変更します

[app:main]
paste.app_factory = helloapp:main
message = Hello, world!

(´・ω・`)さて、ここからが本編です。
ええ、ここまでは、PasteDeploy自体知らないなんていう人が多いのではないかということで書きました。
なんせ、このアドベントカレンダーは8割がたDjangoネタですからね。おまいらフレームワーク1つしか知らないのか。

PasteDeployの設定を共有する
とりあえず設定値を2つにしてみます。

helloapp.py

class HelloApp(object):
    def __init__(self, message1, message2):
        self.message1 = message1
        self.message2 = message2

    @wsgify
    def __call__(self, request):
        return u"{0} {1}".format(self.message1, self.message2)

def main(global_conf, message1, message2, **app_conf):
    return HelloApp(message1, message2)

hello.ini

[app:main]
paste.app_factory = helloapp:main
message1 = Hello
message2 = world!

まあこんなもんです
が、たまにmessage1だけを変えたい時があるかもしれません。

そんなときには
セクションを追加します。

[app:hello]
use = main
message1 = Hi

このように use でセクションを指定して、変えたいところだけ設定します。
このappを実行するには、 paster serve に -n オプションで hello と指定します。

$ paster serve hello.ini -n hello

という、pastedeployで設定の共有や上書きができるよ。というお話でした。
これで、 production.iniやdevelopment.iniなどで、設定を共有しながら、デバッグフラグやDB接続など、必要な部分だけを変更して扱うことができますね。

19日目は @podhmo お願いします。

virtualenvなしでもプロジェクトごとのライブラリを管理する方法 #python

ついうっかりvirtualenvせずに setup.py develop してしまったわけですが。

しかもwindowsだったので、権限でのストップがかからずそのまま大元のsite-packagesにざっくりインストールされました。

さて、setup.py developの場合、pipとは違い、eggごとのディレクトリにインストールされます。

で、これだけじゃPYTHONPATHに入ってこないわけで、site-packages内の *.pth ファイルにパスが羅列されているというのが、結構前に調べた結果。

インストールされてしまった、eggたちも、全部削除するんでなく、easy-install.pthの記述を消してしまえば、問題ないわけです。

さて、このeggたちを有効活用できないものか。

というか、そうするためにeggはディレクトリが分けられていまして、 pkg_resources.require() を使って、sys.pathに追加できます。

ポイントは、 pkg_resources.requireを使うと、そのeggが依存するeggもsys.pathに追加されるということ。

なので、今開発しているパッケージが bucho という名前で、きちんとsetup.pyに依存ライブラリを指定してあるなら、 pkg_resource.require('bucho') と書けば一発で必要なライブラリがsys.pathに追加されます。

さて、ちょっと目先を変えて、setup.pyから実行できるサブコマンドはサードパーティライブラリからも追加できます。

有名どころでは、noseのnosetestsコマンド、sphinxのbuild_sphinxコマンド。

で、先ほどの pkg_resources.requireですが、setup.pyから起動されるサブコマンドでは、setupされてるeggを内部でrequireしてあるようで、 コマンド自体のライブラリ(たとえばnose)があらかじめimport可能な状態なら、問題なく setup.py nosetestなどできます。

さらに、pkg_resources.requireはバージョン指定も可能です。また、easy_installコマンドは-m (マルチバージョン)オプションがあり、このオプションをつけると easy_install.pthに追記されずにインストールされます。このあたりの仕組みを使いこなせば、どんどん増えていくvirtualenvに悩まされることはなかったでしょう。

という、もうすぐpackaging/distutils2が出てしまう段階で、こんなTIPを調べたことに腹正しさを感じつつvirtualenvしなおしています...

BPStudy #54で発表してきたよ!

ということで第二部で、Python3移行の話をしました。

組織的な移行じゃなくて、オープンソースコミュニティでのライブラリ対応の話です。

http://www.slideshare.net/aodag/bpstudy54-python3

 

なんか誤解されちゃってる気がするので、補足しとく。

Python3自体は別に難しくないし面倒でもないです。

スライド中の苦しい話は、2と3と両方で動くように書くので面倒なんです。

そういうの気にしなければ、Python3使うのなんて簡単なんだからね!

Python2.4から3.2までで通用するexcept句の書き方

まあ3系文法との互換性って2.6からの話なんで、2.5とかの非互換は結構あきらめていたのですが。

PasteDeployは2to3なしで、2.5から3.2をサポートしてるのですね。

except句って、2.5だと

except Exception, e:

という書き方で、2.6以降で導入された

except Exception as e:

じゃないと3で通用しないわけです。

PasteDeployが、これをどう解決してるかというと、

except Exception:
    e = sys.exc_info()[1]

 

 

な、なるほど。これなら確かにどちらのバージョンでも通用する!

が、こんな書き方するよりは、2.5を切り捨ててしまいたい。

 

http://www.voidspace.org.uk/python/articles/porting-mock-to-python-3.shtml

 

ちなみに2/24のBPStudy #54 で、こんな感じの話をしますのよ。

http://connpass.com/event/268/

まだ枠あいてるっぽいです。どうぞよろしく。