nose でテスト事前、事後処理する

ナウでヤングな Pythonista はテストは nose を使うみたいなのです。
良くわかりませんがそういうものです。

事前処理と事後処理

DB の初期化とかテストの事前処理や事後処理はないと困っちゃいますが、
nose ではこんな風に書きます。

def setup_func():
    # ...

def teardown_func():
    # ...

@with_setup(setup_func, teardown_func)
def test():
    # ...

デコレータ以外にも色々やり方はあります。
http://somethingaboutorange.com/mrl/projects/nose/0.9.3/

pit と併用して使う

外部の API と連携してるテストなどをやりたい時に
いつも使ってる pit の環境汚したく無いなぁなんて時は
プロファイルを切り変えられるので
それを呼ぶと幸せになります

from nose.tools import with_setup
from hatenad import HatenaDiary
from pit import Pit

__profile__ =''

def setup_func():
    global __profile__
    __profile__ = Pit.config()['profile']
    Pit.switch('test')

def teardown_func():
    Pit.switch(__profile__)  

@with_setup(setup_func, teardown_func)
def test_Post():
    test_conf = Pit.get('hatena.ne.jp',{'require' : {'userid':'Your hatena.ne.jp userid','password':'Your hatena.ne.jp password'}})    
    # ...

これだけでテストの時だけプロファイルを切りかえられますので
少しだけテストが楽になるかなぁと思います。


注意点としては pit の設定ファイルは共有してるので
テストの途中で違うプログラムが pit を使ってしまうとテスト用のプロファイル見ちゃうので
そこだけ気をつけてくだしあ><

Python ではてなダイアリーに投稿

Python の atompub で使いやすいライブラリがみつからなかったので
自前でゴリゴリ

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# http://d.hatena.ne.jp/keyword/%a4%cf%a4%c6%a4%ca%a5%c0%a5%a4%a5%a2%a5%ea%a1%bcAtomPub

import base64
import random
import sha
import urllib2
import urllib
from datetime import date,datetime

import sys
# 2.5 までは 201 がエラー扱いになっちゃうので
if sys.version_info[0] < 3 and sys.version_info[1] < 6:
    def __http_response(self, request, response):
        code, msg, hdrs = response.code, response.msg, response.info()
        if not (200 <= code < 300):
            response = self.parent.error(
                'http', request, response, code, msg, hdrs)
            
            return response

    urllib2.HTTPErrorProcessor.http_response = __http_response

class WSSEHeader(urllib2.BaseHandler) :
    
    def __init__(self, userid, passwd):
        self.userid = userid
        self.passwd = passwd

    def get_nonce(self):
        private = str(random.random())
        now = datetime.now()
        timestamp = now.strftime('%Y-%m-%dT%H:%M:%SZ')
        return '%s %s' % (timestamp, sha.new('%s:%s' % (timestamp, private)).hexdigest())

    def get_wsse(self):
        nonce = self.get_nonce()
        base64_encoded_nonce = base64.encodestring(nonce).replace('\n', '')
        now = datetime.now()
        post_creation_time = now.strftime('%Y-%m-%dT%H:%M:%SZ')
        password_digest = base64.encodestring(sha.new(nonce + post_creation_time + self.passwd).digest()).replace('\n', '')
        return 'UsernameToken Username="%s", PasswordDigest="%s", Created="%s", Nonce="%s"' \
            % (self.userid, password_digest, post_creation_time, base64_encoded_nonce)

    def http_request(self,req):
        req.add_header('X-WSSE', self.get_wsse())
        req.add_header('User-agent', 'yoshiori\'s test(yoshiori@gmail.com)')
        return req

class HatenaDiary:
    
    def __init__(self, userid, passwd):
        self.userid = userid
        self.passwd = passwd
        self.url = 'http://d.hatena.ne.jp/%s/atom/' % userid
        
    def postEntry(self, title, text, isDraft=False):
        entry = '''<?xml version="1.0" encoding="utf-8"?>
        <entry xmlns="http://purl.org/atom/ns#">
        <title><![CDATA[%s]]></title>
        <content type="text/plain"><![CDATA[%s]]></content>
        </entry>
        ''' % (title,text)
        print entry
        url = self.url + 'draft' if isDraft else self.url + 'blog'
        return self._open(url,entry)

    def getEntrys(self):
        return self._open(self.url + 'blog')

    def get(self):
        return self._open(self.url)
    
    def _open(self,url,param=None):
        opener = urllib2.build_opener( WSSEHeader(self.userid, self.passwd))
        return opener.open(url,param) if param else opener.open(url)
        
if __name__ == '__main__':
    from pit import Pit
    hatena_conf = Pit.get('hatena.ne.jp',{'require' : {'userid':'Your hatena.ne.jp userid','password':'Your hatena.ne.jp password'}})
    diary = HatenaDiary(hatena_conf['userid'],hatena_conf['password'])
    print diary.postEntry('日本語','**日本語\n-ほげ\n-ふぉお\n>|python|\nimport sys\n||<',True)

とりあえず初 Mercurial
http://bitbucket.org/yoshiori/hatenad/

pip って何(・ω・ )

http://d.hatena.ne.jp/mopemope/20090220/p4
で、pip って何!?

pip

http://pip.openplans.org/

pip is a replacement for easy_install. It uses mostly the same techniques for finding packages, so packages that were made easy_installable should be pip-installable as well.

