Pythonの式をダイナミックに関数にコンパイルする


# 関数定義の雛形。Trueのexpressionを後で置き換える。
# ここで、string代入してもいいが、ASTのノードを置き換えた方が安全そうな気がする。
>>> defun="""
def foo(num):
return True
"""
... ... ... >>>
# ASTにパースする
>>> x=ast.parse(defun, mode='single')
>>> x
<_ast.Interactive object at 0x7f963f9c1950>

>>> expression='num==42'

# ASTでTrueにあたるノードを上の式に置き換える。
# body,valueのコンビネーションは試行錯誤でみつける。
# ASTをダンプする機能があったら簡単に見付かるだろう。
>>> x.body[0].body[0].value=ast.parse(expression, mode='single').body[0].value
>>> c=compile(x, '', 'single')
# コンパイルするとコードになる。Pythonらしく素直だ。
>>> c
<code object <module> at 0x7f963f96b3f0, file "<string>", line 2>
>>> locals={}
# 関数定義をサンドボックス内で実行。
>>> eval(c, {}, locals)
>>> locals
{'foo': }
# 関数を現スコープに「インポート」
>>> foo=locals['foo']
>>> foo(42)
True
>>> foo(43)
False
>>>

これで、ランタイムに文字列として得た式を効率よく何度も実行できることになる。例えばコマンドでデータに対する処理をアドホックな言語をでなくpythonの式を引数として与えることができるようになる。
cat life.jsons | datastream filter 'meaning==42'
という具合に。

追加: 関数としてまとめてみた。
python式をコンパイルして関数にする関数(なんかややこしいな…)

def function(body):
"""python式を関数にコンパイルする"""

# 関数ソース
defun_src="def surrogate(*args, **kw): return True\n"

# astにパース
defun_ast=ast.parse(defun_src, mode='single')

# コード差し替え
defun_ast.body[0].body=body_ast.body

# コンパイル
defun_code=compile(defun_ast, '', 'single')
# 抽出
locals={}
eval(defun_code, {}, locals)
return locals['surrogate']

これだとexpressionを関数にするのに一々returnを入れなければいけない。returnをオプショナルにしたい…

pythonでマルチキャストを受信する


#!/usr/bin/env python
import socket
import sys

# データを受信するインターフェースをIPアドレスで指定。
# 全てのインターフェースを指定するときはinaddr_any 0.0.0.0にする。
multicast_if_addr='10.0.1.2'

# マルチキャストアドレス
multicast_group='224.3.29.71'
multicast_port=9999

my_addr='0.0.0.0'
server_address=(my_addr, multicast_port)

# ここまではUDP受信ソケットと同じ
sock=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(server_address)

# マルチキャストを受けるにはsetsockopt(..,IP_ADD_MEMBERSHIP,..)で
# マルチキャストグループに登録する。
# IP_ADD_MEMBERSHIPの値はin.hに定義されているstruct ip_mreq。
# struct ip_mreq /* found in in.h */
# {
# struct in_addr imr_multiaddr; /* IP multicast address of group */
# struct in_addr imr_interface; /* local IP address of interface */
# };
# 一般的にはstruct.pack()でバイトパターンをつくるが、ここでは安易にraw stringを結合。
mreq=socket.inet_aton(multicast_group)+socket.inet_aton(multicast_if_addr)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

while True:

data, address=sock.recvfrom(1024)
print data

参考

doug hellmannのこの解説にもとづく http://www.doughellmann.com/PyMOTW/socket/multicast.html
この例ではクライアントにパケットを送り返す仕様になっている。

gnumakeで「このファイル」__FILE__を実装する

多くのプログラミング言語が「このファイル」を変数__FILE__で教えてくれる。__FILE__を参照したコードを含むソースファイルのパスが返ってくる。この便利な機能はgnumakeにない。しかし、MAKEFILE_LISTという変数から現ファイルを推測できる。

config.mkに

THIS_FILE=$(filter %config.mk,$(MAKEFILE_LIST))                                                                    

とするとTHIS_FILEがconfig.mkのパスを値として持つことになる。ただし、「config.mk」が唯一この名をもつMakefileであることを前提とする。

このハックを発見していい気になっていたら、JGCがすでに丁寧に解説しているのを発見:
http://blog.jgc.org/2007/01/what-makefile-am-i-in.html

どうやらMAKEFILE_LISTの最後の値が現makefileになるらしい。つまり上記のfilterが無用ってことになる。さらにコメントにgmake 3.81以降だとlastwordが使える、と改善案が。つまりこれが、__FILE__として使えることになる:

$(lastword $(MAKEFILE_LIST))                                                                                       


