2012-09-01
Pyramid ベースの軽量 CMS フレームワーク Kotti の紹介
ここ最近 Kotti アドオンの開発を行っていました。
Kotti は、Pyramid の上位でアプリ開発を支援するフレームワークです。いまのところ、軽量 CMS として開発が進められています。
Kotti を紹介するウェブサイトも構築されています。まだ概要程度の情報量ですが、日本語化もされているのでざっとご覧ください。
個人的にちょっと使ってみた感想としては、Kotti の CMS 機能もその完成度もまだまだベータ開発中といったところです。実用的なレベルまではもう少し時間がかかりそうですが、開発が活発に行われているので将来が楽しみです。私の場合、Pyramid を使ったアプリを開発してみたかったことと、普通の Web アプリにユーザー管理やコンテンツ管理の機能を簡単に追加したかったという動機で Kotti を利用してみました。
開発していると Pyramid の知識も必要になりますが、Node/Content という Kotti がもつリソースモデル、pyramid_tm によるリクエスト単位のトランザクション管理など、Pyramid 流の開発のプラクティスとして、フレームワークでどんなことをやっているのかを学ぶことが多かったです。また、後述する Kotti アドオンの開発も容易でした。
私が開発したものは Amazon Elastic MapReduce (Amazon EMR) を操作する Kotti アドオンです。
現時点の EMR の GUI と比較して、
- EMR 設定をリソースとして保存・再利用
- サービス単位にジョブフローをまとめる
- Keep Alive を有効にしたインスタンスへ追加ステップを実行
といった特徴があります。但し、まだまだ開発ステータスがアルファレベルであり、機能や UI は最低限のものですし、運用面の考慮も全くされていません。実験中といった感じです。
Kotti アドオンを開発する上で慣れが必要なのは、複数プロダクトの仕組みやドキュメントを調べないといけないことです。主なライブラリのドキュメントは以下です。
例えば、INI ファイル の設定においても、
kotti.site_title = Kotti with mapreduce kotti.secret = qwerty
などの kotti で始まる設定は Kotti 本体の設定ですが、
pyramid.default_locale_name = en pyramid.includes = pyramid_debugtoolbar
などの pyramid で始まる設定は Pyramid のドキュメントを調べないといけません。
また、
mail.default_sender = yourname@yourhost
は、pyramid_mailer の設定であり、これは Message オブジェクトの sender 属性 (envelope-from) に利用されます。
開発していて気になることや不具合があったときに Kotti 本体だけ調べても分からないこともよくあります。そんなとき、この機能を実際に提供しているのはどのプロダクトか、それはどういった仕組みかを理解するために、それぞれのプロダクトについての理解を深め、全体像を把握しないと解決策が分からないこともあります。
最初の取っ掛かりとしては、既に PyPI 上にも10個以上の Kotti アドオンが公開されているので、実際に動くサンプルとしてコードを眺めるのも参考になります。
まだまだ荒削りなフレームワークですが、その分、自分で開発したり、改善したりする余地がたくさんあります。Pyramid アプリ入門として Kotti アドオンを作るのでも良いように思います。もう少し経って Kotti の開発が落ち着いたら、ドキュメント翻訳にも挑戦してみたいところです。
2012-08-20
ローカル環境で複数の Sphinx ドキュメントを参照する
最近の Python パッケージは、Sphinx でドキュメントが書かれていることが多いです。
Sphinx でドキュメントが書かれているなら、そのパッケージのソースをクローンしてきて、ローカルでドキュメントをビルドするのも簡単です。私の場合、そのときの開発に用いるフレームワーク・ライブラリ・ツール等は、リポジトリからソースをクローンしてきて、最新版ソースを開発環境へインストールして、自分のアプリケーションを開発する上でデバッグしやすい環境を構築しています。また、そのドキュメントもローカルでビルドしています。
例えば、Kotti と Pyramid のフレームワークのドキュメントをローカルでビルドしてみます。
$ cd /path/to/Kotti/ $ python setup.py build_sphinx $ cd /path/to/pyramid/docs/ $ make html
このとき、前者は Kotti/build/sphinx/html/ に、後者は pyramid/docs/_build/html/ に HTML ドキュメントが生成されます。
ローカルで HTML ドキュメントを参照する際に Sphinx の検索機能を使いたいので HTTP サーバーを介して参照できると便利です。とはいえ、そのためにわざわざ HTTP サーバーを設定・構築するのもちょっと面倒です。前置きが長くなりました、そんな用途に「ちょっとだけ」便利な HTTP サーバーを作ってみました。
その名の表す通り、標準ライブラリの SimpleHTTPServer をちょっとだけカスタマイズしたものです。
先ず SimpleHTTPServer を使って、複数のドキュメントを見ようとすると次のようになります。
$ cd /path/to/Kotti/build/sphinx/html/ $ python -m SimpleHTTPServer 8001 Serving HTTP on 0.0.0.0 port 8001 ... $ cd /path/to/pyramid/docs/_build/html/ $ python -m SimpleHTTPServer 8002 Serving HTTP on 0.0.0.0 port 8002 ...
SimpleHTTPServer は、カレントディレクトリ配下のみを公開ディレクトリとして扱います。2-3のドキュメントならこれでも良いですが、10個のドキュメントをこの方法で見ようとすると、コマンドラインでサーバー起動するのが面倒だったり、どのドキュメントがどのポート番号か分からなくなったりします。
この煩わしさを解決するのが LittleHTTPServer です。次のようにコマンドラインオプションで指定します。
$ littlehttpserver -p 8003 -d /path/to/pyramid/docs/_build/html/ -d /path/to/Kotti/build/sphinx/html/ INFO: Serving HTTP/1.0 on 0.0.0.0, port: 8003 ...
http://localhost:8003 へアクセスすると、次のように表示されます。
これならローカルで見たいドキュメントが複数になっても煩わしくないですね。
2012/8/22 追記
コメントをもらったので -m オプションで実行できるように修正して再リリースしました。
$ python -m littlehttpserver -i path/to/top -d path/to/pkg1
2012-08-18
コマンドラインオプション付きの which コマンドを実装してみた
python | |
![]()
すべてのコマンドがperlで実装されているLinuxディストリビューション。誰得 / perl linux URL
Ruby / Python の勉強用に Perl Power Tools みたいに Ruby / Python Power Tools を作ってみようかと妄想したことがあるが、現実逃避ネタになりそうなので、手を出していない。
2012-08-18 00:22:06 via web
というツイートを見かけました。
以前、勉強がてらに
を作ったことがありましたが、もうあれから3年も経つんだなとコードを見返しながら感慨に浸っていました。
そんなとき、ふと which コマンドを実行したところ、コマンドラインオプションがあることに気付きました!
$ which
usage: which [-as] program ...
$ man which
WHICH(1) BSD General Commands Manual WHICH(1)
NAME
which -- locate a program file in the user's path
SYNOPSIS
which [-as] program ...
DESCRIPTION
The which utility takes a list of command names and searches the path for each
executable file that would be run had these commands actually been invoked.
The following options are available:
-a List all instances of executables found (instead of just the first one of each).
-s No output, just return 0 if any of the executables are found, or 1 if none are found.
Some shells may provide a builtin which command which is similar or identical
to this utility. Consult the builtin(1) manual page.
これは、、、
。。。
過去の私の実装に足りないものを発見してしまいました、もう再実装するしかない! (> <)
ということで、コマンドラインオプションを追加するだけなのですが、結構難しくて悩んでしまいました。
# -*- coding: utf-8 -*- import glob import os import sys import argparse from itertools import chain from os.path import join as pathjoin from operator import itemgetter def search(cmd, paths, is_all=False): for path in paths: for match in glob.glob(pathjoin(path, cmd)): if os.access(match, os.X_OK): yield match if not is_all: raise StopIteration def parse_argument(args=None): parser = argparse.ArgumentParser() parser.set_defaults(is_all=False, is_silent=False, commands=[]) parser.add_argument("-a", dest="is_all", action="store_true", help="List all instances of executables found " "(instead of just the first one of each).") parser.add_argument("-s", dest="is_silent", action="store_true", help="No output, just return 0 if any of the executables are found, " "or 1 if none are found.") parser.add_argument("commands", nargs="*") args = parser.parse_args(args or sys.argv[1:]) return args def main(cmd_args=None): args = parse_argument(cmd_args) env_paths = os.environ['PATH'].split(':') result = [] for cmd in args.commands: founds = list(search(cmd, env_paths, args.is_all)) result.append((0, founds) if founds else (1, [cmd])) status_code = max(map(itemgetter(0), result)) if not args.is_silent: cmd_paths = [paths for ret_val, paths in result if ret_val == 0] for cmd_path in chain.from_iterable(cmd_paths): print cmd_path return status_code if __name__ == '__main__': sys.exit(main())
実行結果はこんな感じです。
$ python which.py -a ls vi unknown /bin/ls /opt/local/bin/vi /usr/bin/vi $ python which.py -s ls vi unknown; echo $? 1
ついでに本当に which コマンドと動作が一致しているか、簡単なテストを書いてみました。
# -*- coding: utf-8 -*- import sys from subprocess import Popen, PIPE import pytest import which FILESYSTEM_ENCODING = sys.getfilesystemencoding() def get_system_which_result(args): cmds = ["which"] cmds.extend(args) p = Popen(" ".join(cmds), stdout=PIPE, stderr=PIPE, shell=True) out, err = p.communicate() return p.returncode, out, err @pytest.mark.parametrize("args", [ ["ls"], ["cd", "pwd"], ["non_existence"], ["-a", "vi"], ["-a", "ls", "vi"], ["-s", "non_existence"], ["-a", "-s", "ls", "vi"], ["-a", "-s", "ls", "vi", "non_existence"], ]) def test_which_command(args, capsys): my_ret = which.main(args) my_out, my_err = capsys.readouterr() sys_ret, sys_out, sys_err = get_system_which_result(args) assert sys_ret == my_ret assert sys_out == my_out.encode(FILESYSTEM_ENCODING) assert sys_err == my_err.encode(FILESYSTEM_ENCODING)
ソースは以下にあります。
こういったシンプルなツールを作るのはおもしろいですね。
2012-07-12
PyCon Taiwan 2012 イベントレポート Day 2
6月9-10日に開催された PyCon Taiwan 2012 に参加してきました。
前回の記事からちょっと間があきましたが、2日目 (10日) のカンファレンスレポートです。私は基調講演について執筆しました。
2012-06-28
fanstatic による Python パッケージを使った静的リソース管理
python, javascript | |
![]()
最近 Kotti で Web アプリを作ってみようと調査しています。但し、今日は Kotti のお話ではなく、たまたま更新差分を見ていたら、fanstatic という静的リソース管理ツールが新規に使われているのを見つけました。ちょっと調べてみると、とても良さそうに見えたので紹介します。
fanstatic って何?
特にドキュメントでパッケージ名の由来を見つけられなかったのですが、fan + static と区切ってみると名前を覚えやすいです。個人的に fan- という係りが fancy, fantasy, fantasista などを連想させて言葉の響きが良いですね。static リソースの管理の煩雑さを解消してくれる夢のようなツールを連想します。
さて、fanstatic は、スマートな静的リソースパブリッシャーとあります。
テンプレートに静的リソースの記述をインジェクションしてくれるので、パブリッシャーという表現をしているのでしょうが、それよりも javascript や css といった静的リソースを Python パッケージとして管理できる仕組みに私は驚きました。
パッケージ、ドキュメント、リポジトリはそれぞれ以下になります。
- fanstatic 1.0a : Python Package Index
- Fanstatic: Resource publishing for Pythonistas — Fanstatic 1.0.dev0 documentation
- fanstatic — Bitbucket
fanstatic で jquery を使ってみよう
fanstatic を使うと、どんなことができるか、クイックスタート を参考にしながら見てみましょう。
まずは fanstatic と js.jquery というパッケージをインストールします。
(fanstatic)$ pip install fanstatic js.jquery (fanstatic)$ pip freeze Paste==1.7.5.1 WebOb==1.2 distribute==0.6.24 fanstatic==0.11.4 js.jquery==1.7.1 wsgiref==0.1.2
簡単なサンプルコードから紹介します。
(fanstatic)$ vi quick.py # -*- coding: utf-8 -*- from fanstatic import Fanstatic from js.jquery import jquery def app(environ, start_response): start_response('200 OK', []) jquery.need() return ['<html><head></head><body>Hello World</body></html>'] if __name__ == "__main__": from wsgiref.simple_server import make_server fanstatic_app = Fanstatic(app) server = make_server('0.0.0.0', 8080, fanstatic_app) server.serve_forever()
通常の WSGI アプリを作成して、2行だけ追加します。jquery.need() は、HTML の <head> セクションに <script> タグを埋め込みます。あとは Fanstatic で WSGI アプリをラップするだけです。
...
jquery.need()
...
fanstatic_app = Fanstatic(app)
...
実行結果を見てみましょう。
(fanstatic)$ python quick.py
別のターミナルで
(fanstatic)$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /
<html><head>
<script type="text/javascript" src="/fanstatic/jquery/jquery.js"></script>
</head><body>Hello World</body></html>
ちゃんと <head> セクションに jquery の <script> タグが埋め込まれていますね。
作業前に js.jquery を pip でインストールしただけで使えるのが便利です。jquery の最新版をダウンロードしてきて、どこそこに展開して、パス設定はどうしようかな、、、と悩まなくて済むのが凄いところです。また、jquery のような、汎用的なライブラリは、みんなで共有してバージョンアップもパッケージ管理ツール (今回は pip) で行えた方が楽で良いですね。
任意のアプリから静的リソースを管理する
もう少し現実的なアプリケーションについて、リソースライブラリの作成 から見てみましょう。
ここでは静的リソースを含む Python パッケージを作成してみます。まずパッケージディレクトリとひな形を作成します。
(fanstatic)$ mkdir myapp (fanstatic)$ cd myapp (fanstatic)$ mkdir -p foo/static (fanstatic)$ touch foo/__init__.py (fanstatic)$ touch foo/static/base.css (fanstatic)$ touch foo/static/base.js
次にパッケージの setup.py を定義します。
(fanstatic)$ vi setup.py # -*- coding: utf-8 -*- from setuptools import setup setup( name="myapp", version="0.1", install_requires=["fanstatic", "js.jquery"], entry_points={ "fanstatic.libraries": [ "foo = foo.static:lib_foo", ], }, )
install_requires に fanstatic と使いたい js ライブラリを記述します。ここでは js.jquery のみを記述します。fanstatic がパッケージ内の静的リソースの場所を見つけられるように entry_points を使って定義します。ここが1つのポイントです。
では、先ほど作成した static ディレクトリを fanstatic から見つけられるように static.py に定義します。サンプルコードから紹介します。
(fanstatic)$ vi foo/static.py from fanstatic import Group, Library, Resource from js.jquery import jquery _resources = [jquery] lib_foo = Library("foo", "static") foo_js = Resource(lib_foo, "base.js", depends=[jquery], bottom=True) _resources.append(foo_js) foo_css = Resource(lib_foo, "base.css", bottom=True) _resources.append(foo_css) resources = Group(_resources)
大体見た感じで雰囲気は掴めますが、詳細に見て行きます。
lib_foo = Library("foo", "static")
Library オブジェクトは、名前と静的リソースの置き場所へのパスを引数に取ります。ここで lib_foo は、setup.py の entry_points で定義した名前を使う必要があるのに注意してください。
foo_js = Resource(lib_foo, "base.js", depends=[jquery], bottom=True)
Resource オブジェクトは、実際の静的リソース (js/css) を定義します。base.js は jquery を使うスクリプトなので depends=[jquery] を定義することにより、base.js より前に jquery.js が読み込まれるようにインジェクションされます。
resources = Group(_resources)
Group オブジェクトは、複数のリソースをまとめます。あとで紹介しますが、複数リソースのインジェクションを resources.need() のように実行できて便利です。
主要な点は紹介しました。実際に動かせるように残りのファイルも紹介します。
main プログラムです。せっかくなので css/js が適用されていることを確認できるように html を少し変更します。
(fanstatic)$ vi foo/main.py # -*- coding: utf-8 -*- from fanstatic import Fanstatic from static import resources as static_resources def app(environ, start_response): start_response('200 OK', []) static_resources.need() html = """ <html> <head></head> <body> <button type="button" id="sample_btn">Click Me!</button> </body> </html> """ return [html] if __name__ == "__main__": from wsgiref.simple_server import make_server fanstatic_app = Fanstatic(app) server = make_server('0.0.0.0', 8080, fanstatic_app) server.serve_forever()
base.css と base.js は、それぞれ以下の通りです。
(fanstatic)$ vi foo/static/base.css button { border-color: #666666; background-color: #E8E6E1; font-size: large; padding: 10px; }
(fanstatic)$ vi foo/static/base.js $(function() { $("#sample_btn").click(function() { alert("Hello World"); }); });
最終的なパッケージの構成です。
(fanstatic)$ tree myapp/ myapp/ ├── foo │ ├── __init__.py │ ├── main.py │ ├── static │ │ ├── base.css │ │ └── base.js │ └── static.py └── setup.py 2 directories, 6 files
それでは、パッケージをインストールして、実行してみましょう。
(fanstatic)$ python setup.py develop (fanstatic)$ python foo/main.py
ブラウザでアクセスして、ボタンをクリックすると、次のような画面を確認できます。
まとめ
fanstatic の強力さと利便性が分かる簡単なチュートリアルを紹介しました。
ここで紹介したソースは以下に置いてあります。
fanstatic リポジトリ を見ると、js. の名前空間で始まるライブラリがたくさんあります。ここに無ければ、自分でパッケージングして公開するのも良いですね。パッケージ管理の仕組みを、こんな用途にも使えるんだと再発見しました。おもしろいですね。
2012-06-27
PyCon Taiwan 2012 イベントレポート Day 1
6月9-10日に開催された PyCon Taiwan 2012 に参加してきました。
今回は、初日 (9日) のカンファレンスレポートです。私は基調講演について執筆しました。
2012-06-25
PyCon Taiwan 2012 イベントレポート Day 0
6月9-10日に開催された PyCon Taiwan 2012 に参加してきました。
初めて行った台湾ですが、羽田空港から飛行機で3時間半と近く、とても良いところですっかり気に入りました。そんな雰囲気が分かるレポートになっていると思います。記事は3人で執筆しています。
2012-06-14
detox で tox テストを並列実行しよう
複数の Python バージョンでテストを実行するツールに tox があります。
tox ツールそのものがとても便利なのですが、この tox テストを並列実行してくれるツールがリリースされました。
既に tox を使っている環境であれば、detox をインストールするだけで良いです。
$ pip install detox
使い方は tox と全く同じで特別な設定は不要です。tox コマンドを実行する代わりに detox コマンドを実行します。試しに実行してみましょう。
(test)$ detox
py26 create: /Users/t2y/work/repo/littlehttpserver/.tox/py26
GLOB sdist-make: /Users/t2y/work/repo/littlehttpserver/setup.py
py27 create: /Users/t2y/work/repo/littlehttpserver/.tox/py27
py32 create: /Users/t2y/work/repo/littlehttpserver/.tox/py32
py26 installdeps: :pypi:pytest, :pypi:pep8
py27 installdeps: :pypi:pytest, :pypi:pep8
py32 installdeps: :pypi:pytest, :pypi:pep8
installdeps py26- py27/ py32-
この例では、Python 2.6, 2.7, 3.2 の tox テストが並列実行され、依存パッケージのインストール中です。
最終的な実行結果です。
(test)$ detox GLOB sdist-make: /Users/t2y/work/repo/littlehttpserver/setup.py py27 sdist-reinst: /Users/t2y/work/repo/littlehttpserver/.tox/dist/LittleHTTPServer-0.1.2.zip py32 sdist-reinst: /Users/t2y/work/repo/littlehttpserver/.tox/dist/LittleHTTPServer-0.1.2.zip py26 sdist-reinst: /Users/t2y/work/repo/littlehttpserver/.tox/dist/LittleHTTPServer-0.1.2.zip py26 runtests: commands[0] py27 runtests: commands[0] py32 runtests: commands[0] _______________________________ summary ________________________________ py26: commands succeeded py27: commands succeeded py32: commands succeeded congratulations :)
1つの tox 環境でテストが通ったときに素早く他の環境でも実行したい、ちょっとした修正を行ったときに他の環境でも一応テストしてみたい、といった用途にとても便利です。何よりも CPU パワーが有り余ってるマシンで並列処理を実行するのは気持ち良いです。
detox は、並列処理に eventlet を使っていて、detox のソース自体も百数十行というシンプルなものです。eventlet の GreePool から greenthread でプロセスを実行しているようです。私は eventlet を使っているソースを初めて見かけたので興味深かったです (^ ^;;
2012-06-08
pytest のドキュメントを翻訳しました
Python のテストツールに pytest があります。ここ最近、徐々にテストを書くのに慣れてきて、さらにテスト設計や知見を高めようという思いが強くなってきました。
pytest は、wikipedia:設定より規約 を設計原則とするツールです。Python は明示する文化なのでやや違和感を感じる人もいるかもしれません。さらに xUnit スタイルではない funcarg という関数の引数をフックしてリソースインジェクションを行う仕組みを提供するなど、私にとっては新たな概念を学ぶのにおもしろそうな題材に見えました。
Holger Krekel 氏と出会い
先日 PyCon US 2012 に参加して、pytest の作者 Holger Krekel 氏と出会い、話すことができました。
最初に会ったのはカンファレンスが始まる前日のチュートリアルデーで、手持ち無沙汰な様子に見えたので、ランダムデータテストのやり方を相談してみると、pytest のパラメーターテストやフックの仕組みを教えてくれました。そこで顔を覚えてもらったせいか、その後のカンファレンスやスプリントを通して、サンプル実装したコードをレビューしてもらったり、アドバイスをしてもらったりして、最終的な成果物が以下のプラグインです。
カンファレンスも彼の発表を聴講しました。
pytest ドキュメント翻訳
日本へ帰国後、すぐに pytest の翻訳を始めました。
本当は2週間ほどで完了し、4月中旬には出来ていました。その後、メーリングリストで Holger 氏と翻訳ドキュメントの公開やメンテナンスについてやり取りをして、最終的には pytest 本体に翻訳ドキュメントを取り込んでもらいました *1 。とはいえ、やはり Sphinx の i18n 機能を使った方が良いかどうかを協議中だったりします。
以下で和訳されたドキュメントが公開されています。
今後のバージョンアップに伴うメンテナンスも継続的に行います。誤字/脱字、誤り翻訳などあれば、こちらまで ご連絡頂けると助かります。
*1:2ヶ月近くかかった理由は Holger 氏が5月末まで休暇中だったため
2012-05-24
pytest-pep8 を 0.7 から 0.8 にアップグレードしたらテストが失敗するようになった
タイトルは釣りです。
pytest で pep8 のテストを行う pytest-pep8 プラグインがあります。次のようなサンプルコードを用意します。
(test)$ vi sample.py x=3 class A(object): pass def f(x): return x
このサンプルコードを pep8 でチェックすると、たくさんのエラーが出ます。
(test)$ pep8 -r sample.py sample.py:1:2: E225 missing whitespace around operator sample.py:2:1: E302 expected 2 blank lines, found 0 sample.py:2:16: E701 multiple statements on one line (colon) sample.py:4:1: E302 expected 2 blank lines, found 1 sample.py:6:1: W391 blank line at end of file
同じように pytest-pep8 0.7 で実行します。
(test)$ py.test --version
This is py.test version 2.2.4, imported from /Users/t2y/.virtualenvs/test/lib/python2.7/site-packages/pytest.pyc
setuptools registered plugins:
pytest-pep8-0.7 at /Users/t2y/.virtualenvs/test/lib/python2.7/site-packages/pytest_pep8.pyc
(test)$ py.test --pep8 sample.py
========================= test session starts ==========================
platform darwin -- Python 2.7.3 -- pytest-2.2.4
pep8 ignore opts: (performing all available checks)
collected 1 items
sample.py F
=============================== FAILURES ===============================
______________________________ PEP8-check ______________________________
/Users/t2y/tmp/t/sample.py:1:2: E225 missing whitespace around operator
x=3
^
/Users/t2y/tmp/t/sample.py:2:1: E302 expected 2 blank lines, found 0
class A(object): pass
^
/Users/t2y/tmp/t/sample.py:2:16: E701 multiple statements on one line (colon)
class A(object): pass
^
/Users/t2y/tmp/t/sample.py:4:1: E302 expected 2 blank lines, found 1
def f(x):
^
/Users/t2y/tmp/t/sample.py:6:1: W391 blank line at end of file
^
======================= 1 failed in 0.01 seconds =======================
全く同じエラーが出ます。何もオプションを付けずに使っている分には問題ありません。
ここで pytest.ini に PEP8 のコーディングスタイルを無視するオプションを追加します。
(test)$ vi pytest.ini [pytest] pep8ignore = E302 E701
E302 と E701 のエラーだけを無視して、E225 と W391 のエラーは出力されるはずなのですが、、、
(test)$ py.test --pep8 sample.py ========================= test session starts ========================== platform darwin -- Python 2.7.3 -- pytest-2.2.4 pep8 ignore opts: E302 E701 collected 1 items sample.py . ======================= 1 passed in 0.01 seconds =======================
pytest-pep8 0.7 では、テストが成功してしまいます。これは pep8ignore に設定した無視したいエラー総数が、PEP8 違反としたいエラー総数よりも多いときにテストが成功してしまう不具合がありました。
pytest-pep8 0.8 ではこの不具合が修正されています。
(test)$ pip install pytest-pep8==0.8 (test)$ py.test --pep8 sample.py ========================= test session starts ========================== platform darwin -- Python 2.7.3 -- pytest-2.2.4 pep8 ignore opts: E302 E701 collected 1 items sample.py F =============================== FAILURES =============================== ______________________________ PEP8-check ______________________________ /Users/t2y/tmp/t/sample.py:1:2: E225 missing whitespace around operator x=3 ^ /Users/t2y/tmp/t/sample.py:6:1: W391 blank line at end of file ^ ======================= 1 failed in 0.01 seconds =======================
今度は正しく E225 と W391 のエラーが検出されましたね。
おそらく pep8ignore オプションを設定していると、1つ2つのエラーを見逃してしまっている可能性があります。あるとき pytest-pep8 をアップグレードしたら、ソースいじってないのにエラーが出るようになったと不思議に思うことがあるかもしれません。
参考までに修正された内容です。
もしかしたら、あるときに pep8 の API 仕様が変わったのかな?詳しく調べてはいません。



要望があるけどbitbucketにissuesが無かったのでこちらに書きます。
``python -m littlehttpserver path/to``で起動できると嬉しいです。
issues が private 設定になってた、これで見えるかな?やってみます!
https://bitbucket.org/t2y/littlehttpserver/issue/1/execute-as-a-python-script-with-m