ラベル Python の投稿を表示しています。 すべての投稿を表示
ラベル Python の投稿を表示しています。 すべての投稿を表示

2014-03-16

boto から Apache Libcloud に乗り換えるかも

簡単な管理ツール+管理画面を作ることになり、以前からやってみたかった Python 3 での開発を試みた。AWS の S3 にファイルをアップロードするタスクがあるのだけれど、boto は Python 3 で使えない。

今回は Apache Libcloud を使うことにした。いろんなクラウドサービスを単一のインタフェースで扱おう、というライブラリだ。S3 に特化した操作をするのは難しいかも知れないけれど、今回はシンプルな KVS サービスとして使うので、これでよい。


ただ、Fabric が Python 3 に対応していなかったので、結局 2.7 で書くことになっている。


2014-02-08

pyenv と tox で複数バージョンの Python に対応したライブラリ開発

生まれてはじめて PyPI にコードを登録した。Python 2.7 と 3.3 で使えるようにするにあたり、ライブラリの実装とは直接関係ないところで、とまどった。現時点での手順を記録しておく。

以下を前提とする。

  • 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 にアクセスする

Google App Engine から backlog.jp の API にアクセスしようとしたら、何やらエラーが出ました。 libbacklog は xmlrpc を使っていて、xmlrpc は socket を使っていて、Google App Engine では無料枠では socket 使えないのですよね。ちょっとしたツールを作ることが目的だったので、まずはその場しのぎすることにしました。Making XML-RPC calls from a Google App Engine application をちょっと変えただけです。

結局、このツールは使う必要がなくなったのですが、もしものときのために書き置きしておきます。

PyCon APAC 2013 に行ってきました

2013年9月14日、15日にPyCON APAC 2013 に行ってきました。ほんとは、今日はスプリントをやっているそうですが、自宅でだらだらしています。だらだらしながら、Evernote のメモを、ここに書き写すことにしました。

(9/16 23:30 ごろ: イアン・ルイス氏のプレゼンの感想を追記)

自分自身の発表

"Test Failed, Then..." というタイトルで、以前に書いた「requests でテストした、その後ですよ」の内容を、ラッパのコードだけではなくて、なんでそもそもそういうことをしているのか、という視点で話しました。コンポーネントだけでなく、人も疎結合にしたいのですよ、私は。




プレゼンテーション資料は、しゃべりがあることを前提に作るので、この内容だけでは伝わらないような気がします。You Tube に動画があがっていますが、話し始める前に動画が終了します。演台の後ならばれないと思ったのですが、パンツ履いていないのが見えたためカットされたのでしょう。

いつか、まとめて文章にするかも。

Programming AWS with Python

VPN を設定するときに boto でやるとどんな感じなのかな、と思って聞いていたら、かなり簡単そうである、というのが分かりました。これは試してみます。


Release Faster

David Cramer 氏による、継続的リリースの考え方と必要条件の話でした。内容自体はおそらくは、継続的デリバリーや devops の文脈で語られる話を総括したものです。ですが、ちと思い入れがあります。

Cramer 氏は2012年の PyCON US で Practicing Continuous Deployment という演題で発表をしています。この発表の practical さに感動し、自分のプロジェクトに少しずつ取り込んでいき、その結果を、昨年の PyCon JP で Trying Continuous Delivery という演題で発表しました。DISQUS から Dropbox へ移った Cramer 氏の発表を聞きたいなぁと wktk で行きました。





ブートストラップ
  • 新メンバーが速く仕事できるように環境設定は自動化
  • 依存性をできるだけ排除
  • 本当に必要なものだけインストールする。Apache、HAProxy、RabitMQ、Zookeeper、Hadoop はアプリ開発者のローカルには、初日には要らないだろう。
テスト
  • テストさえあれば、まあなんとかやってける
  • ツールを整備することで、文化に影響を与える
    • テスト書きたくなるようにする
    • コピペでもいいから、とにかく書きたくさせる
  • pytest, flake8, mock
  • 素早いフィードバック