プロジェクトのルートディレクトリのconfig.mkやらinclude.mkにこれを入れておけばどこからincludeしても常にプロジェクトルートを参照できることになる。
PROJECT_ROOT=$(abspath $(dir $(lastword $(MAKEFILE_LIST))))


これにより次のようgnumakeによるプロジェクト構成が可能になる:

  • 各サブディレクトリはconfig.mkをincludeする
  • config.mkは上記の方法でPROJECT_ROOTをexportする
  • サブディレクトリのMakefileはPROJECT_ROOT下のリソースを参照することができる。
    • virtualenvとか: $(PROJECT_ROOT)/virtualenv/bin/python foo.py
    • binに入れてあるコマンドとか: $(PROJECT_ROOT)/bin/my-build-util.py

求むPython/NLPハッカー

ソーシャルメディアのセンチメントアナリティックススタートアップが日本語ネィティブのエンジニアを探している。
http://www.netbase.com/job-posting/search-quality-engineer-japanese/
就労ビザ

実はこの仕事応募して受け入れられたんだけおど、個人的な理由で断わることになってしまった。厳しいインタビューのプロセスを経ると雇われていなんだけどチームと連帯感とかが発生するんだよね。それで、失望させてしまった申し訳なさからお手伝いを申し出た。リクルーティングボーナスなどの利害関係は一切ない。唯一下心があるとしたら、ここで良い印象を与えておいて次回縁があるときに覚えてもらいたい、ぐらい。

但し書き: 以下、面接過程の記憶から書いているので間違いや現時点であてはまらない情報があることが考えられる。あくまでも参考として読んでほしい。そしてnetbaseは以下の内容について何の責任ももっていないことを理解してほしい。

仕事内容は検索結果のrelevanceを管理するPythonシステムの構築と日本語におけるパフォーマンス評価作業と理解した。部門はQAっていうことになっているが、仕事の内容はツール開発と言語エンジンの評価と考えていいだろう。つまり、カスタマに直接触れる部分ではない。

ここで野心のあるエンジニアは「スケーラブルなエンジンをつくりたい」と思うが、しかし、ML系ではエンジンの部分は次々とインフラとしてcommodity化されていく傾向にある思う。最近だけでも、スタートアップやapache foundationからおびだたしい数のBig Data/MLツールが公開されている。最近の面接体験で、今迄かっこよかった「スケーラブルなシステム」ってのは実は金の力にまかせでマシンを横に並べたのをJava系のツールでつなぐだけの型にはまったオペレーションになりつつあると感じるようになってきた。

一方、いくらマシンを投入しても計算機はおバカさん。人間様が教えてあげないと学習できない。そしてデータから学ぶこととそうして得た情報の応用はビジネスの中核と切り離せないので、これがcommodity化されることはありえない。インフラよりデータ解析とML評価の方がビッグデータの時代で生き延びるのに有利。

そういう意味でこの仕事は面白いなと思った、ってのは知的正当化で、単にデータに触れる仕事がしたかっただけだ。だって日本語のツイートとか眺め(言語エンジニアが)その意味を汲み取ることを支援するなんて面白そうでしょ。しかもそれがpythonでできるんだから最高。

上司となる人はすごく切れる感じの言語学者。面接の過程であらゆる要望にきめこまく対応してくれた。同僚となるエンジニアはかなりのPythonハッカーだ。題材としてもチーム面でも申し分の無いポジションだった。

俺がつけなかったこのポジション、読者の一人が手にいれることを願う。もし質問とかあったら karasuyamatengu あっと gmail.com まで

gnu makeの変数を全てダンプする


hoge=foo

print-vars:
@$(foreach v,$(.VARIABLES),$(info $v=$($v)))

foreachで.VARIABLESが持つ全ての変数名をinfoで出力する。$($v)は$vという名の変数の値の参照だな。これだと環境変数やdefault,automatic変数も出力してしまう。これらを排除して「普通の変数」をは下のリンクを参照。

出力


$ make print-vars | sort
%D= # 訳のわからんものが色々ある…
%F=
*D=
*F=
+D=
+F=
.DEFAULT_GOAL=all # defaultターゲットをallじゃなくすこともできるようだ。
...
@F=print-vars # 現ターゲットか。
AR=ar
ARFLAGS=rv
AS=as
CC=cc
CHECKOUT,v=+co print-vars
CO=co
COFLAGS=
COMPILE.C=g++ -c
COMPILE.F=f77 -c
COMPILE.S=cc -c
...
GTK_MODULES=canberra-gtk-module # 環境変数
HOME=/home/tengu
INSIDE_EMACS=t # こんなのもあるのか。gnu emacsは特別扱い
LANG=en_US.UTF-8
...
^D=
^F=
hoge=foo # ユーザが定義したもの
出力を眺めているだけでも勉強になる。

