MacPorts で virutalenv, virtualenvwrapper を導入
明日の Python 東海のハンズオンの準備のため,MacPorts で virtualenv, virutalwrapper を導入した.
http://groups.google.com/group/python-tokai/web/12
とりあえず port install
.
% sudo port install virutalenv-2.6 % sudo port install virutalenvwrapper-2.6 % ln -s /opt/local/Library/Frameworks/Python.framework/Versions/2.6/bin/virtualenv /opt/local/bin/virtualenv % ln -s /opt/local/Library/Frameworks/Python.framework/Versions/2.6/bin/virtualenvwrapper_bash /opt/local/bin/virtualenvwrapper_bash
virtualenvwrapper が virtualenv という名前のコマンドを要求するため,シンボリックリンクをつくる.
virutalenv-2.6 を入れるだけだと,/opt/local/bin/virutalenv-2.6 がつくられるだけみたい.
ついでに virtualenvwrapper_bash のシンボリックリンクもつくっておいた.
次に仮想環境を入れておくディレクトリをつくる.
% mkdir ~/.virtualenvs
最後に .zshrc なり .bashrc に以下のように追加.
## Python # virtualenv export WORKON_HOME=${HOME}/.virtualenvs if VIRTUALENVWRAPPER=`which virtualenvwrapper_bashrc` ; then source ${VIRTUALENVWRAPPER} fi
virtualenv の使い方は他のサイトにいっぱい載ってるのでそちらで.
名古屋 Hackathon ラムダ村 に参加してきました
もう一週間以上も経ちますが,名古屋 Hackathon に参加してきました.
主催者の @mzp さんをはじめ他の参加者のみなさんのおかげでとても楽しい時間をすごせたことを感謝いたします.
さて,自分は @akineko さん,@kumagi さん,@terurou さんとともに Python 組で参加してきました.
やろうとしたこと
Python 組では Python 3.1 を使って twitter クライアントの作成に挑戦しました.
Python 3.1 には,twitter ライブラリも OAuth ライブラリもないようなので,そこから作成しました.
はまったこと
Python 3.1 でやってく上ではまったことは,str
と bytes
の扱いです.
OAuth では HMAC や base64 を使うのですが,Python 2 と Python 3 とでは引数や返り値が違うという問題にはまりました.
Python 3 では str
が Unicode 文字列になってかなりハッピーなのですが,bytes
が関係するところではかえって面倒なことになってます.
できたこと
結局,OAuth でアクセス トークンを取得するところまでしかできませんでした.
成果物は https://bitbucket.org/kei10in/pytter/:title= にあるので興味のある方はご覧ください.
今もちょいちょい触ってます.
その後
名古屋 Hackathon 後 @terurou さんが https://bitbucket.org/terurou/oauth-python3:title= に update と home_timeline ができるものを公開してくださっています.
設定ファイルを bitbucket で管理する
いつかやろう,やろうと思ってた設定ファイルの Web 上での管理をやってみました.
いろいろな環境で同じ設定を使ったり,バックアップのやり忘れをさけたいといった場合は Web 上での管理がいいと思います.
管理には bitbucket を使うことにしました.
Python が好きなので.
そして,Mercurial デビューです (*'ω')b
手順
まず,bitbucket のアカウントがないと始まらないので取得します.
次に,[Repositories] から [Create new repository] を選択してリポジトリを作ります.
bitbucket にリポジトリを作ったら,ローカルに作業ディレクトリを作ります.
ここに設定ファイルをおくことになります.
% mkdir path/to/working/directory % cd path/to/working/directory
bitbucket のリポジトリから clone をして作業ディレクトリにローカル リポジトリを作ります.
% hg clone https://{USER_NAME}@bitbucket.org/{USER_NAME}/{REPOSITORY_NAME} % cd {REPOSITORY_NAME}
ローカル リポジトリのディレクトリに bitbucket で管理したい設定ファイルをコピーします.
% cp -rf ~/.emacs.d emacs.d % cp ~/.zshrc zshrc などなどをコピーする.
.elc
とか管理したくないファイルは .hgignore というファイルをリポジトリのルートにおいて,その中にいろいろ記述するといいです.
ちなみに今回自分が作ったのはこんな感じです.
syntax: glob *~ *.elc *.pyc .DS_Store Thumbs.db ac-comphist.dat auto-save-list
リポジトリのディレクトリにコピーしたファイルを add して,commit して push します.
% hg add * % hg commit -m % hg push https://{USER_NAME}@bitbucket.org/{USER_NAME}/{REPOSITORY_NAME}
最後に,ローカル リポジトリのファイルのシンボリック リンクをホームディレクトリに作ります.
% ln -s emacs.d ~/.emacs.d % ln -s zshrc ~/.zshrc
参考サイト
今回参考にしたところを紹介しておきます.
それと自分の設定ファイルも
可読性について
まずは、" プログラムの可読性に関する検討 - Life like a clown" をご覧ください。
で、サンプルのプログラムについて、自分としては [5] はなしかなと。
理由は、a ≠ b の評価値と関数 f
の引数が bool
であることに因果関係を見出せないから。
たとえば f
に渡す引数が true
, false
でなくて、1
, 2
とかだったら [5] は使えない。
ところで、ここで話題にしてるのは可読性について。
可読性をあげるには一貫性が必要なのだと思うのだけど、[5] は「f
の引数が bool
のときにのみ限定的に記述できる方法」でしかないと思うんです。
つまり、[5] だと一貫性を保つことが難しいため、なしと思います。
TDD Boot Camp 名古屋 1 日目でやったことを Python で復習する その 2
今日は TDD Boot Camp 名古屋 1 日目のペアプロ体験の後半でやった仕様変更の復習をするよ!
前回のものに仕様変更を加えていく.
仕様変更 1
一度にいっぱい set
できる set_multi
と一度にいっぱい get
できる get_multi
を実装する仕様変更.
正確仕様はちょっとうろ覚えだけど,だいたいこんな感じの仕様.
>>> store = FileStore() >>> store.set_multi({'foo': 'hoge', '': 'toto', 'bar': 'fuga'}) >>> store.dump() 'foo:hoge\nbar:'fuga\n' >>> store.get_multi(['bar', 'foo', None]) ['fuga', 'foo']
まず set_multi
のテストから
class TestFileStore(unittest.TestCase): def setUp(self): self.store = FileStore() self.store.set('foo', 'hoge') def test_set_multi(self): self.assertEqual(self.sotre.dump(), 'foo:hoge\nbar:fuga\nbuzz:piyo\n')
assert から書くのを忘れない(*'ω')b
次に,この assert が成功する用に set_multi
する.
class TestFileStore(unittest.TestCase): def setUp(self): self.store = FileStore() self.store.set('foo', 'hoge') def test_set_multi(self): self.store.set_multi({'bar': 'fuga', 'buzz':'piyo'}) self.assertEqual(self.store.dump(), 'foo:hoge\nbar:fuga\nbuzz:piyo\n')
ここまでできたら set_multi
を実装する.
set_multi
の実装
テストができたから set_multi
を実装する.
明白な実装なのでそのまま実装しちゃう.
実装はこんな感じ
class FileStore(object): def __init__(self): self._store = OrderedDict() def set(self, key, value): if key is None or key is '': return if key in self._store: self._store.pop(key) self._store[key] = value def set_multi(self, pairs): for k, v in pairs.items(): self.set(k, v) ...
テストもばっちり成功!!
get_multi
のテストと実装
set_multi
と同様に get_multi
も行います.
1. テスト作成
2. テスト失敗
3. 実装
4. テスト成功
は変わりません.
うんで,出来上がるテストと実装はこんな感じ.
class FileStore(object): ... def get(self, key): return self._store.get(key, None) def get_multi(self, keys): return [self.get(k) for k in keys if k in self._store] ... import unittest class TestFileStore(unittest.TestCase): def setUp(self): self.store = FileStore() self.store.set('foo', 'hoge') ... def test_get_multi(self): self.store.set_multi({'bar': 'fuga', 'buzz':'piyo'}) self.assertEqual( self.store.get_multi(['buzz', 'toto', '', 'bar']), ['piyo', 'fuga'])
set_multi
も get_multi
も既存のメソッドに影響を与えないので,わりと簡単に追加できた.
次の仕様変更はちょっと大変だよ!
仕様変更 2
メインディッシュきた!
次の仕様変更はこんな感じ.
>>> store = FileStore() >>> store.set('foo', '${now}') # 現在時刻が登録される >>> store.get('foo') 2010-07-16T21:57:48
テストがかけない.
残念ながらこのままじゃテストを書けない (>_<)
現在時刻とかをテストで扱うのはぶっちゃけ無理.
テストが書けない場合,テストができない原因になっているところだけ取り出して,それ以外のところでテストすればいいと思う.
なので開き直って現在時刻をテストするのはしない.
ここではテストするのは set が "${now}" を何かしら展開しよするかどうかをテストする.
そんなテストはこんな感じに書いた.
class FakeExpander(object): def expand(self, value): if value == '${now}': return '%s is expanded' % value return value class TestFileStore(unittest.TestCase): def setUp(self): self.store = FileStore() self.store.set('foo', 'hoge') def test_set(self): fake_expander = FakeExpander() self.store.set_expander(self.fake_expander) self.store.set('bar', '${now}') self.assertEqual(self.store.dump(), 'foo:hoge\nbar:${now} is expanded\n') ...
このテストがクリアされるように set
を修正し,こっそり増えた set_expander
を追加する.
class Expander(object): def expand(self, value): if value == '${now}': return datetime.now().strftime('%Y-%m-%d %H:%M:%S') return value class FileStore(object): def __init__(self): self._store = OrderedDict() self._expander = Expander() def set_expander(self, expander): self._expander = expander def expand(self, value): return self._expander.expand(value) def set(self, key, value): if key is None or key is '': return if key in self._store: self._store.pop(key) self._store[key] = self.expand(value) ...
さらにこっそり,expand
も追加されてるけど気にしない.
というのも set_expander
にしろ,expand
にしろそこに不安はないのでテストはない.
もう一点,Expander
っていうクラスを追加した.
ここがまさにテストの出来ない部分で,ここについてのテストは書かないことにした.
テストが出来ない部分はテストが必要ないくらい単純なものにしてやれば,開き直ってテストなしでいいと思う.
さらに Expander
クラスを導入したことで,"${now}" 以外に "${name}" とか増やしたくなった場合でも,FileStore にテストを追加する必要はなくて,Expander の方をごにょごにょしてやればよくなってます.
まとめ
TDD Boot Camp 名古屋の 1 日目でやったことの復習をした.
とりあえず,仕様変更 2 までやった.
仕様変更 3 (データの生存期間指定機能の追加) は力尽きたので省略しちゃう (ごめんなさい (>_<) )
仕様変更 3 も仕様変更 2 のときと同じように,テストができない部分は最小化してモックオブジェクトとか作ってテストすればいいと思う.
仕様変更 3 の場合だったら,CachedFileStore
クラスを導入してデータの保持とかは FileStore
に委譲.
CachedFileStore
はデータの生存,消滅の管理やらせて,消滅させるかどうかの判断だけを行うクラスを導入.
仕様変更 3 でテストできないのが時間関連の判断だけなので,そこをちっちゃいクラスに閉じ込めてしまえば他の部分は全部テストできるようになるっていう思惑.
責任の分割,超大切.
TDD では,とにかくテストから書く.
テストの中では assert から書く.
普通のプログラムはメソッドの上から書くけど,テスト プログラムはメソッドの下から書く.
テストもメンテナンスする.
最期に最終的なコードを.
#!/usr/bin/env python3 # -*- utf-8 -*- from datetime import datetime from collections import OrderedDict class Expander(object): def expand(self, value): if value == '${now}': return datetime.now().strftime('%Y-%m-%d %H:%M:%S') return value class FileStore(object): def __init__(self): self._store = OrderedDict() self._expander = Expander() def set_expander(self, expander): self._expander = expander def expand(self, value): return self._expander.expand(value) def set(self, key, value): if key is None or key is '': return if key in self._store: self._store.pop(key) self._store[key] = self.expand(value) def set_multi(self, pairs): for k, v in pairs.items(): self.set(k, v) def get(self, key): return self._store.get(key, None) def get_multi(self, keys): return [self.get(k) for k in keys if k in self._store] def dump(self): return '\n'.join( [':'.join([k, v]) for k, v in self._store.items()]) + '\n' import unittest class FakeExpander(object): def expand(self, value): if value == '${now}': return '%s is expanded' % value return value class TestFileStore(unittest.TestCase): def setUp(self): self.store = FileStore() self.store.set('foo', 'hoge') def test_set(self): fake_expander = FakeExpander() self.store.set_expander(fake_expander) self.store.set('bar', '${now}') self.assertEqual(self.store.dump(), 'foo:hoge\nbar:${now} is expanded\n') def test_get1(self): self.assertEqual(self.store.get('foo'), 'hoge') def test_get_miss_hit(self): self.assertEqual(self.store.get('toto'), None) def test_set_invalid_key1(self): self.store.set('bar', 'fuga') self.store.set(None, 'momo') self.assertEqual(self.store.dump(), 'foo:hoge\nbar:fuga\n') def test_set_invalid_key2(self): self.store.set('bar', 'fuga') self.store.set('', 'momo') self.assertEqual(self.store.dump(), 'foo:hoge\nbar:fuga\n') def test_set_overwrite(self): self.store.set('bar', 'fuga') self.store.set('foo', 'buzz') self.assertEqual(self.store.dump(), 'bar:fuga\nfoo:buzz\n') def test_dump1(self): self.assertEqual(self.store.dump(), 'foo:hoge\n') def test_dump3(self): self.store.set('bar', 'fuga') self.assertEqual(self.store.dump(), 'foo:hoge\nbar:fuga\n') def test_set_multi(self): self.store.set_multi({'bar': 'fuga', 'buzz':'piyo'}) self.assertEqual(self.store.dump(), 'foo:hoge\nbar:fuga\nbuzz:piyo\n') def test_get_multi(self): self.store.set_multi({'bar': 'fuga', 'buzz':'piyo'}) self.assertEqual( self.store.get_multi(['buzz', 'toto', '', 'bar']), ['piyo', 'fuga']) if __name__ == '__main__': unittest.main()
TDD Boot Camp 名古屋 1 日目でやったことを Python で復習する
タイトルとおりです.
id:t-wada さんからも許可得ることができたのでお題も Python で書き直してみます.
あ,ちなみに Python 3.1 です.
お題
↓のような仕様の FileStore クラスを作ります.
>>> store = FileStore() >>> sotre.set('foo', 'hoge') >>> store.get('foo') 'hoge' >>> store.dump() 'foo:hoge\n' >>> store.set('bar', 'fuga') >>> store.dump() 'foo:hoge\nbar:fuga\n' >>> store.get('toto') None >>> store.set(None, 'momo') >>> store.dump() 'foo:hoge\nbar:fuga\n' >>> store.set('foo', 'piyo'); >>> store.dump() 'bar:fuga\nfoo:piyo\n'
まずはテストから書く
get のテスト書く.
assert first
テストを書くときは assert から書く.
import unittest class TestFileStore(unittest.TestCase): def test_get1(self): self.assertEqual(store.get('foo'), 'hoge') if __name__ == '__main__': unittest.main()
こんな感じ.
でもこれを実行するとエラーになっちゃう.
% FileStore.py E ====================================================================== ERROR: test_get1 (__main__.TestFileStore) ---------------------------------------------------------------------- Traceback (most recent call last): File "FileStore.py", line 10, in test_get1 self.assertEqual(store.get('foo'), 'hoge') NameError: global name 'store' is not defined ---------------------------------------------------------------------- Ran 1 test in 0.000s FAILED (errors=1)
とりあえずエラーにならないところまで書く.
それと,そもそも set して get すると値が得られるという仕様のはずなので set も追加する.
#!/usr/bin/env python3 # -*- utf-8 -*- class FileStore(object): def set(self, key, value): pass def get(self, key): pass import unittest class TestFileStore(unittest.TestCase): def test_get1(self): store = FileStore() store.set('foo', 'hoge') self.assertEqual(store.get('foo'), 'hoge') if __name__ == '__main__': unittest.main()
これで Error じゃなくて Fail になったはず.
% FileStore.py F ====================================================================== FAIL: test_get1 (__main__.TestFileStore) ---------------------------------------------------------------------- Traceback (most recent call last): File "FileStore.py", line 21, in test_get1 self.assertEqual(store.get('foo'), 'hoge') AssertionError: None != 'hoge' ---------------------------------------------------------------------- Ran 1 test in 0.000s
よし,おk.
仮実装 (Fake it)
Fake itはテストのテスト
TDD Boot Camp 名古屋に登壇させていただきました - t-wadaの日記
ということでまずgetを仮実装する.
class FileStore(object): def set(self, key, value): pass def get(self, key): return 'hoge'
そしてテスト.
% FileStore.py . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
やったー.「OK」だってーーー!!!
三角測量 (Triangulation)
さぁ,仮実装をいじめるぞ!!
class TestFileStore(unittest.TestCase): def test_get1(self): store = FileStore() store.set('foo', 'hoge') self.assertEqual(store.get('foo'), 'hoge') def test_get2(self): store = FileStore() store.set('bar', 'fuga') self.assertEqual(store.get('bar'), 'fuga')
ぎゃ!仮実装じゃこんなテストを追加されたらひとたまりもない.
ということでset, getを実装します.
#!/usr/bin/env python3 # -*- utf-8 -*- from collections import OrderedDict class FileStore(object): def __init__(self): self._store = OrderedDict() def set(self, key, value): self._store[key] = value def get(self, key): return self._store[key] import unittest class TestFileStore(unittest.TestCase): def test_get1(self): store = FileStore() store.set('foo', 'hoge') self.assertEqual(store.get('foo'), 'hoge') def test_get2(self): store = FileStore() store.set('bar', 'fuga') self.assertEqual(store.get('bar'), 'fuga') if __name__ == '__main__': unittest.main()
% FileStore.py .. ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
(*'ω')b
ここでは,dump で順番の保持が必要そうなので collections.OrderedDict 使ったよ.
テスト->実装->リファクタリング
この調子で dump とか get,set の細かい仕様を実装していく
まずdump
class FileStore(object): ... def dump(self): return '\n'.join( [':'.join([k, v]) for k, v in self._store.items()]) + '\n' import unittest class TestFileStore(unittest.TestCase): def test_get1(self): store = FileStore() store.set('foo', 'hoge') self.assertEqual(store.get('foo'), 'hoge') def test_get2(self): store = FileStore() store.set('bar', 'fuga') self.assertEqual(store.get('bar'), 'fuga') def test_dump1(self): store = FileStore() store.set('foo', 'hoge') self.assertEqual(store.dump(), 'foo:hoge\n') def test_dump2(self): store = FileStore() store.set('bar', 'fuga') self.assertEqual(store.dump(), 'bar:fuga\n') def test_dump3(self): store = FileStore() store.set('foo', 'hoge') store.set('bar', 'fuga') self.assertEqual(store.dump(), 'foo:hoge\nbar:fuga\n')
- テスト test_dump1 を作成
- テスト実行: 失敗
- FileStore.dump を仮実装
- テスト実行: 成功
- テスト test_dump2 を作成
- テスト実行: 失敗
- FileStore.dump を実装
- テスト実行: 成功
- テスト test_dump3 を作成
- テスト実行: 成功
っていう流れ.
テストのリファクタリング
いい加減 store = FileStore()
とか store.set('foo', 'hoge')
って書くのがだるいのでテストをリファクタリングする.ついでに test_get2, test_dump2
ももういらないので削除する.
で,こうなる
class TestFileStore(unittest.TestCase): def setUp(self): self.store = FileStore() self.store.set('foo', 'hoge') def test_get1(self): self.assertEqual(self.store.get('foo'), 'hoge') def test_dump1(self): self.assertEqual(self.store.dump(), 'foo:hoge\n') def test_dump3(self): self.store.set('bar', 'fuga') self.assertEqual(self.store.dump(), 'foo:hoge\nbar:fuga\n')
細かい仕様を実装
とりあえずは最終形.
#!/usr/bin/env python3 # -*- utf-8 -*- from collections import OrderedDict class FileStore(object): def __init__(self): self._store = OrderedDict() def set(self, key, value): if key is None or key is '': return if key in self._store: self._store.pop(key) self._store[key] = value def get(self, key): return self._store.get(key, None) def dump(self): return '\n'.join( [':'.join([k, v]) for k, v in self._store.items()]) + '\n' import unittest class TestFileStore(unittest.TestCase): def setUp(self): self.store = FileStore() self.store.set('foo', 'hoge') def test_get1(self): self.assertEqual(self.store.get('foo'), 'hoge') def test_get_miss_hit(self): self.assertEqual(self.store.get('toto'), None) def test_set_invalid_key1(self): self.store.set('bar', 'fuga') self.store.set(None, 'momo') self.assertEqual(self.store.dump(), 'foo:hoge\nbar:fuga\n') def test_set_invalid_key2(self): self.store.set('bar', 'fuga') self.store.set('', 'momo') self.assertEqual(self.store.dump(), 'foo:hoge\nbar:fuga\n') def test_set_overwrite(self): self.store.set('bar', 'fuga') self.store.set('foo', 'buzz') self.assertEqual(self.store.dump(), 'bar:fuga\nfoo:buzz\n') def test_dump1(self): self.assertEqual(self.store.dump(), 'foo:hoge\n') def test_dump3(self): self.store.set('bar', 'fuga') self.assertEqual(self.store.dump(), 'foo:hoge\nbar:fuga\n') if __name__ == '__main__': unittest.main()
やっぱり,ここでも
- テスト作成
- テスト実行: 失敗
- 実装
- テスト実行: 成功
の流れには逆らわずにひたすらまわす.
TDD Boot Camp 名古屋に参加してきた
2010年7月10日から11日まで2日間に渡ってTDD Boot Camp 名古屋に参加してきた.
id:t-wada さん,id:bleis-tiftさん,他すべてのみなさんありがとございました.
とても楽しく参加することができました.
どんな内容だったかは,TDD Boot Camp名古屋 - 名古屋アジャイル勉強会 - Yahoo!ブログ のトラックバックにみなさんがすでに書いておられるので,ここでは TDDBC で行ったペアプログラミングとTDD道場の感想を書きます.
ちなみに,ペアプログラミングと道場は言語ごとに分かれてやってます.
自分は C++ での参加です.
1 日目 ペアプログラミング
内容: TDD 体験ペアプログラミング.
ペアプログラミング初体験.
人のプログラミングの流れを見るのも初めてだし,人に見せるのも初めて.
ペアプロを仕事でやるにはまだちょっと不安があるけど,一生で一度は絶対にやるべきと思う.
単純に参考になることいっぱいあるし,相手に教えることもできて楽しい.
2 日目 TDD道場
内容: レガシコード改善 TDD 道場
道場初体験.
目標大事
最初に目標をどうするかをしっかり決めたグループもあるくらい.
特に比較的多めの人数で行うため,目標が定まってないとメンバーがそれぞれ違う方向を向いてしまうこともある.
そもそもレガシーコードに立ち向かうときには仕様変更とかバグフィックスのような目標があるはず.
そういった目標がない限りはレガシーコードを改善する必要はないはず.
そういう意味でも目標設定が大事だなと痛感した.
宗教戦争
ペアプログラミングとは宗教戦争の始まりのこと(うそ)
まずは自分の宗教および宗派から
OS: Mac
エディタ: Emacs
キーボード: 英語配列
まずは OS
さて,会場の半分くらいはMac.
世間とはかけ離れた環境ですね!!ヽ(´ー`)ノ
あとたぶんWindowsノートの格好をしたLinuxとかBSDとかSolarisもいるはず!!
なのできっと Mac > Win > Linux.
自分が参加するC++では???
Win: 4人, Linux: 1人,Mac: 1人 (自分)
惨敗ですがな、、、
だいたい予想はしてたけどね.
戦い
みんな大人なのでみにくいあらそいはありませんでした.
ざんねん☆