easy_install っていうか package の管理をもっと便利にするぜっていう認識でOK?

とりあえずインストール

$ sudo easy_install pip

自分の環境にインストールしてる package の一覧取得

$ pip freeze

で出来る


http://d.hatena.ne.jp/tokuhirom/20090219/1235034086


こういう事を Python でやりたいので
最近、環境をまっさらにした Ubuntu
とりあえず雛形的な物を作る

$ pip freeze
Beaker==0.9.5
Brlapi==0.5.2
Conch==8.1.0
GnuPGInterface==0.3.2
Numeric==24.2
PAM==0.4.2
PIL==1.1.6
PyYAML==3.06
SQLAlchemy==0.4.6
Twisted==8.1.0
Twisted-Core==8.1.0
Twisted-Lore==8.1.0
Twisted-Mail==8.1.0
Twisted-Names==8.1.0
Twisted-News==8.1.0
Twisted-Runner==8.0.0
Twisted-Web==8.1.0
Twisted-Words==8.1.0
apturl==0.2.7ubuntu1
ccsm==0.7.8
command-not-found==0.1
cups==1.0
docutils==0.4
gdata.py==1.0.9
gnome-app-install==0.5.12-0ubuntu1
human-theme==0.5
ipython==0.8.4
jockey==0.5beta3
mercurial==1.0.1
nvidia-common==0.0.0
onboard==0.0.0
pexpect==2.1
pit==0.2
pyOpenSSL==0.7
pycrypto==2.0.1
pyserial==2.3
python-apt==0.6.17
python-debian==0.1.11
python-irclib==0.4.6
python-launchpad-bugs==0.3.1
python-twitter==0.5
pyusb==0.4.1
pyxdg==0.15
rdflib==2.4.0
roman==0.2-
screen-resolution-extra==0.0.0
simple-ccsm==0.7.8
simplejson==2.0.6
system-service==0.1.6
ubuntu-gdm-themes==0.30
ubuntu-wallpapers==0.28.1
ufw==0.23.3
unattended-upgrades==0.1
usb-creator==0.1.8
virtkey==0.01
wsgiref==0.1.2
xkit==0.0.0
zope.interface==3.3.1

けっこう色々な物入ってるなぁ

pip freeze > mypackages.txt

して、自分が使ってそうなのだけ残す

$ cat mypackages.txt
PyYAML==3.06
SQLAlchemy==0.4.6
ipython==0.8.4
pit==0.2
python-irclib==0.4.6
python-twitter==0.5
simplejson==2.0.6

自分用の bundle 作成

$ pip bundle -r mypackages.txt yoshioris.pybundle

完成!!

これで完成した「yoshioris.pybundle」を持っていけばスグに環境が作れる!!

TODO mypackages.txt yoshioris.pybundle を バージョン管理

これ自体をどんな名前で管理しよう??
とか思いつつ mercurial 使ってみようかなぁ
誰か良い名前教えてくだしあ><
(Task::BeLike::Tokuhirom みたいなの)

pit 用の yasnippet

なんか毎回書くのメンドイので snippet 書いた。
便利♪

# -*- coding: utf-8 -*-
# name: pit
# contributor: Yoshiori SHOJI <yoshiori@gmail.com>
# --
${1:name} = Pit.get('${2:domain}',{'require' : {'${3:require1}':'Your $2 $3}','${4:require2}':'Your $2 $4'}})

2 つの Python-mode

なんか色々な歴史的経緯があって emacs には 2 つの Python-mode があるっぽい


今まで全然しらなかったので標準で入ってるのを使ってたんだけど、
タマにインデントまわりでおかしな挙動をする事があった。


と、いうかインデントまわりでおかしな挙動をされると Python は致命的なので
調べてたら上記の 2 つの Pyhton-mode を知ったんですけどねw

というわけで、気分を変えて使ってみたんだけど……
何が違うのか判らないwwww


C-c C-c で実行時にコンソールが開くか開かないかくらいの違いしかわからなかった><
とりあえず使ってみます><
http://w.koshigoe.jp/study/?%5Bpython%5Dpython-mode.el%A4%CE%A5%AD%A1%BC%A5%D0%A5%A4%A5%F3%A5%C9

ipython-el 入れてみた!!

http://ipython.scipy.org/dist/ipython.el

(require 'ipython)
(setq py-python-command-args '("-cl"))

少し幸せになった!!

ファイルの更新があったら Firefox で今開いてるページをリロード

なんか色々方法はあるのですが、準備がめんどくさかったので
MozRepl 使って自分でさくっと

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys,os,telnetlib,time

class MozRepl:

    def __init__(self, host='localhost',port=4242):
        self.tn = telnetlib.Telnet(host,port)
        self.tn.read_until("repl>")

    def reload(self):
        self.tn.write('content.location.reload(true)')

    def close(self):
        self.tn.write('repl.quit()')
        self.tn.close()


def getMtime(filename):
    return os.stat(filename).st_mtime


if __name__ == '__main__':
    filename = sys.argv[1]
    print 'start - ' + filename
    mtime = getMtime(filename)
    mozrepl = MozRepl()
    while True:
        new_mtime = getMtime(filename);
        if mtime != new_mtime:
            mozrepl.reload()
            mtime = new_mtime
            print 'reload'
            time.sleep(1)
    mozrepl.close()
    print 'end'

なーんも考えずに動けばいいやで作ったので突っ込み大歓迎です><