feedparserとwerkzeug.contrib.atomで時間の型を合わせる

仕事でfeedaggregatorを作る必要があって、家でやるならplaggerでいいじゃんとか思うんだけど、職場でそれやっちゃうとメンテナンスできるヒトがいなくなっちゃって困るし、インストールも時間がかかるので、requestsとfeedparserでちゃっちゃと作ることにした。

実は非同期のクローラーが作ってみたくかったので、Tornadoでつくったのもあるんだけど、サイトアクセスのタイミングがよく分からないのと、最初はそれほど多くのサイトにアクセスしそうにないので同期型のクローラーを動かすことにした次第。

feedparserでparseするとupdate_parsedとかpublished_parsedはtime.struct_time型になるのでwerkzeug.contrib.atomに食わせるにはdatetime型に変換する必要がある。

この場合にはfromtimestampメソッドを使えばいい。

updated=datetime.fromtimestamp(mktime(e.updated_parsed))

時間の変換は結構めんどくさいね。

XML-RPCでPYPIにパッケージ一覧の問い合わせをする

PYPIに登録してあるパッケージ全てのリストと、ここ一日の間で変更されたパッケージのリストを取ってくるサンプル。

import xmlrpclib
import time

proxy = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
proxy.useragent = 'pep381test/0.1'
packages = proxy.list_packages()
print len(set(packages))

lastweek = int(time.time()) - 60 * 60 * 24 * 1

changes = proxy.changelog(lastweek)

# [name, version, last_modified]
print len(changes)

acupは上手くいかなさそうだ

この前の思いつきでpipのキャッシュにインストールしないでダウンロードだけするスクリプトを書けばいいじゃんと思ったので、

pip install --no-install --no-deps packages

でいいんじゃないかなぁと軽く考えてたんだけど、pipのソースコード読んでみたらバージョンをpypiに問い合わせてから対応するバージョンのキャッシュがあるかどうかを調べる流れになっているので単純にキャッシュすればいいってわけじゃなかった。

バージョンを返すHTTPサーバーは必須みたいだなぁ。

pipでインストールしたパッケージをキャッシュしておきたい

virtualenv使っていると、仮想環境ごとに同じようなパッケージ(Flask,SQLAlchemy,pyqueryなんか)を何度もダウンロードすることになり、帯域の無駄遣いだよなと思っていた。それから、東京出張の際に新幹線の中でコード書いてることが多いんだけど、大抵三島-新横浜間のどこかで一度はイラッとするのもどうにかならんかと。

perlでいうところのminicpanみたいなのないかなぁと調べてみたらpep381client使えっていうやりとりをみつけたので調べてみた。ここなんかよくまとまっている。

ぱっと見ると、pep381clientがいいかなと思うんだけどPYPIのほぼ完全なミラーを作る感じだし、これはノートに入れて持ち歩くにはでかすぎるだろ(30Gくらいのストレージが必要)っていう理由で保留。まぁ、職場にミラーつくるんだったらいいんだろうなぁと思い、使ってみようかなとふとissue見たどうなんだろうね?って思った(パッチ当てたのもあるけどね)。

collective.eggproxyはキャッシュプロキシサーバーであり、キャッシュされてないパッケージはネットワークにつないでダウンロードする必要がある。これはローカルにミラーを用意したいという自分の用途には微妙に合わない。サーバー動かしておくのもなんかめんどくさいなーと。

決定打が見つからないまま悶々としたあげく、結局新しいパッケージを頻繁に入れることは少ないからキャッシュ使うっていう方法でも結構いけそうだからcollective.eggproxyでいこうかなぁと、pip installしたら、ふとpipにキャッシュのオプションあるんじゃないの?と調べたらあったというオチ。

export PIP_DOWNLOAD_CACHE=$HOME/.pip_download_cache

こんだけだった。pipのドキュメントにもchangelogのとこにちょろっと書いてあるだけだったので見逃してた。

pip自体にキャッシュの仕組みがあるんだったらダウンロードキャッシュにPYPIの最新パッケージだけをミラーするスクリプトを書けばpipからはきちんとPYPIに繋がって適切に処理しているように見えるからこのやり方でいいんじゃないかと思ったので、改めてこれをacupと名付けよう。

  • acup(上のやつ、やっつけスクリプト)
  • bcup(minicpan相当のやつ)
  • ...
  • fcup(pep381client)

という妙な脳内認識が出来上がってしまった。

参考

ProductName Pythonプロフェッショナルプログラミング
ビープラウド
秀和システム / 2940円 ( 2012-03-26 )


Flask-SQLAlchemyとFlask-WTFの連携

最近Flask-WTFをよく使っている。

SQLAlchemyのデータをWTFに適用する時にはコンストラクタにobj引数で渡せばいいらしいんだが、逆にWTFのデータをSQLAlchemyはどうすんのかなぁと思いドキュメントを調べたら

form.populate_obj(user)

こんだけだった。

ProductName Pythonプロフェッショナルプログラミング
ビープラウド
秀和システム / 2940円 ( 2012-03-26 )


PLY(python, lex, yacc)

コンパイラ(中田 育男 (著))を読んだ。この本は分かりやすくて良いですね。中古で安いので余裕があれば買って積んでおけばいいと思う。

ProductName コンパイラ (新コンピュータサイエンス講座)
中田 育男
オーム社 / 3360円 ( 1995-06 )


pythonにはplyっていうピュアpythonのlex,yacc実装があるので、本書に載っているPL/0'をこれで書いてみようかなと思った。