レビュー
  • 壊れたマージを防ぐ
  • 「この変更ってまともかな?」
フィーチャ・ゲート
  • 一部の人にだけ機能を追加/変更する
  • 社内でリリースするのは、外部に対してリリースするのと同じ効果がある
  • gargoyle  Django 用のフィーチャゲート

その他

  • ツールの提供を通して、文化を浸透させる
  • インフラのビルドには時間がかかるので、最後に成功したビルドのスナップショットをとっておき、続きからやることでスピードアップする


Does Python dream of being unleashed?

Google App Engine の実験的機能 VM backend の紹介。Google App Engine のバックエンドサーバに、Google Compute Engine のインスタンスが使えるようになるそうです。まだ trusted tester のみ。

  • apt-get のみ管理者権限で実行できる。
  • /_ah/start でシェルを呼び出せる(ので、ユーザ領域で好きなことできる)
  • TCP, UDP 使える
  • CGI, WSGI 経由のリクエストは、これまでどおりの書き方でOK
  • URL による frontendとbackend の振り分けもこれまでどおりOK
これは気になります。やりたいことがいっぱいある。

Luigi, The Friendly Pipeline Plumber

俺達のイアン・ルイス氏によるツールの紹介。入力、出力(ファイル)、タスクを定義しておいて実行すると、依存関係を解決して順番にタスクを実行してくれるツールである luigi

このセッションが、いちばん即効性がありました。というのは、日付からネットワーク上に保存されているデータを特定してダウンロードし、加工して保存し、それをさらに加工や修正をする、という仕事があるのです。あるのですが、これがたまーーーーにしかやってこないので、ほったらかしになってて、やる度にちょっとずつバグを修正している感じです。Luigi は失敗したファイルを生成しない=そのタスクは終わっていない、ってことになっているので、コードを修正したら、そこから再実行してくれるのです。

今は、Make を使っているんですが、Luigi に乗り換えるのがいいなと思い始めました。Make で呼び出しているひとつひとつのタスクは、Python で書いてあるので移植も簡単だと思います。

おわりに

主催者のみなさん、ありがとうございました。10人の宴会どころか、4人の飲み会の幹事もできない私には想像もできないような大変なことだったことでしょう。

来年の PyCon APAC は台湾らしいです。PyCon US や Euro Python と違って近いので、行きたいなぁと思い始めました。そういう風にふらっと出ていける仕事の回し方ができるようになるといいなぁ。

David Cramer 氏と話をしたかったのですが、何を話していいのか分からず、結局ひとことも話していません。話すことがないのに話す必要もないわけですが。

英語も、プレゼンももっと練習しないとなぁと痛感。去年も思ったような気がしつつ。せっかく時間を割いて聞いてくれているのだから、もうちょいうまくならねば失礼ってもんです。しかもプレゼンのときすごく体がガチガチだったらしく、終わってから腰が痛くて、帰り道で休憩しないと動き出せないくらい疲れました。何やってんだか。

質問タイムでも、その後のパーティでも、「そのアプローチで、一体に何を作っているのか?」と、何人にも聞かれました。需要がありそうなので、機会があればまとめてみようと思います。

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 のバイナリをダウンロード、インストールして写経をしていたら、主催者の @ymotongpooGo 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  // 上書き
}

プロセスをまたいでいる時にどうなるかは不明です。こんなときにスレッドセーフじゃないようなコードを書くなという話なわけです。

並列処理といえば…

これは明らかに誤解なのですが、気分がよかったので、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 旅館 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')

これ。で、torumemo.css の中で

div.related {display: none}

という強引な方法で不可視にしています。データとしては含まれている、という、セマンティック的にひどい状態です。

とこんなことを書いているうちに、Kindle ストアで買えるようになるはずなのですが、Amazon からのメールに書かれていた「このURLで公開されているぜ」というページが 404 です。なんか時間がかかるみたいなので、もう今日はいいです。

2013-03-18

Python の辞書に dot-notation でアクセスする