参照

なんかやたら詳しいなと思ったら、gnu make unleashedの著者であり、このブログでも紹介したハッカーJOHN GRAHAM-CUMMING (http://www.jgc.org/)の解説だった。納得。

AppEngineで動くfacebookパーソナル検索エンジン

Facebookでうまく検索できない。自分や友人がつぶやいたことを探しているのに、他人やウェブの検索結果がかえってくる。あと日本語がうまく区切れていないようで引っ掛るべきポストが出てこない。ただ俺が使い方がわかっていないのだろうと思っていたらこんなのを発見した:
https://locateweb.appspot.com/
こういうものが出てくるということはやはりfacebookの日本語検索はへぼいんだな。

Facebook Connectでログインすると昔のグーグルのような真っ白な背景に検索ボックスだけの画面が迎えてくれる。テキストを打ち込むと素早くそれを含むポストが出てくる。デザインも挙動も簡素で快適だ。

実装は公開されている
https://github.com/hideki/locateweb

AppEngine+Pythonによる実装だ。ソースを見ると特殊ライブラリに依存せず、600行以内で仕上がっている。NoSQLでinverted indexを実装しているところが面白い。

プロトタイプということで、現在は自分の呟きしかインデックスされない。早期に友人の言葉も検索できるようになることを期待する。Googleの手が届かないソーシャルウェブを検索可能にする個人サーチエンジンってのはいけそうなアイデアだ。Googleがソーシャル苦手なぐらいにFaceBookも検索が弱そうなんで参入の余地は十分ありだ。

MOSH: 反響、覚書など

まとめ:

  • ロカールエコーや接続再開は快適
  • サーバのfirewallUDPポートを開けなきゃいけないのが面倒
  • セキュリティには未知の不安が残る (下の@m_bird氏のコメント参照)

MOSH紹介したところ大反響だった。反応をまとめてみる。

快適
  • sshからmoshに変えるとレスポンス爆速になった。 @ichyo
  • 入力でもたつかないの素晴らしい @ebith
セキュリティー大丈夫?
  • どうやって安全性を確保しているか見てからにしたいな @hmori
  • udpでちょっぱや☆ ssh代替プロトコル!が話題になってるけど、セキュリティ的に大丈夫なのかな?という不安が。sshは、枯れててセキッキュア!だから皆使うんであって……。 @m_bird
firewall開けなきゃいけないの?
  • mosh気になるけどFW開けるのがちょっとだけ気が引ける。@fujya
その他
  • かなり興味深いけど、肝心なiOS/Android向けのクライアントはまだみたいだね。@dora_kou
    • そう言われてみればそうだな。
  • UDP ってのはあれかな。昨今の回線品質だったら UDP のエラー率が十分低いから、mosh プロトコルのレイヤでエラー訂正しちゃって、速度優先したって事かな。
    • というより移動したりお眠りでTCPの接続が保てないという問題に取り組んでいるんで、どうせ接続が維持できないからstatelessなUDPでやっちゃおってことじゃないかな。
  • SSHが最終的な経路を保護するから、その下層で多少ゴニョゴニョしても大丈夫ってことかな…
    • 最終的な経路はサーバにポートを開けたUDPでauthenticationだけsshでラップしているだと思う。なんで不安が残る。

トラブルシューティング覚書

bash: mosh-server: command not found

Connection to example.com closed.
/usr/local/bin/mosh: Did not find mosh server startup message.

  • 問題:サーバ側にmoshがはいていない
  • 解決: mosh.mit.eduに表示されているやりかたでサーバにmoshをインストール
クライアントが次のような表示でブロックする

mosh: Exiting on user request... (13 s without contact.) [To quit: Ctrl-^ .]

  • サーバ側でUDP 60000-61000を開けなければならない
  • debianだと: sudo ufw allow 60000:61000/udp
ec2でサーバのfirewallに穴あけたのに接続時に次のメッセージでブロックする


...(13 s without contact.) [To quit: Ctrl-^ .]

  • AWS Management Console: Amazon EC2: EC2 Security Groups:
    • quicklaunch-1を選択
    • Inbound タブ
    • Custom UDP Rule メニュー選択
    • Port range: 60000-61000
    • Add Rule クリック
    • Apply Rule Changes クリック
  • 注:"default" security group でやっても駄目だった。
  • 注:Source: のipアドレスを制限した方が安全

上記はあくまでも覚書。このようなトラブルシューティングが自分でできない方はセキュリティーセンシティブなツールはインストールしない方が無難かも。