それから、plyのサンプルフォルダに入っていたBASIC実装も面白かった。

rpy2+ggplot2

なぜグラフを描くのかと問われれば要約統計量を見やすい形で表現したいからであり、ヒトの目という不完全なデバイスを通してデータを認識するためにはグラフとよばれる表現手段を介したほうが効率が良いからである。

と考えるならば、分析のためにグラフを描くという行為は統計解析と切り離せないものであり、画像を描くためだけのライブラリを選択することはあまりよろしいことではないと思う。

そして望ましくは、適切に前処理されたマトリックスから望むグラフが作られるべきであり、グラフを描くためにデータを(ハッシュなんかに)加工するというのはあまりいいやり方とは言えないんじゃないかなぁと思っている。

これが、Rでグラフを描く大きな理由。データの前処理はRでやることはほとんどなくて、大体はperl,pythonを使うので、rpy2は重宝する。

import rpy2.robjects.lib.ggplot2 as ggplot2
from rpy2.robjects import r
from rpy2.robjects.packages import importr
base = importr('base')

datasets = importr('datasets')
mtcars = datasets.mtcars

pp = ggplot2.ggplot(mtcars) + \
     ggplot2.aes_string(x='wt', y='mpg', col='factor(cyl)') + \
     ggplot2.geom_point() + \
     ggplot2.geom_smooth(ggplot2.aes_string(group='cyl'), method='lm')

#pp.plot()
r['ggsave'](file="mypng.png", plot=pp)
#r['dev.off']()

これで、重量(x)と燃費(y)のプロットに対して、気筒毎に回帰直線と信頼区間を描くことができる。

ggplot2

分析手法をテンプレート化して自動的にレポートを出力したい時にはプログラミングできる手段があるといいのでrpy2は良いライブラリだと思う。

ProductName グラフィックスのためのRプログラミング
H. ウィッカム
丸善出版 / 4200円 ( 2012-02-29 )


PyJade(jade)の更新を検知してコンパイルするスクリプトを書いた

jQuery Mobile用のHTMLをPyJadeで書いているんだけど、いちいちファイルを修正するたびに手でコマンド打つのもめんどくさくなったので、ファイルの更新を検知して自動的にコンパイルするようにしてみた。

import jinja2
import time
import sys
import watchdog.events
import watchdog.observers
env = jinja2.Environment(extensions=['pyjade.ext.jinja.PyJadeExtension'])
env.loader = jinja2.FileSystemLoader('.')

class JadeHandler(watchdog.events.FileSystemEventHandler):
    def __init__(self, file):
        self.file = file
        html = file
        self.html = html.replace('.jade', '') + '.html'

    def on_modified(self, event):
        if event.src_path.endswith(self.file):
            template = env.get_template(self.file)
            with open(self.html, 'w') as f:
                f.write(template.render().encode('utf-8'))
            print "file modified (re-compiled)"

def main():
    file = sys.argv[1]
    event_handler = JadeHandler(file)
    observer = watchdog.observers.Observer()
    observer.schedule(event_handler, path=".")

    observer.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

if __name__ == "__main__":
    main()

これでなかなか快適にはなったんだけど、jadeで書いてみて思うのはdata-属性多すぎで見栄えが悪い。いっそdata-は@に置換してしまったらどうだろうかと。

  div(@theme='a', @role='footer', @position="fixed")
    div(@role="navbar")
      ul
        li: a(href="#", @icon="home") Home
        li: a(href="#", @icon="search") Search
        li: a(href="#info",  @icon="info") Info

これでいけそうなカンジがするので、Jinjaにテンプレートを読ませる前に@をdata-に置換する処理をかましておけば、jqm用のHTMLを快適に書けそうだ。

Python Testing: Beginner

pythonでのテストの本

doctestの使い方に始まり、doctestでのモックの使い方(mockerライブラリ)を学んだらdoctestでは不十分なユニットテストをUnittestライブラリを使ってテストする。

続いてNoseでdoctestとunittestを統合することを学びつつ、TDDの話しへ。

ほんでもって、twillを使ったwebアプリのテストのやり方をざっと紹介して、integration test, system testっていう流れ。

ProductName Python Testing: Beginner's Guide
Daniel Arbuckle
Packt Publishing / 3220円 ( 2010-01-31 )


エキスパートPythonプログラミングのテストの章の内容の充実度が物足りなく感じたら読めばいいレベルの本だと思う。知りたかったモックの使い方とtwillの使い方が丁寧に書いてあったので個人的には費用対効果が高くて満足の一冊だった。

ProductName エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス / 3780円 ( 2010-05-28 )


SpineのtodoサンプルをSpine.appで書いてみた

SpineのtodoサンプルをSpine.appを使って書いてみた。これはBrunch(Backbone.js)みたいに、モデル、ビュー、コントローラーをバラバラに開発してbuildすると一つのjsファイル(cssも)にアセンブルしてくれるツールですね。テンプレートはecoを使っています。

index.coffeeの役割がわかりづらかったので理解するのにちょっと時間がかかった。

hem model task

とかやるといっしょにテストのひな形が作られるのとlocalhost:9294/testにアクセスすればjasmineのテストが走るようになっているので使いやすそう。

そして以下の素晴らしい三部作を読んでSinon.jsの便利さに目覚めたので積極的に使っていこうと感じた。

Spine.Model.AjaxつまりRESTのAPIはFlaskを使おうと思っているんだが、Flask-Restlessを利用するのが手っ取り早くていいかなぁと。