Python の辞書オブジェクトは x['foo']['bar'] みたいに書くのですね。それはよいのですが、 x.foo.bar みたいにアクセスしたくなる日もあるわけですよ。

もうこれは、古くて新しい問題で、いろんな人がいろいろと似たような実装をしていて、検索するといっぱい引っかかります。 自分のプロジェクトの中にも、そういうのが幾つかあります。

今日も久しぶりに検索したら、よさそうなのがありました。

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日目

このエントリは、PySpaアドベントカレンダー という「思いの丈を綴る(なんでも良い)」ブログイベント4日目の記事です。比較的歳をとってから、プログラマになろうとしたおっさんの吐露です。


能書き


PySpa とは「Python温泉」を語源とするはずで、開発合宿と見せかけて、なんとなくソフトウェア開発者やその周辺の人たちが、温泉旅館を借りきる集まりのひとつ、と理解しています。

アドベントカレンダー栄えある1日目は @akisutesama が書いているわけですが、いきなり事実誤認があるわけですよ。

例えば今回のアドベントカレンダー参加メンバーだと 
12/1 akisutesama
12/3 tokibito
12/4 torufurukawa
12/8 shimizukawa
あたりがPySpa経由で雇用されたビープラウド社員だったりします。

待てやこら。PySpa に初参加したときには、すでにビープラウドを退職した後ですよ。

とはいえ、私のソフトウェア開発者としてのキャリア形成で、PySpa は重要な要素なのです。さて、このアドベントカレンダー、なんでも良いって説明なので、俺のPythonとPySpaの話をしちゃうぞ♡  ハウツーとしての意味はありませんが、考古学的価値はあるかも知れません。


いまはむかし…


34歳ごろ、計測器メーカーでマーケティングの仕事をしつつ、2ちゃんねるの「35歳なんだけどプログラマになれる?」みたいなスレを見ていました。学生の頃からプログラマになりたかったのですが、面接に落ちたり、大学院を中退したり、面接に落ちたり、面接に落ちたり、女の子に振られたりしているうちに、負けることに慣れきってしまい、いろんなことを諦めていました。その割には、でもやっぱりプログラマやりたなぁ、などとぐずぐず2ちゃんねるですよ。

どういうきっかけがあったのか、まったく覚えていませんが「転職して職業としてプログラマになろう!」と決めたような気がします。どうせキャバ嬢にそそのかされたとか、そんなことがきっかけでしょう。で、面接とか受け始めるのですが、まあ、30代未経験が受かるわけがありません。

そういうとき、ググると、自分に都合のいい解釈ができるウェブサイトがすぐに見つかります。そのひとつがアクセンスのおまけ というページです。たぶん、ここに書かれていたような気がするのですが、当時「Python ハッカーを募集」って書いてあったのです。いまはもう書かれていません。ほら、世界は俺を求めている、とか思って安心だけしていました。いかにも不採用になりそうだったので、応募していません。

ハッカーに会おう


ひっぱってすみません、もうすぐ PySpa 出てきます。

普通に自分の職歴で転職しようとすると、どうしても、計測制御分野でマーケティング担当とか、マネージャとかになってしまうんですよね。ちがうんだよ。プログラマになりたいんだよ。ってなわけで、きっとハッカーたちが居るであろう勉強会に顔を出そうと思いました。初対面で話すとかまったくできないので、話聞いて帰るだけでした。行ってたのは Python Code Reading という勉強会で、そこで Python 3.0 の紹介をやらせてもらえることになりました。

講師をやると、その後の懇親会で、何もしなくても話しかけてもらえます。これすごく楽なんですよね。そのとき @atusi さんにも話しかけてもらえました。

atusi「いつも、どんなとこ行ってるんですか?」
私「いえ、ここ (Python Code Reading) しか来てないんです」
atusi「Python やってるんだったら、 BPStudy と Python 温泉っていうのに行ってみたらいいですよ」

おまたせしました、やっと登場、Python 温泉 a.k.a PySpa です。

atusi「Python 温泉は、集まってコード書いたりだらだらやってるだけなんですけどね。」

