2014-03-16
boto から Apache Libcloud に乗り換えるかも
今回は Apache Libcloud を使うことにした。いろんなクラウドサービスを単一のインタフェースで扱おう、というライブラリだ。S3 に特化した操作をするのは難しいかも知れないけれど、今回はシンプルな KVS サービスとして使うので、これでよい。
ただ、Fabric が Python 3 に対応していなかったので、結局 2.7 で書くことになっている。
2014-02-08
pyenv と tox で複数バージョンの Python に対応したライブラリ開発
以下を前提とする。
- OS X 10.9.1 (Mavericks)
- homebrew でパッケージを管理
- pyenv, pyenv-virtualenv で OS 上の Python 環境を管理
- PyPI に登録するライブラリは Python 2.7 と 3.3 に対応
まず始める
shimizukawa によるハンズオン資料と公式ドキュメントを併読しながら進める。基本的な作業はハンズオンでだいたい分かる。とはいえ、あなたが持っているライブラリは、必ずしもチュートリアルどおりではないだろうから、公式ドキュメントのガイドが必要になるだろう。ところで、ナウなヤングは egg じゃなくて wheel を使うらしい。
pyenv と pyenv-virtualenv をインストールする
pyenv は先週まで存在すら知らなかった。肉の日生まれの wozozo が使っているらしいので、試すことにした。まずインストールする。$ brew install pyenv pyenv-virtualenv $ cat >> ~/.zshrc export PATH=$HOME/.pyenv/bin:$PATH if which pyenv > /dev/null; then eval "$(pyenv init - zsh)"; fi
pyenv は複数の Python インタプリタをインストール/アンインストールするツールだ。pyenv-virtualenv は、pyenv と virtualenv を一緒に使いやすくする。実際に使ってみる。
$ pyenv install 2.7.6 $ pyenv virtualenv 2.7.6 foo-27
ひとつめのコマンドで Python 2.7.6 をインストールする。通常 ~/.pyenv/versions/2.7.6/bin/python にインストールされる。他にも pip なんかも入る。
ふたつめのコマンドで、Python 2.7.6 をベースに virtualenv を作る。通常 ~/.pyenv/versions/foo-27/bin/python に構築される。pip の他に activate なんかも入る。
同様に Python 3.3 をインストールして、仮想環境を作る。
$ pyenv install 3.3.3 $ pyenv virtualenv 3.3.3 foo-33
普段は Python 2.7 で作業しているので、おそらく foo-33 環境は不要だ。Python 3.3 については、tox が virtualenv を別途作ってくれる。けれど、カジュアルに pip とかしてしまうと、元の環境を壊してしまう。元の環境は綺麗にしておきたいので foo-27 と foo-33 環境を作る。
つづいて、プロジェクトで foo-27 と foo-33 う使うように設定する。
$ cd /path/to/foo $ pyenv local foo-27 foo-33 $ cat .python-version foo-27 foo-33
pyenv local X Y を実行すると、そのディレクトリで使う Python 環境を設定する。ここでは foo-27 と foo-33 だ。
順序には意味がある。python コマンドを発行した時には、foo-27 環境の Python が呼び出される。
そして foo-27 と foo-33 の両方を指定する必要がある。後述する tox は python2.7 や python3.3 というコマンドを実行する。このとき pyenv local で指定された環境を探しに行くからだ。pyenv local foo-27 だと python3.3 が見つからないというエラーがでる。
ref. Using tox with pyenv #92
tox を設定する
$ pip install tox pytest $ pip freeze | grep -v wsgiref > requirements.txt
wsgiref を含まないようにしている。理由は後述。
tox を使うための設定ファイルは以下のとおり。
$ cat > tox.ini [tox] envlist = py27,py33 [testenv] deps = -rrequirements.txt commands=py.test
envlist で py27 と py33 を指定しているため、このディレクトリ以下で、ビルドやテスト時に python2.7 と python3.3 を呼び出す。
tox.ini ファイルで、Python 2.7 インタプリタをフルパスを指定できる。だが、やらない。小さなプロジェクトだけれど、自分のところでしか動かないような設定にしたくないからだ。
tox を実行
$ tox
Python 2.7 用に virtualenv して、インストールして、ビルドして、テストを実行。Python 3.3 も同じ。
requirements.txt に wsgiref が含まれていると Python 3.3 で以下のようなエラーが出る。
Downloading/unpacking wsgiref==0.1.2 (from -r requirements.txt (line 7))
(...)
File "./ez_setup/__init__.py", line 170
print "Setuptools version",version,"or greater has been installed."
^
SyntaxError: invalid syntax
PyPI へ登録する
$ python2.7 setup.py sdist bdist_egg upload $ python3.3 setup.py bdist_egg upload
これで登録はできるが、こういう呼び出し方でいいのか自信がない。
まとめ
- はじめて setup.py を書いた。
- ヤク刈り、とは言わないけれど、地味に解決すべきことがことがあった。
- 明日は肉の日である。
2013-09-16
Google App Engine から backlog.jp の API にアクセスする
結局、このツールは使う必要がなくなったのですが、もしものときのために書き置きしておきます。
PyCon APAC 2013 に行ってきました
(9/16 23:30 ごろ: イアン・ルイス氏のプレゼンの感想を追記)
自分自身の発表
プレゼンテーション資料は、しゃべりがあることを前提に作るので、この内容だけでは伝わらないような気がします。You Tube に動画があがっていますが、話し始める前に動画が終了します。演台の後ならばれないと思ったのですが、パンツ履いていないのが見えたためカットされたのでしょう。
いつか、まとめて文章にするかも。
Programming AWS with Python
Release Faster
- 新メンバーが速く仕事できるように環境設定は自動化
- 依存性をできるだけ排除
- 本当に必要なものだけインストールする。Apache、HAProxy、RabitMQ、Zookeeper、Hadoop はアプリ開発者のローカルには、初日には要らないだろう。
- テストさえあれば、まあなんとかやってける
- ツールを整備することで、文化に影響を与える
- テスト書きたくなるようにする
- コピペでもいいから、とにかく書きたくさせる
- pytest, flake8, mock
- 素早いフィードバック
- 壊れたマージを防ぐ
- 「この変更ってまともかな?」
- 一部の人にだけ機能を追加/変更する
- 社内でリリースするのは、外部に対してリリースするのと同じ効果がある
- gargoyle Django 用のフィーチャゲート
その他
- ツールの提供を通して、文化を浸透させる
- インフラのビルドには時間がかかるので、最後に成功したビルドのスナップショットをとっておき、続きからやることでスピードアップする
Does Python dream of being unleashed?
- apt-get のみ管理者権限で実行できる。
- /_ah/start でシェルを呼び出せる(ので、ユーザ領域で好きなことできる)
- TCP, UDP 使える
- CGI, WSGI 経由のリクエストは、これまでどおりの書き方でOK
- URL による frontendとbackend の振り分けもこれまでどおりOK
Luigi, The Friendly Pipeline Plumber
俺達のイアン・ルイス氏によるツールの紹介。入力、出力(ファイル)、タスクを定義しておいて実行すると、依存関係を解決して順番にタスクを実行してくれるツールである luigi 。このセッションが、いちばん即効性がありました。というのは、日付からネットワーク上に保存されているデータを特定してダウンロードし、加工して保存し、それをさらに加工や修正をする、という仕事があるのです。あるのですが、これがたまーーーーにしかやってこないので、ほったらかしになってて、やる度にちょっとずつバグを修正している感じです。Luigi は失敗したファイルを生成しない=そのタスクは終わっていない、ってことになっているので、コードを修正したら、そこから再実行してくれるのです。
今は、Make を使っているんですが、Luigi に乗り換えるのがいいなと思い始めました。Make で呼び出しているひとつひとつのタスクは、Python で書いてあるので移植も簡単だと思います。
おわりに
2013-07-28
おまえらこのライブラリ使ってないの? m9 (2013-07)
pyfes 2013.07 に行ってきました。最近、ぜんぜんコードを書いていないので、この機会にエディタを開いてコードを書いたのは楽しかったです。数行のサンプルだけですけどね。
bpython ちょっと使ってみたら面白そうでした。あと suddendeath, xaxtsuxo, aodag.scaffold も便利だよという意見もあったので、気が向いたらカバーします(しねぇよ)。
2013-04-21
requests でテストした、その後ですよ
Web API をもつアプリケーションのテストを、Python と requests ライブラリを使って書いています。それはよいのですが、テストが通らなかったときですよ。酔ってますよ、もう、休日に仕事してぜんぜんはかどらなくて。それと、これとは別ですけど。
アプリケーションが Python で書かれていない場合、開発者が Python 環境を自由に使えない場合があります。テストのレポートを再現するためだけに、Python モジュールをインストールしてもらうのも気が引けます。
というわけで、requests を使ったアクセスを、 curl で再現するように hooks に追加することにしました。最初から curl 使えよとか、いろいろあると思いますが、すでにレイヤをまたいで requests 使ってたもので。
import curledrequests as requests
requests.debug = True
requests.post('http://example.com/', auth=("foo", "bar"), data={"hoge":"moge"})
のように書くと、
$ curl http://example.com/ -u 'foo:bar' -w '\n%{http_code}\n'
... ここに body が入る ...
200
と表示されます。これを Web API 開発者に渡して、再現してもらうことができます。gist においてあります。
2013-04-13
Go Conference 2013 Spring
Go Conference 2013 Spring 略して GoCon に行ってきました。
A Tour of Go
公式チュートリアルであるところの A Tour of Go というのがあります。1ページにコードと文書があって、Run ボタンをクリックすると実行結果が見られます。以前にこれを読んだことがあったのですが、よく分からないまま Run ボタンを押して、どんどんページを送っていって、結局何も分かっていない、という状態になりました。そこで今回は写経することにしました。
ダウンロードサイト から、OS X のバイナリをダウンロード、インストールして写経をしていたら、主催者の @ymotongpoo が Go Playground を教えてくれました。ブラウザで Go のコードを書いて、その場で実行できます。実行時間が短く、サードパーティのライブラリを必要としないコードであれば、これで簡単に実行できます。そういうわけで、Go Playground でチュートリアルをやっていきました。
ついでに書いたコードを簡単に共有するための URL も生成できます。
変数とポインタ
Go では変数とポインタが明確に区別されます。普段つかっている Python では名前にオブジェクトをバインドするモデルで、事実上すべてがポインタみたいなものです。ここはちょっと注意が必要でした。Go では x = y と書くと x に y のコピーを代入します。ポインタ(というか参照というか)は明示的に & を使います。
package main
import "fmt"
type Foo struct {val int}
func main() {
f := Foo{0}
g := f // 変数なので f のコピー
h := &f // ポインタ
f.val++
fmt.Println(f.val) // => 1
fmt.Println(g.val) // => 0
fmt.Println(h.val) // => 1
}
一方 Python では x = y と書くと、x に y のポインタ(というか参照というか)を代入します。コピーを代入するには明示的に copy モジュールを使うという考え方です。
from copy import copy
class Foo(object):
def __init__(self, val):
self.val = val
f = Foo(0)
g = copy(f) # copy
h = f # pointer
f.val += 1
print(f.val) # => 1
print(g.val) # => 0
print(h.val) # => 1
goroutine
並列処理/並行処理を Go は言語レベルでサポートしていて、go foo(..) と書くと、foo() 関数の呼び出しが非同期で実行できます。
トイレで「goroutine ってコルーチンなんだなぁ」と思っていたのですが、席に戻った瞬間 @Jxck_ さんの発表 で「goroutine はコルーチンではありません」と電撃発言です。どうやら OS のスレッド上で動作する並行ルーチンとして実装されているようです。
実装はおいといて、コルーチンを使ったトランポリンみたいなことが簡単に書けそうです。ちなみにトランポリンの理解は非常に曖昧です。
goroutine として呼び出す時でも、関数は通常の引数をとるので、ポインタを渡すことができます。ということは、複数の goroutine で同じオブジェクトを参照するという事態が起こるんだろうなぁと考えて、簡単なコードを書きました。
package main
import (
"fmt"
"time"
)
type Foo struct {
value int
}
func main() {
f := Foo{0} // 初期値は 0
fmt.Println(f.value)
go Incr(&f, 1, 1) // +1
go Incr(&f, 2, 200) // +2
time.Sleep(1 * time.Second)
fmt.Println(f.value) // => 2
}
func Incr(f *Foo, delta int, wait time.Duration) {
oldValue := f.value // 古い値を取得して
time.Sleep(wait * time.Millisecond) // しばらく休んで
f.value = oldValue + delta // 上書き
}
プロセスをまたいでいる時にどうなるかは不明です。こんなときにスレッドセーフじゃないようなコードを書くなという話なわけです。
並列処理といえば…
#gocon 会場のはじっこで @ikasamt @torufurukawa と熱いFPGAトークしてたらbuchoがハード設計バリバリな人だってことが判明した
— Kazunori Satoさん (@kazunori_279) 2013年4月13日
これは明らかに誤解なのですが、気分がよかったので、Twitter 上では訂正せずにいました。
以前の職場で開発環境/ツールのマーケティングをしていて、そのラインナップに FPGA 用にコードを書くツールが含まれていました。もともとグラフィカルに記述すると、PC 用に実行形式のバイナリを吐き出すというツールで、簡単に並列処理を記述できたのです。そこ発展して FPGA を搭載した I/O ボードの処理をデプロイできる、というツールです。
そんなわけでハードの設計は、まったくできません。ただFPGAを使うときにはプログラムの書き方が、大きく変わる、というのは感じています。Go も並列処理を念頭においた言語らしいので、あらためて並列処理を考える時期にきているようです。
2013-04-07
mock.patch() が置き換えする対象
mock.patch() の挙動について質問されて、即答できなかったので、簡単に調べ直してみました。
簡単なサンプル
まず以下のようなコードを想定します。
# foo.py その1
import random
def pickup(seq):
return random.choice(seq)
random.choice() 関数は呼び出すごとに挙動が変わり、テストしにくいので、mock の出番です。
# test.py
from unittest.mock import patch
import foo
with patch('random.choice') as m:
m.return_value = 0
assert foo.pickup([1, 2, 3]) == 0
import の仕方を変えるとモックに失敗する
ここで foo.py の書き方を以下のように変えます。
# foo.py その2
from random import choice
def pickup(seq):
return choice(seq)
pickup() 関数を外側から見た挙動は同じです。しかし、なんということでしょう、test.py を実行すると assert が失敗します。pdb や print を使って調べるとわかりますが、choice() 関数が Mock オブジェクトになっていません。
名前に対してパッチしている
with patch('random.choice') のコンテクストに入るとき、大雑把に以下のようなことが起こります。
import random random.choice = Mock()
※ 実際には Mock ではなく、そのサブクラス MagicMock ですが、この議論の本質ではないので、Mock で話を進めます。
Python のプロセスでは、モジュールはシングルトンとしてふるまうため、プロセス内で random.choice という名前は、Mock オブジェクトを参照することになります。foo.py その1の中では random.choice という名前を使っているため、Mock オブジェクトを参照します。
ところが foo.py その2 は random.choice という名前を参照しているわけではありません。from random import choice すると、大雑把に以下のようなことが起こります。
import random choice = random.choice del random
patch() 適用後、名前の参照先は
- グローバルな random.choice => Mock オブジェクト
- foo.py の choice => 元の choice 関数
のようになっています。このため、foo.py その2では choice がモックにならないのです。
名前空間 foo 以下の名前に対して patch を適用する
じゃあ、どうするのかというと、普段は patch('foo.patch') としています。上記の問題が発生するような状況だというのが分かっていれば、これで解決です。しかしながら、foo.py を、その1からその2に実装を変えたとき、テストが fail するけれど、どこがどうなっているのか見つけにくいな、と思いました。
その1のときに、test.py に patch('foo.random.choice') と書くとよいのでしょうか。こうすると foo.py その2に書き換えてテストすると、fail ではなくて patch() 実行時にエラーが出るので、名前がおかしいことに気付くような気がします。
2013-03-23
Python 旅館 2013.03
Python 温泉の簡易版です。なんと温泉もありません。
というイベントです。前回参加したのはいつだったかなと思い、検索してみたら、 2010年4月でした。3年前です。泊りのイベントは初めてで、知っている人も少なくて、どきどきしながら行ったことを覚えています。何をしたかは、まったく覚えていません。検索してみたら...
文字コード周りの挙動がアヤシイときにはbuchoに助けてもらいました。
そんな偉そうなことしたかなぁ。
@torufurukawa と酔っぱらいUstreamしてたらあんまり出来ませんでした。
これはよく覚えています。
さて、今回は簡単な課題を持って参加しました。とおるメモという駄文集がありまして、これを Kindle Direct Publishing で出版することです。もはや Python でも、プログラミングでもありません。と見せかけて、HTML から epub 形式に変換するまでを Python 3 で書きました。
もともと、簡易オレオレなマークアップで書いたテキストファイルから、スクリプトを使って静的なページ群を作っていました。m4 使ったり、XSLT 使ったりした記憶がありますが、もはや、どこにいったのか分かりません。@ae35 が reST 形式に変換したものを作ってくれていますが、今回は自分でやってみようと思いました。Python 3.3 を使ってみたかったんだい。
各文章の HTML から題名、日付、本文を取得するのに、 Beautiful Soup を使って HTML をパースしてから取り出しました。find_all() メソッドしか使っていません。いつの間にかバージョンが4になっていて、 beautifulsoup4 はそれ以前のものとはまったく別物になっているようです。
ちまちま作っていくときに、何度 print hoge と書いてエラーが出たことか。なかなか慣れませんね。Python 2 を使う時でも、 print(hoge) って書くように気をつけているつもりだったんですけどね。
その後は、まとめて torumemo.rst としてテキストファイルに吐き出すだけでした。タイトル、日付、1つ以上の段落で、ひとつの文書が構成されているので、ベタで出力しました。
それを Sphinx を使って、epub 形式にビルドしました。困ったことがひとつありました。epub テーマで変換すると、各ページのヘッダに「とおるメモ 1.0 document」、フッタに「(c) ふるかわとおる 2013, Created using Sphinx 1.1.3」 になり、それぞれハイパーリングがつきます。あと、index.rst に書いていなくても、空っぽのインデックスページができてしまう。ちと、これ要らないんすけど...。
これはもしやテーマを自作しなければいけないのか、と焦ってしまいました。テーマの作り方とか読んでしまいましたよ、はい。だらだらドキュメントを読んでいってるうちに、簡単な解決策がありました。
まずフッタとインデックスは、conf.py で指定できます。
html_use_index = False html_show_sphinx = Falseヘッダは layout.html という、すべてのテーマが継承しているファイル document という文字列が定義されているようでした。ようでしたというのは、このあたりで調べるのをやめました。というのは、 conf.py に自前の CSS を定義できることに気づいたからです。
def setup(app):
app.add_stylesheet('torumemo.css')
div.related {display: none}
という強引な方法で不可視にしています。データとしては含まれている、という、セマンティック的にひどい状態です。
とこんなことを書いているうちに、Kindle ストアで買えるようになるはずなのですが、Amazon からのメールに書かれていた「このURLで公開されているぜ」というページが 404 です。なんか時間がかかるみたいなので、もう今日はいいです。
2013-03-18
Python の辞書に dot-notation でアクセスする
もうこれは、古くて新しい問題で、いろんな人がいろいろと似たような実装をしていて、検索するといっぱい引っかかります。 自分のプロジェクトの中にも、そういうのが幾つかあります。
今日も久しぶりに検索したら、よさそうなのがありました。
https://github.com/makinacorpus/easydict
これが、x.foo.bar みたいに辞書に辞書が含まれててもうまくいく実装のなかで、シンプルできれいだなぁと思いました。
2013-01-06
Redis をさわりはじめました
Python で Redis を触ってみました。オートインクリメントやインデックス用のkeyを使うイディオムがある。
import json
import redis
r = redis.Redis()
def create_user(name, auth):
uid = r.incr('global:next_uid') # ユニークなIDを取得
return put_user(uid, name, auth)
def get_user(uid):
value = r.get('user:%s' % uid)
if value:
return json.loads(value)
def put_user(uid, name, auth):
user = {'uid': uid, 'name': name, 'auth': auth}
r.set('user:%s' % uid, json.dumps(user))
r.set('index:user:auth:%s' % auth, uid) # auth -> uid を引くため
return user
def get_user_by_auth(auth):
uid = r.get('index:user:auth:%s' % auth)
if not uid:
return
return get_user(uid)
a = create_user('wozozo', 'xxxxxxxx')
b = get_user(a['uid'])
assert a == b
put_user(a['uid'], b['name'], 'yyyyyyyy')
c = get_user_by_auth('yyyyyyyy')
2012-12-04
中間地点としての PySpa - PySpaアドベントカレンダー4日目
能書き
例えば今回のアドベントカレンダー参加メンバーだと
12/1 akisutesama
12/3 tokibito
12/4 torufurukawa
12/8 shimizukawa
あたりがPySpa経由で雇用されたビープラウド社員だったりします。
いまはむかし…
そういうとき、ググると、自分に都合のいい解釈ができるウェブサイトがすぐに見つかります。そのひとつがアクセンスのおまけ というページです。たぶん、ここに書かれていたような気がするのですが、当時「Python ハッカーを募集」って書いてあったのです。いまはもう書かれていません。ほら、世界は俺を求めている、とか思って安心だけしていました。いかにも不採用になりそうだったので、応募していません。
ハッカーに会おう
普通に自分の職歴で転職しようとすると、どうしても、計測制御分野でマーケティング担当とか、マネージャとかになってしまうんですよね。ちがうんだよ。プログラマになりたいんだよ。ってなわけで、きっとハッカーたちが居るであろう勉強会に顔を出そうと思いました。初対面で話すとかまったくできないので、話聞いて帰るだけでした。行ってたのは Python Code Reading という勉強会で、そこで Python 3.0 の紹介をやらせてもらえることになりました。
しかし…
で、PySpa は?
明日は、Python と人生とお酒の先輩である @turky です。
2012-11-04
What's New in Python 3.3
- generator の delegation
- u'...' 記法の復活
- faulthandler
- ipaddress
- lzma
- unittest.mock
- venv
- I/O 例外の階層構造
- import
- unicode 文字列のメモリ使用量
- 属性辞書のメモリ使用量
- decimal モジュールの C アクセラレータ
- email モジュールでの unicode 処理
- デフォルトでハッシュの randomization がオン
2012-10-28
pyspa 2012-10 3日目 〜リファクタリング〜
昨日はチューニングっぽいことをした後、眠りにつきました。今日はもう直接仕事をするのではなく、コードのエントロピィを下げる作業をしました。
たとえば、テストコードを見てもらったときに「これは、mock.patch を使いすぎだろwww」と指摘された箇所があります。
class API_UpdateCountsTest(TestBedTestCase):
@patch('poll.api.get')
@patch('backend.api.update_options_in_datastore')
@patch('backend.api.calculate_deltas')
@patch('google.appengine.api.taskqueue.Queue')
def test(self, Queue, calculate, update, get_poll):
...
self.assertEqual(Queue.call_count, 3)
self.assertEqual(Queue.call_args_list[0], ((backend_api.QUEUE_NAMES[0],), {}))
self.assertEqual(queue.lease_tasks.call_count, 2)
self.assertEqual(queue.lease_tasks.call_args, ((backend_api.LEASE_SECONDS, backend_api.MAX_TASKS), {}))
... (以下、大量の assert)
まあ、ひどい。テスト対象の関数の中で、いろんなことをしていて、かつ、それが丸見えになっているのが原因です。手順が多いのは仕方ないのだけれど、データの受け渡しがたくさん丸見えなのがよくない。
def _update_counts(queue_id):
queue_name = QUEUE_NAMES[queue_id]
queue = taskqueue.Queue(queue_name)
tasks = queue.lease_tasks(LEASE_SECONDS, MAX_TASKS)
vote_count = len(tasks)
option_count = 0
if tasks:
payloads = [json.loads(task.payload) for task in tasks]
deltas = calculate_deltas(payloads)
option_count = len(deltas)
update_options_in_datastore(deltas)
# handle done
for data in payloads:
... (まさかの20行くらい)...
# delete task
queue.delete_tasks(tasks)
ひどす。急ぎで性能を上げるために、コードを追加/削除/変更をしたときに、とにかくテストを書いて通すようにした残骸が残っています。こうやってエントロピィが増加するわけです。エントロピィの意味分かってませんが。
というわけで、以下のように変更。
def _update_counts(queue_id):
data = dequeue(queue_id)
if not tasks:
return
update_options(data)
update_dones(data)
それでも patch は 3つくらい必要ですが、assert がシンプルになります。
そんな作業をしていたら、夜が明け、海辺まで散歩し、温泉に浸かり、朝食を食べて、もうすぐ10時になります。解散して、帰宅して寝ます。
2012-10-27
pyspa 2012-10 2日目
というわけで、意地になっているチューニングを継続しました。データの流れは、
フロントエンド → タスクキュー → バックエンドインスタンス
になっていました。フロントエンドは自動スケールで台数がいくらでも多くなります。タスクキューとバックエンドインスタンスは 1:1 対応していて、 lease_tasks() している最中に、フロントエンドが add() するのに時間がかかるように見えます。
てなわけで、タスクキュー数とバックエンドインスタンス数 を N:1 にしてみたら、ちょっと応答がよくなってる感じもありつつ、しかし、あんまり優位な差ではないなあ、というところです。ひととおりの変更をしたので、月曜にちゃんと負荷かけてみます。
細々とした修正をしました。月曜に備えて、明日はゆっくりしようと思っています。リファクタリングとか。
pyspa 2012-10 1日目
- 無理だろ m9
- そういうことに使うなよ m9
- GAE のキューは datastore なんだから遅ぇよ m9
- いくらインフラ弱いっつっても、そろそろ潮時だろ m9
2012-09-16
PyCon JP 2012 に参加しました
Trying Continuous Delivery というテーマで、私の継続的デリバリーの試みの軌跡を話しました。英語のプレゼンテーション、全力で疲れて、汗だくです。
いくつか質問を受けたので、その応答を残します。
- [例に出てた] DateProperty を StringProperty に変更するみたいなことしたの?
いいえ。例が悪いですね。このままやったことはないですが、データ型を変更したことはあります。 - 受け入れテストではどこまでやってるのか?ステータスコード確認するだけ?
最初はステータスコードを見てただけ。今は、実際のユーザが行うような API 呼び出しをして、適切なレスポンスボディが返ってくることを確認しています。 - どんなツール使ってるの? Selenium とか?
作っているものが Web API なので Selenium は使わいません。requests モジュールで HTTP アクセスを書いています。テストランナは nose です。 - 負荷テストには何を使っている?
実はまだローカルのPCを使っているだけ。来週くらいから、 neustar の Load Testing サービスを使う予定です。以前に使ったこともあります。
次は「何かを作りました」みたいな話をしたいなぁとか思ってます。あと、このブログ英語にしてみようかな。今日はもう疲れきってて、いろいろ書いていますが、そんなことは忘れて、昨日までの自分になるかも知れません。
2012-07-29
Python Developers Festa 2012.07
Git
PyCharm
watchdog モジュール
$ bin/watchmedo shell-command --patterns="*.c;" --recursive --command='make all'
Traceback (most recent call last):
…
from argh import arg, alias, ArghParser
File "/Users/torufurukawa/works/pyfes201207/lib/python2.5/site-packages/argh/__init__.py", line 25
from .exceptions import *
SyntaxError: 'import *' not allowed with 'from .'
面倒くさいなぁ。もういいや。
ndb
from google.appengine.ext import ndb
class Foo(ndb.Model):
pass
class Bar(ndb.Model):
foo = ndb.KeyProperty(kind=Foo)
f = Foo()
f.put()
b = Bar(foo=f.key)
b.put()
k = b.key
# k から f を取得する。
b = Bar.get_by_id(k.id()) # ここでフェッチ
f = b.foo.get() # ここでもフェッチ
キーに親子関係があると
from google.appengine.ext import ndb
class Foo(ndb.Model):
pass
class Bar(ndb.Model):
pass
f = Foo()
f.put()
b = Bar(parent=f.key)
b.put()
k = b.key
# k から f を取得する
f = b.key.parent().get() # フェッチはここだけ
だべり
- とおるメモを、電子書籍にして売れば? → いいなぁ。このブログのアフィリエイトよりも、遥かに効率がよさそうだ。
- とおるメモアプリは、電話機能にアクセス権限とか要るんでしょ? → w
- アイコンはち◯こだろ → w
- うちの製品のマーケティングって、次は何したらいいの? → (・∀・)ニヤニヤ
2012-06-12
boto で S3 からダウンロード
$ python
>>> from boto.s3.connection import S3Connection
>>> conn = S3Connection('<aws access key>', '<aws secret key>')
>>> conn.get_bucket('wozozo')
<Bucket: wozozo>
>>> from boto.s3.key import Key
>>> k = Key(b)
>>> k.key = 'path/to/resource'
>>> fout = open('filename', 'w')
>>> k.get_file(fout)
簡単すぎる。
2012-06-01
Google App Engine の backends のデプロイ
$ appcfg.py update .が成功するのに、変更した挙動が backends サーバに反映されていないように見えました。そして、実際に反映されていません。
$ appcfg.py backbends . updateのように、backends コマンドに update アクションをつけて実行します。backends.yaml の設定を反映させるだけに使うと思っていたんだけど、コードのデプロイをする役目もあります。