おお、ハッカーと温泉で仲良くなって、会社に入れてもらおう!という甘い策略を思いつきました。

私「ありがとうございます。早速申し込んでみます」


しかし…


当時の PySpa は、開催の告知をしたら、一瞬にして枠が埋まってしまって、ぜんぜん参加できなかったのです。くぅぅぅぅ。

けど真っ向勝負をしていたら、いつまでたってもプログラマになれないわけですから、PySpa に参加し、アクセンスの人と仲良くなり、うっかり間違って採用される、という目論見を持ち続けていました。

一方で、ソフトウェア開発の勉強会は、転職とは関係なく面白かったので、Python Code Reading や BP Study には、ときどき参加するようにしていました。あ、いや、ちょっとは期待があったかも。すみません、すみません。その後、ビープラウドで募集があったときに拾ってもらい、めでたくプログラマになれたわけです。


で、PySpa は?


その後も PySpa は開催されていたのですが、仕事や個人の都合、あるいは震災があったりとかで、参加していませんでした。やっと参加したのは、今年になってからです。

PySpa 経由で転職、というのは、まったく見当違いな目論見だったわけですが、 PySpa という道標があったから、その界隈にうろうろすることになり、プログラマになれました。

いまどき、35歳でプログラマになりたいような人がいるのか分かりませんし、PySpa を目指してもおそらくプログラマになれる気はしませんが。これを読んで PySpa 目指すよりも、手を動かしたほうがいい気がします。なので、この文章から教訓や方法論は得られないでしょう。最初に書いたように、おっさんの吐露です。

けど、まあ、何か偉そうに書いちゃおう。思いの丈を綴っていいらしいし。はっきり言って、プログラマになって後悔したことなんて、何回もあります。これからもあるでしょう。マーケやってたほうがモテたかも知れません。けど、プログラマになっていなかったら、もっと大きな後悔をしたはずです。死ぬまでずーーーーーっと「私は本当はプログラマになりたかったのに」と悔い続けたであろうことは、想像に難くありません。行動の諦めはいいくせに、気持ちだけ未練がましい私が、なんとか蟻地獄から抜け出せたのは、PySpa をゆるいながらも目指し続けていたからです。

ちなみに PySpa は、当初想像していたものとは、ぜんぜん違いました。


明日は、Python と人生とお酒の先輩である @turky です。

2012-11-04

What's New in Python 3.3


What's New in Python 3.3 のハイライトをコピペ。自称情弱な人のほうが、はるかに詳しそうだけれど、まずは調べるであろうことをリストアップ。

と思ったのですが、 What's New In Python 3.3をざっと眺める にすでにまとまっている気がします。これでいいんじゃないだろうか。

文法
  • generator の delegation
  • u'...' 記法の復活
新ライブラリ
  • faulthandler
  • ipaddress
  • lzma
  • unittest.mock
  • venv
built-in
  • 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日目

Python 温泉 2012-10 の1日目 は、Google App Engine の応答が悪くてふて寝で終わったわけですが、午前3時ごろに監視ツールから「正常に戻りました」メールが届いて起床しました。

というわけで、意地になっているチューニングを継続しました。データの流れは、

フロントエンド → タスクキュー → バックエンドインスタンス

になっていました。フロントエンドは自動スケールで台数がいくらでも多くなります。タスクキューとバックエンドインスタンスは 1:1 対応していて、 lease_tasks() している最中に、フロントエンドが add() するのに時間がかかるように見えます。

てなわけで、タスクキュー数とバックエンドインスタンス数 を N:1 にしてみたら、ちょっと応答がよくなってる感じもありつつ、しかし、あんまり優位な差ではないなあ、というところです。ひととおりの変更をしたので、月曜にちゃんと負荷かけてみます。

細々とした修正をしました。月曜に備えて、明日はゆっくりしようと思っています。リファクタリングとか。

pyspa 2012-10 1日目

本日から Python 温泉 2012-10 に参加しています。当初はだらだらと「いつかやる」リストに入っていることをやろうと考えていました。が、仕事で作っているプロダクトの性能要件が、昨日の夜に上がってしまったので、そのあたりをやろうとしています。まあ、なんとかなると思うので、やらなくてもいいんですが、ちょっと意地になっています。

Google App Engine で、アンケート集計をするようなサービスを作っています。アンケートへの回答が秒間5000 くらいです。一瞬だけであればさばけますが、これが長時間続くとキツイなぁというところです。memcache に置ければいいのですが、誰が回答したかをあとで取り出す必要があるので、揮発する memcache には置かない方針です。ちょっと意地になっています。

で、モヒカンたちに相談したところ...
  • 無理だろ m9
  • そういうことに使うなよ m9
  • GAE のキューは datastore なんだから遅ぇよ m9
  • いくらインフラ弱いっつっても、そろそろ潮時だろ m9
と...。だんだん、意地になってきました。

ボトルネックは backends のインスタンスが、 pull キューから lease_tasks()/delete_tasks() するとき、add() にすごく時間がかかることです。数秒かかります。ここをなんとか工夫したいなぁと思っていたのですが...



Python 不安定になっとるやんけ! というわけで、今日は寝る。

2012-09-16

PyCon JP 2012 に参加しました

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

Python Developers Festa (pyfes) 2012.07 に行きました。完全に個人的なメモです。

今回は発表することもなかったので、知り合いとダベることと、気になっていることをちょろっと試すことにしました。

普段、仕事をするときには、目の前の問題を、できるだけ確度の高い予想期間で片付けたいので、手持ちのカードだけで解決しようとしてしまいます。たかだか数時間ですが、ちょろっと試す機会に使おうと思ったわけです。自宅だと、ついエロ動画サイトとか徘徊しちゃうんでね。

Git

すみません、今更です。とりあえず、空のプロジェクトを作って、いじいじしました。Git と、いつも使っている Mercurial は、ブランチの考え方が違うので 1 on 1 でコマンドが対応しません。(1) add の意味の違いと、(2) push でどのリポジトリに、どのブランチを、っていうのを指定するのが、いちばん私が気をつけねばと感じたことです。

PyCharm

いまは Komodo Edit (Komodo IDE の機能限定無料版)を使っています。以前 @ikasamt に Pycharmいっすよと言われていました。試用版をインストール。

Backspace にCTRL-H 、Enter に CTRL-M を割り当てる。

App Engine のプロジェクトを作ったのだけれど、最近は buildout で環境を使っているので、Komodo も Pycharm の App Engine インテグレーションはじつは不要だったりする。単体テストやデバッガを使うときに効いてくるのかも知れない。

watchdog モジュール

ドキュメントもコードも、ファイルを変更したらビルド/テストするようにしています。が、これが美しくないやりかたをしています。OS X 上でのEmacs や Komodo でのファイル変更は、OMake で検知できません。

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

GvR が作った、Google App Engine のデータストアライブラリ(あるいは、BigTable のラッパ)。この文書をだらだら眺めていました。Pythonic で使いやすいし、隠蔽のバランスが直感的です。

ところで未だに、キーをヒエラルキーにすることが活かされる状況って、どういうときだよ、と思っていました。トランザクションで要るとは思うんだけどなぁ、などと、昼ごろにうとうとしながら考えていました。

あー、エンティティではなくて、キーから親子関係を引けるのがいいのか、と。
キーに親子関係がないときは…

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 からダウンロード

Amazon S3 に保存されたデータをダウンロードする、という作業を自動化することにしました。認証周りを自分でやるの面倒だったので boto を使うことにしました。

$ 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 のデプロイ

Google App Engine の backends のデプロイで困りました。
$ appcfg.py update .
が成功するのに、変更した挙動が backends サーバに反映されていないように見えました。そして、実際に反映されていません。
$ appcfg.py backbends . update
のように、backends コマンドに update アクションをつけて実行します。backends.yaml の設定を反映させるだけに使うと思っていたんだけど、コードのデプロイをする役目もあります。