2008年12月29日月曜日

call/cc

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
yieldしてgetresultするスタイルは、どういった部類の考え方かというと、call/ccだ。

Scheme:使いたい人のための継続入門が参考になるであろう。

ところでここを見ると、

注目してもらいたい関数はcall-with-input-fileだ
call-with-input-fileの処理はキモだけを抽出すると、次のコードになる。

;; とんでもなく杜撰なcall-with-input-fileの実装(コアのみ)
(define (call-with-input-file file-name proc)
(proc (open-input-file file-name)))
つまり、call-with-input-fileは ファイルから入力ポートを作り出し、それを渡してprocをcallする という関数なのだ。 call procedure with input fileという感じだろうか。

これってpythonのwithとやりたいことは同じだ。()を使わずにかつよく使う使い方のみをうまく使えるようにするのがpythonの言語としての方向性なんだろう。

rubyではcall/ccやめるとかなんとか??(このへん)。古いんで2009年はどうなるかは興味ある人は自分で調べてください。

そもそもCall/CCがrubyに実装されている理由はただ一つで、「実装できてし まったから」である。最近のRuby関連メーリングリストの流れを見ていると 「Call/CCステ」の方向で あるのは間違いない。まあ、普通の人間が「おっ、ここはCall/CCを使えば カッコ良く実装できるね!」などと思いついたりすることはまずありえないので、 ある意味安心である。


LLの実装の構造上、実装することはそれほど難しいことではないが、使い手を選ぶといったところなのだろう。この点で言語の課題は「実装技術」ではなく「利用者負担」である以上、pythonの方針は正しいのだろう。

C/C++が低レベルの方向で切れ味がある分、指をきったり足を打ちぬいたりが簡単にできるように、lisp系の言語ではそれを抽象化の方向で同じように指をきったり足を打ちぬいたりできるといったところだろうか。

2008年12月28日日曜日

つづき

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
で、ruleがhandlerを登録してcallbackしてもらうのはごく普通なのだが、ruleを書く側の立場として生活しやすいtrickがほしい。どういうことかというと

def hanlder(self):
classmembers = self.guessClassmember(klass)
for func in klass.findall('./Stmt/Function/'):
if self.isClassMethod(func):
continue
for assattr in func.findall('.//Assign/AssAttr'):
if self.isClassMemberAssign(assattr, classmembers):
print 'defined at', classmembers[assattr.attrib['attrname']].attrib['lineno']
print 'substitute at', assattr.attrib['lineno']

と書いていたのを

def calc(self):
classmembers = self.guessClassmember(klass)
for func in self.wait('.//Class/Stmt/Function/'):
if self.isClassMethod(func):
continue
for assattr in self.wait('.//Class/Stmt/Function//Assign/AssAttr'):
if self.isClassMemberAssign(assattr, classmembers):
print 'defined at', classmembers[assattr.attrib['attrname']].attrib['lineno']
print 'substitute at', assattr.attrib['lineno']

とかしたい。self.waitは実際にはxpath文字列じゃなくてxpathを与えて作ったhandlerにしないと、methodの評価のタイミングが呼び出されたときなので、xpathがvisitにわたらないので駄目かな?あ~でもcalcに制御がわたってwaitを読んだときにcalcから抜けてhandlerのcallbackでyieldしてforに戻ってくる構造だからなぁ。

pollingするための口と、pollした結果どのhandlerをfireするか決めればいいのか。pollingするとほかのruleのcalcを呼ぶ可能性がある。ひとひねりしないとpollingされる関数が再帰してstack over flowだ。multithreadで書くほうが自然に思えてきた。実際にvisitするobjectがいるthreadがproducerで、handerを登録するruleがいる側がconsumerだ。といいつつも、次にvisitするものは何かをconsumerから教えてもらわないといけない。(ここがyieldになる。)

yieldが戻り先から値を返してくれるとwaitのかわりにyieldしてあげればらくに書けそう。yieldでxpathを受け取り、見つかったときにその関数(calc)を、見つかったnodeとともに返してあげればいい。すくなくとも2.4だとできないのでyieldから戻ってきたときに持たせるvalueを書き込むスペースを用意してあげることになる。美しくないが、複雑なコントロールフローを作るよりはましだ。しかしfor文が

while True:
yield ".//Stmt"
r = getresult()
#do something with r here.

こんな感じになってしまうだろう。かなり個人的な趣味をいうと、PEPで提案されているyieldの括弧のつけ方はきらいだなぁ。yield(42)とかのほうがいい。関数呼び出しっぽいが。しかしexception関係といい、2.4のgeneratorは微妙だね。

あとxpathに先読み否定(要はclassmethodをチェックさせたい)とかあるともうちょっとエレガントになる。まあ、蛇足ですが。

曖昧さ(対策案)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
class methodじゃないとことでのclass memberへの代入は変ということ。

def guessClassmember(self, klass):
return dict([(n.attrib['name'], n) for n in klass.findall('./Stmt/Assign/AssName')])

def calc(self, klass):
classmembers = self.guessClassmember(klass)
for func in klass.findall('./Stmt/Function/'):
if self.isClassMethod(func):
continue
for assattr in func.findall('.//Assign/AssAttr'):
if self.isClassMemberAssign(assattr, classmembers):
print 'defined at', classmembers[assattr.attrib['attrname']].attrib['lineno']
print 'substitute at', assattr.attrib['lineno']

klass.findall('./Stmt/Assign/AssName')]
はクラスノード直下でStmtノードAssignノードAssNameノードのすべてと読むのですが、(つまりクラス変数の代入)のこと。

どーも綺麗にかけない。名前のscopeの関係上必要となるものを全部渡してあげないといけない。継承の処理やimportの関係の処理をちゃんとやると大変。
多分、継承やimport関係はもっといろいろなruleを考えると共通化できて、treeを作ったときにまず最初に処理して使いまわすのが筋だろうね。nodeの受け取る関数とそのnodeを指定するpathの組でやるruleのモデリングが駄目すぎ。もうちょっと考えないとruleの数が増えてくるとfindallの回数が多すぎて耐え難いスピードになるだろう。でも考えるにはruleがそれなりに手に入らないと駄目なんだけどね。

最初はobjectにすることすら避けてruleをmethod + docでやろうかと思ったのだ、それだとruleを増やしていくとクラスが馬鹿でかくなる。Naming Conventionのチェッカーとか。xpathとhandlerの集合を受け取って、最小の回数のvisitで処理をしてほしいのだが。xpathをマージしてhandlerを呼ぶような何かを作るのは自明でないし。

findallの中身は、多分visitをやっているときのstackの中身とxpathのマッチなので、大筋としては{xpath: handler}なものを渡してしまえばよいだろう。

handler同士での相対pathと絶対pathの問題とそのときのデータ共有が解決しないとくさいfor文が消えてくれない。
普通はclass変数を先に書くが、メソッドのあとに書いてもよく、名前がどこで出てくるかはnodeをvisitした瞬間には解決できず、あとでまとめてやらなきゃいけない。handlerをruleが生成して、handlerはruleにreportする、最後にruleが問題を確定させるというような構造にせざるを得ないだろう。もちろん、単にxpathにmatchするnodeをかえされてもどこの何かわからないので、rootからのnode listをもらうほうがうれしいだろう、まあstackのcopyそのものだ。reportされたものをグループ化する仕組みがないとはまりそう。諸所の作業量からして2009年に持ち越しです。まあ、デザインが絞れただけよかったか。

taintみたいなことはしないので、classmemberへの参照から変更をやられるともちろん駄目。そもそもにそんなものはstaticには解決できない。

さて夕飯でもつくるか。

曖昧さ

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

class Hoge(object):
bar = 1

class Piyo(Hoge):
def __init__(self):
self.bar = 2

Piyoのなかでbarというinstance変数を作ろうとして実際のところはclass変数への代入が生じて間違えているのだが、継承ツリーが深いと気づけないかもしれない。どうしたら安全に書けるのだろうか。

名前を探すときにinstanceみてなければclassをみる
という類の話。

属性アクセスのデフォルトの動作は、オブジェクトの辞書から値を取り出したり、 値を設定したり、削除したりするというものです。例えば、a.x による 属性の検索では、まず a.__dict__['x'] 、次に type(a).__dict__['x'] 、そしてtype(a) の基底クラスで メタクラスでないものに続く、といった具合に連鎖が起こります。


a.__dict__にエントリを追加するのか代入しているのか区別がつかないことが原因のひとつではある。
問題が起こらないが、気になるときはparserとかformatterの__init__のなかでresetを呼ぶと時とかは

C++でやられているm_みたいなダサい解決策はcls_をつけるとかがそう。しかしそのようなnaming conventionは、文法上selfを書かせるpythonとしては正しい解決策(pythonic)ではないだろう。
いまどきの人はIDEつかうから問題にならないのかな・・・。


class Hoge(object):
index = dict(...)
def __getattr__(self, name):
begin, end, validator = self.index[name]
assert validator
return validator.from_bitarray(self._data[begin:end])

def __setattr__(self, name, value):
begin, end, validator = self.index[name]
assert validator
validator.to_bitarray(value, self._data, begin, end)

def __init__(self, s=None):
self.__dict__['_data'] = BitArray(66, binary=s, endian='<')

みたいなことをすればclassを書いた時点でインスタンス変数の名前は固定されてしまうので回避はできる。
いいかどうかは別にしてね。動的にインスタンス変数を増やすコードが簡単に書けるという人もいるかもしれないが、それはどのくらいつかわれるかとか、dictを変数でもってそのdictへ転送する手間は大したことじゃないはずだ。

propertyになぞらえていうなら(変ですが)、set, get, delの組じゃなくて、new, set, get, delの組であってほしいのです。

property( [fget[, fset[, fdel[, doc]]]])
新しい形式のクラス (object から導出されたクラス) における プロパティ属性を返します。
fget は属性値を取得するための関数で、同様に fset は 属性値を設定するための関数です。また、fdel は属性を 削除するための関数です。以下に属性 x を扱う典型的な利用法を示します:

class C(object):
def getx(self): return self.__x
def setx(self, value): self.__x = value
def delx(self): del self.__x
x = property(getx, setx, delx, "I'm the 'x' property.")
バージョン 2.2 で 新たに追加 された仕様です。


構文的にシンプルでわかりやすい回答がない、現実的なのか、そこまで問題なのかといった話のほかにもpython本体の実装のシンプルさの問題があるのだろうなぁ・・・。

2008年12月24日水曜日

Python 3000

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
諸般の事情でWhat’s New In Python 3.0を読んでます。関係するコードはどこかとか探そうとするとなかなか大変だったり。どうgrepするのかはたまたは提供している機能から連想するのか。

Need to know basisでしらべることしか普段していないと脳みその負荷の上がり方に痺れたり。

2008年12月19日金曜日

cacheの効果

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
1つ100k byteのエントリーを10k個つくって10k回set/getしてみた。


  • Memcacheはlocalhostで-m 2048(要は2GByteメモリを割り当てた)

  • pythonのtime.time()を使って測定。(なんか問題があったような気がする)



結果を見る限り、dictが早いの当然としてmemcacheとdiskの差だが、データセットがでかいのでswapしているが、memcacheはデーモン側で遅延書き込みが行われている分、diskをpythonからたたくdiskより、set関数の戻りが早いので速いといったところだろうか?

それからgetがすべての要素を1づつ順番になめているので、実際に起こるだろうアクセスの分布とはまったく異なる。その点にも留意しなければならない。なにか適当な分布関数を用意してlong tailになる局所性のあるアクセスを作り出さないとweb siteのキャッシュへの応用としてはだめだろう。

========== PerfDisk ==========
set: 0.00299947600365
get: 0.000581149601936
========== PerfMemcached ==========
set: 0.00100942788124
get: 0.000538439702988
========== PerfDict ==========
set: 6.94487094879e-06
get: 2.11861133575e-06

2008年12月18日木曜日

Cacheの階層

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
cacheの階層に思いをはせてめまいがした。

たとえばHTTPでなんかpageをリクエストしたとする。

サーバ側:
CPUのレベルでL1cache ~ L3 Cache
Memoryのレベルで
applicationが計算結果をmemory上にcacheする
applicationがmemcachdの上にcacheする。
applicationがdisk上にcacheする
OSがdiskアクセスをcacheする
apacheやsquidがネットワーク上でcacheする。

client側:
browserがcacheする。

なんて深いんだ・・・。

2008年12月17日水曜日

memo化につかうストレージとスピードup率

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
compilerモジュールとelementtreeを使って、pythonのcodeをparseしelementtreeを作るconverterを実装したが、結構遅いのでメモ化してみた。ただやるだけだと芸がないので、複数の方法を試した。

計測に使ったデータ。かなり小さい

#!/usr/bin/env python
# -*- coding: us-ascii -*-
# vim: syntax=python
#
# Copyright 2008 Noriyuki Hosaka bgnori@gmail.com
#
'''
module level document.
'''
import sys
from compiler import parse
import elementtree.ElementTree as ET

class Hoge(object):
def foo(self):
def bar(x):
return self
return bar

if __name__ == '__main__':
print "Hello World!"
print lambda x : x



  • dictを使う方法: 約144倍早くなった

  • memcacheを使う方法: 10 倍早くなった

  • fileをつかう場合: 1.3~2.2倍早くなった


測定回数がぜんぜん足りていないので、disk seekが起こる場合はなんともいえないだろう。
また、fileが小さいのでdiskの速度の上がり方の小ささはdisk seekの占める時間によって支配されているだろう。
through putが出る状況で、処理するfileが大きければもっと向上するだろう。
memcacheはpickleの処理とmemcacheとの通信処理だろう。ファイルが大きくなれば相対的に向上率は同じか低下するかだろう。

実用ではまた別途計測が必要だ。(/usr/lib64/python2.4下を全部処理して計測するとしないとね)

今後の実装方針としては、キャッシュの構造を二層化してまずはdisk cacheして次にmemcacheするか、disk cacheしてdict cacheするとかするとよいかもです。それで起動のたびに生成するのではなく、disk cacheから読もうとすればいいのでしょう。

2008年12月15日月曜日

改行コード

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
標準のライブラリは大丈夫だと思うが、site-packageにいれているようなものはひどいものがある。

class XMLSchemaFake:^M
# This is temporary, for the benefit of WSDL until the real thing works.^M
def __init__(self, element):^M
self.targetNamespace = DOM.getAttr(element, 'targetNamespace')^M
self.element = element

とかなっていると、fileobj = ile(..., 'rU')で読み込んであげないと改行のところでcompile.parse(fileobj)でエラーになってしまう。

しかしrUで読んでもだめなものがいくつかあった。


  • /usr/lib/python2.4/test/badsyntax_nocaret.py
    そもそもに文法的に正しくないテストコード(failすることを確認するためだと思われる。中身はこれ。

    def f(x):
    [x for x in x]=x


  • ファイルの終わりにspaceだけからなる行が存在していて、おかしくなっている。vimで開くとnoeolとかいわれる。

    • /usr/lib/python2.4/site-packages/yumgui/__init__.py

    • /usr/lib/python2.4/site-packages/yumgui/callbacks.py

    • /usr/lib/python2.4/site-packages/MoinMoin/_tests/test_pysupport.py

    • /usr/lib/python2.4/site-packages/testgears/command.py

    • /usr/lib/python2.4/site-packages/testgears/tests/test_collection.py

    • /usr/lib/python2.4/site-packages/turbojson/tests/test_sqlalchemy.py



  • ファイルの終わりにtabだけからなる行が存在していて、おかしくなっている。

    • /usr/lib/python2.4/site-packages/sqlobject/include/pydispatch/robustapply.py





このへんかな?

next()
ファイルオブジェクトはそれ自身がイテレータです。すなわち、 iter(f) は (f が閉じられていない限り) f を返します。for ループ (例えば for line in f: print line) のようにファイルがイテレータとして 使われた場合、next() メソッドが繰り返し呼び出されます。 個のメソッドは次の入力行を返すか、または EOF に到達したときに StopIteration を送出します。ファイル内の各行に対する for ループ (非常によくある操作です) を効率的な方法で 行うために、next() メソッドは隠蔽された先読みバッファ を使います。先読みバッファを使った結果として、(readline() のような) 他のファイルメソッドと next() を組み合わせて使うと うまく動作しません。しかし、seek() を使ってファイル位置 を絶対指定しなおすと、先読みバッファはフラッシュされます。

rpm packageするまえにText::FixEOLを通すような元気はあまりないなぁ。作る側でケアしてほしいのだが。

もうちょっと丁寧にみないといけないだろうが、今日はもうここまで。

2008年12月13日土曜日

PythonのLambda

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

import compiler import parse
from metrica.ast2et import convert, dump

ast = parse("""lambda : 0""")
t = convert(ast)
dump(t)

の出力は

PythonProgram
Module
+ doc : None
+ lineno : None
Stmt
+ lineno : None
Discard
+ lineno : 1
Lambda
+ kwargs : None
+ argnames : ()
+ flags : 0
+ lineno : 1
+ defaults : ()
+ varargs : None
Const
+ lineno : 1
+ value : 0

となる。

よく見ると、defaultsとかkwargsとかがある。ひょっとしてlambdaにdefaultが持たせられる??!

In [2]: f = lambda x=1: x

In [3]: f
Out[3]: <function <lambda> at 0x7fcfaf42c668>

In [4]: f()
Out[4]: 1

In [5]: f()
Out[5]: 1

In [6]: f(2)
Out[6]: 2

In [7]: f()
Out[7]: 1

In [10]: g = lambda x=0, y=1, z=2: (x,y,z)

In [11]: g()
Out[11]: (0, 1, 2)

In [12]: g(y=5)
Out[12]: (0, 5, 2)

うげー。ものすごく変な感じがする。まあ、pythonのlambdaはlispのlambdaじゃないからなぁ。

2008年12月12日金曜日

PyMetric撲滅運動その1

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
attributeが扱えるようになった。つぎはこの情報を使って、メトリック計算を行ったり、評価を行う機構を書けばいい。

入力

#!/usr/bin/env python
# -*- coding: us-ascii -*-
# vim: syntax=python
#
# Copyright 2008 Noriyuki Hosaka bgnori@gmail.com
#

'''
module level document.
'''
import sys
from compiler import parse
import elementtree.ElementTree as ET

class Hoge(object):
def foo(self):
def bar(x):
return self
return bar

if __name__ == '__main__':
print "Hello World!"
print lambda x : x


出力
elementtreeを自作のdumpでdump(serializeの問題を回避するため)


PythonProgram
Module
doc
module level document.

lineno None
Stmt
lineno None
Import
names [('sys', None)]
lineno 11
From
modname compiler
names [('parse', None)]
lineno 12
Import
names [('elementtree.ElementTree', 'ET')]
lineno 13
Class
doc None
bases [Name('object')]
name Hoge
lineno 15
Name
name object
lineno 15
Stmt
lineno None
Function
name foo
doc None
varargs None
argnames ['self']
flags 0
lineno 16
defaults []
kwargs None
decorators None
Stmt
lineno None
Function
name bar
doc None
varargs None
argnames ['x']
flags 0
lineno 17
defaults []
kwargs None
decorators None
Stmt
lineno None
Return
lineno 18
Name
name self
lineno 18
Return
lineno 19
Name
name bar
lineno 19
If
tests [(Compare(Name('__name__'), [('==', Const('__main__'))]), Stmt([Printnl([Const('Hello World!')], None), Printnl([Lambda(['x'], [], 0, Name('x'))], None)]))]
else_ None
lineno 21
Compare
lineno 21
ops [('==', Const('__main__'))]
Name
name __name__
lineno 21
Const
lineno 21
value __main__
Stmt
lineno None
Printnl
dest None
lineno 22
Const
lineno 22
value Hello World!
Printnl
dest None
lineno 23
Lambda
kwargs None
argnames ['x']
flags 0
lineno 23
defaults []
varargs None
Name
name x
lineno 23

PyMetric撲滅に向けて

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

def test_HelloWorld(self):
ast = parse('print "Hello World!"\n')
t = convert(ast)
self.assert_(t.tag == 'Module')
self.assert_(t.find('Stmt'))
p = t.find('*/Printnl')
self.assert_(p)

なるconvertを実装した。attributeやtextも実装しないとね。
変換してしまえばXPathが使えるので、イケてないコードの条件の記述が宣言的になる。

2008年12月11日木曜日

PyMetric

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Libraryとして使うことはあきらめたほうがよさそうだ。C++かなんかの悪い癖丸出しでまったくpythonicでない実装だ。読んでいて気が狂いそうだ。名前も滅茶苦茶だし。

class ComputeMetrics( object ):
""" Class used to compute basic metrics for given module."""
def __init__( self, metricInstance, context, runMetrics, metrics, pa, so, co ):
""" Initialize general computational object."""

paってparseargumentかなんかの略語で、素敵なことに標準のOptParseを使っていてくれてない。だいたいなんでmodelであるはずのComputeMetricにapplicationの引数のパース結果を丸ごと渡しているんだ?必要以上の情報をオブジェクトに渡すのは極悪です。不要な結合が発生して、切り出して使おうとすると困るじゃないか。コメントも糞の役にも立ちません。名前と関数名と引数の名前を見ればわかります。astかなんか使って実装するのが普通ですが、parserを自前しているようです。

マッチョコーディング丸出しでキモイです。死んでくれって感じです。metricがないとマトモなコードにならない人が書くからこういうことになるのだろうなぁ・・・。なんだろう、来ない電車を待つやつはいないじゃないけど。

どーすんべか。以下idea.

  • black boxなscriptとして使用する。必要に応じて出力をparse(涙)して使う

  • 仕様を抽出してMcCabeとslocの計算法がわかればいいかなぁ。

  • indentのcheck, defaultは4space、optionで2spaceを許容。tabは常に禁止。

  • PEP8に準拠かどうかをチェックする

  • spell/case/naming checker、できる範囲で品詞もチェックしたい。

  • 変数のscopeと変数の語長の関係をチェック。広いスコープには長い名前、短いスコープには短い名前

  • search and kill poor comments.

2008年12月10日水曜日

心理

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

  • ideaを考えているとき: うきうきする。

  • codeを書いているとき: なんにかが違うとおもう。

  • testを書いているとき: めんどくさいとおもう。

  • debugしているとき: 無能モノだと自分を呪う。

  • unittestが全部通ったとき: ほっとしてにっこりする。

2008年12月6日土曜日

規模から見た現状

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
自分が作っているものの規模を確認してみた。

backgammon用自作ライブラリ(bglib)

unit testが769case, 29 fail 15 error。テストを走らせるだけで1分掛かる。
16596 Line 98file
プロプラエタリ。20Kはいきそうな気がする。

汎用自作ライブラリ(tonic)

unit test 20 case, 1 fail 2 error
1687Line 22 file
まだまだ成長の余地がある。近日中に200Lineは増えるだろう。

packaging tool (未完成)


unit test 62 case, 4 fail 9 error
2644 Line 54 file


wxpygammon, FIBSのクライアントアプリ(未完成)

unit test 3 case 2 fail(そもそもかかれていない)
2192 Line 19 file
もちろん最初に出てきたLibraryがないと動かない。
UIがあるものを効率よくテストしながら作るのは工夫が必要。
現状のままでは力尽きる。

imageserver、稼動中、image.backgammonbase.com

unit test 16 case 14 fail(メンテされてないなぁ・・・)
1010 Line 13file
2つ自作Libraryに依存。機能がシンプル(ライブラリのレンダラーを呼び出すだけ)なので、TGによって自動生成されたコードが3割くらい(?)、残りでformの処理をしている。cacheのコントロールはdecoratorとしてライブラリに切り出してある。

bglogin、未稼働 backgammonbase.com

unittest 0
572 Line 11 file
自作Libraryに依存。
OpenIDのloginとusername/passwordによるログインの両方をサポート。
データベースの設計が7割だった。このモジュールがほかのモジュールにidentityを供給する義務を負っている。

bgwiki、未完成。wiki.backgammonbase.com


unit test 0
2368 Line 12 file
履歴を持つことができるwiki。wikiの文法自体はbglibで処理している。

今日ちょろっと書いたhandicapのあるトーナメントのシミュレータ。

281 Line 6 file
bglib/statにいれるか。



svnを止めてgitに移行するためにもツールを完成させないとなぁ。もう一度、探すかなぁ。
脱線だが、gitのtagじゃなくてlatest commit のdateをとるほうがよい気がしてきた。tag timeじゃなくてtag lastcommitとかいうコマンドを用意すればいいのだろう。拡張は簡単なはずで、実装の手間の問題だ。

大規模ソフトウェアの効率的な理解(その2)によると10万行までが小規模らしい。てか、10万行を超えたくない。2万行でも負担に感じる。(10万で10人なら一人1万行の倍の量なわけで・・・)
行数を減らすために努力しているつもりだ。書き換え込みの延べ行数だと倍は書いているだろう。
大規模なソースコード、何万行までいじれますか?@/.jpというのもあるが、ライブラリの場合は名前空間が鍵になる。もちろんアプリから共通部分を抜き出してライブラリ化する行為自体も大事だ。とくに私はbackgammonのコードばかり書いているので、それを扱うのに有利なライブラリを早期に取り出し、よいデザインにしないと自分が「何度も同じことを似たようなコードで書き、バグをあちこちに埋める」というひどい目にあうことになる。ちなみにbglibの下はこんな感じに分割されている。

drwxr-xr-x 3 nori nori 4096 Jul 18 13:12 protocol
drwxr-xr-x 5 nori nori 4096 Oct 29 14:09 image
drwxr-xr-x 3 nori nori 4096 Nov 17 18:14 doc
drwxr-xr-x 3 nori nori 4096 Nov 18 19:52 stat
drwxr-xr-x 3 nori nori 4096 Dec 6 23:08 depot
drwxr-xr-x 3 nori nori 4096 Dec 6 23:08 gui
drwxr-xr-x 3 nori nori 4096 Dec 6 23:09 model
drwxr-xr-x 3 nori nori 4096 Dec 6 23:16 encoding


参考までに普段使っているOSSの規模をあげておく。

gnubg
13万行、300file(.c, .h)

python 2.4
.h 164 file 55000行、.c 1014 file 38万行、.py 1620 file 40万行

apache httpd 2.2
.c 21万行 272 file, .h 2万4千行, 136file、apr等は含まない。

linux 2.6 stable 2.26.27.7
.c 235万行、11000file, .h 100万行 11000 file、(ドライバや自動生成されたコードを含む)



apacheはおもったより小さかった。機能に比してでかいのかもしれないがその辺はよくわからない。
ちなみにカーネルはconfig項目だけで1000を超えているらしい。パンピーにはまったく縁の変なハードのドライバに関する選択肢も含まれているだろうが。

pythonは.cがコア機能の実装codeなのかC実装なライブラリ.soを生むためのC codeなのかが不明。
pythonの方が間違いない区記述能力は高いので、ライブラリのほうが言語自体よりも規模が大きい(当然か)。

gnubgはgtkでUIを実装しており、その部分がかなり大きい。neural networkの部分はそれほど大きくは無いはず。
modelに相当するgnubg.cやeval.cはそれぞれ6000行ほど。

find . | egrep "gtk.*\.c$" | xargs wc

とかしてgtkのコードを数えると30000行という答えが得られる。


もしかしたら桁を読み間違えているかもしれないので鵜呑みにしないように。

個人でやっているのでマンパワーは当然無い。だから、こういうdeployを手動でやるようなことは避けたい。

アップロードした後、プログラム名の日付やサイズが変わっているか目視チェックを行います。一人で行うとミスが発生しますので、複数の目で行います。また本数でもチェックを行い二重、三重にミスを防ぎます。今回はこれができなかったのではないのでしょうか。


とくにgnubgやMaraDNSはcentosには含まれていないので、自分でpackageしなければならない。
kernelやapache、pythonもそういう方向にするだろう(セキュリティ上の理由から)。つうわけで、packageをある程度自動化したいのです。でないと開発時間が捻出できない。

長期的な観点からはunittestの件数不足、all green状態維持をどうするかということかな。
タスク管理やDBもやら無いといけないのでこれらは5000Lineから10Kくらいだろう。

2008年12月5日金曜日

update作業てんこ盛り?

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
maradns
あとapacheとpythonか。直接サービスに関係するものはup to dateなものにするdistroは使わない方針で。

ところでgoogle chromeはdigest認証をちゃんとこなせてないらしい。Tracでeditができない。作業環境がどんどん悪化している。svnとのsyncもできていないし。乗りかえどきかねぇ。

OpenID関連

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
TechCrunchによればGoogle Friend Connectが全ウェブサイトに開放とのこと。 にも書いたが、弱小・個人サーバにとってはありがたいことだ。

2008年12月4日木曜日

Server has been down

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
DNS + Http down has been for while.
Sorry for inconvenience who are using git repository, backgammonbase.com and so on.

2008年12月1日月曜日

5GByteのfileのmd5sum

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
かなり差がある。5GByteをなめるだけでもNotePCのHDDには負担だろう。readのbufsizeも10Kbyteとかに指定してたし。

  • おんぼろNotePC windows上でpythonのmd5を使って計算→10分?かなり待たされた。

  • Q6600 with RAID 10 4wayのnative md5sumを使って計算→1分ばかり?

deployのプロセス

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
こんな感じですかねぇ。

  • package支援ツール(gitage)
    • tag打ち→tagを指定してrpm生成→staging repositoryへup

  • staging環境でtest

  • staging repositoryからservice repositoryにcopy

  • service環境に投入

  • 動作を確認


後ろの部分はたいしたことないのですが、一番最初のプロセスが混沌とするとよろしくない。てか、さくさくやりたい。とくにcommitされたものだけをpackageに含めたいので、local modificationのあるfileが誤ってpackageされるとようなことは避けたい。

異常なこだわりかもしれない。別にはてなのクローンになりたいわけではない。

ただ間違いなくこうすることで事故後の復旧作業が楽になる。yumのrepositoryとrpm/yumのdbあとはドライバをバックアップしておけば、何もないところからほぼ自動で元のサーバを再現できるからだ。

次にやることはdatabaseのbackupと復旧、staging、data bisect testの実装だ。非常に初歩的なbisectは用意できたのだが。ああ、stagingと代替サーバを別imageにしなきゃ・・。もきゅう。

repositoryのupdate rss

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
とか作る方法ないのかなぁ?

changelogをblogとしてupしてrss配信。
自動的にRPM化してそのlogもup

repository検索

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
git grepはどこまで使えるのだろうか?gccをとり終わった調べてみよう。
とくに変更をさかのぼって調べられるのだろうか?
つぎの3つはかなり意味が違う。

  1. checkout したものをgrepできる。ただのgrep

  2. 指定したtag、objectをgrepできる。checkoutの手間がないgrep

  3. ヒストリをすべてさかのぼってgrepできる。これがほしい。時間軸でfilterできるとうれしい。


まあ、Linus様のことだからわかっていらっしゃって作っているとはおもいますが。

gccを全取得

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
「全」取得がおばかなのですが実行中。
http://git.tonic-water.com/mirror/gccにとろうとしているのですが、ぜんぜん終わりません。3日くらいとり続けています。途中でgit svnが停止するのでそのたびに再度fetchを実行している。エラーしたら自動的に再fetchするようにしないとダルいかも。

xenはhgでした。gitに変換はしていません。やっぱりプロトコル・フォーマットの変換がないと軽いです。多分、git-svn/git cvsimportがforkしてconnectionのopen/closeを繰り返すことが原因なんですけどね。sourceをチェックしたところで改修案を思いつくかどうか定かじゃないし、めんどくさいからなぁ。どーせ引数渡してpipeしているのだろうし。となると起動されたされた子プロセスが即終わるんじゃなくて再利用とかだろうね。httpのconnectionをkeep aliveするようにhackせにゃならん。そう簡単な事ではない。

2008年11月30日日曜日

よいソフトをつくる

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
無題 23:15

そんなの誰だってそうだよ。私は自分がプログラミングが得意な方だと思っているけれど、 Maeve の要求定義と設計に十ヶ月費やした。世間で言う、日常的な、広い意味でのプログラミングの能力と、「要求定義し、設計し、実装し、デバッグし、リリースし、メンテナンスする」という能力は明らかに異なっている。たった一人で後者ができる人間は、プログラマと呼ばれる者のうちのほんの一握りであり、それゆえハッカーだとかなんだとかいう尊称を与えられるのだ。

Joelはよいソフトウエアには10年かかるって言ってなかったっけ?作る側は使う側のことをわかるのに10年掛かるといったところだろう。使い手にあわせて10年かけて作り直し続けるといったところだろう。

Bindといういやなサンプルもあるぞ。まあそれでもHappyなほうだろう。ものすごい数のユーザがいて、ネットを支えているのは事実だ。質としては受け入れがたいとおもう人は山盛りいるだろうが。

結局、使い手と作り手が同じで作り手の能力がきわめて高い場合だろう。最近おもったのは、Linusとgitの関係だ。彼の日課がmergeであることに間違いはない。その要求を満たすためにgitを作ったのだ。

「リリースし、メンテナンス」することを考えると依存するものをpackage化する作業が手動で孤立していることはちょっとねぇ。repositoryをrsyncして、それを自動でrpm化したいのよねぇ。
とにかく凹んでいる暇があったら作るなりしっかり休むなり、意味がある(完成に向かう)方向で時間を使おう。とてつもなく長い道のりなのだから。

2008年11月27日木曜日

コンピュータ教育

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
まったくどこの教官だ、この質問の原因になった課題をだしたのは・・・。
http://mixi.jp/view_bbs.pl?id=37418835&comment_count=0&comm_id=602606

タイトルにある通り、テキストファイルをCSVファイルにしたいと思っております。

コンバートする際の詳細は、テキストファイルを読み込み、任意の文字列があった場合のみCSVファイルに書き出す、というものを作りたいです。

(例)

学生新聞ちゃんぽん○政治
学者学生ちゃんぽん

上記のテキストファイルの「学生」と「新聞」だけ抜き出して以下のように書き出したい


学生,新聞,学生

としたいです。

是非、分かる方いらっしゃいましたらご教授お願いいたします。

perlとかrubyとかpythonとかgaucheとかを使わない理由がわからない。VBでもいいだろうし。すぐに使える正規表現のない言語を選択するのはなぜ?マゾですか?たぶん「わかっている」大概の人は、one-linerで済ませるだろう。(多分、発生回数をカウントしたいだけのだろうから・・・。)メモ用紙を用意して正の字を一杯書くのも、人と場合によっては正しいかもしれない。

ともかく「コンピュータ・リタレシ」として考えた場合大いに問題がある。たとえて言うなら、建設工事現場の杭打ち機をステープラーの代わりに、折り紙の紙を切るのに大型チェーンソーをはさみの代わりに使いましょうみたいなノリだ。

概念的にコンピュータが動くとはどういうことかとか、ネットワークで通信できるのはなぜかとかdatabaseとはなにかを理解させる。つぎにコンピュータを使っていると必然的におこるテキスト処理をある程度(簡単な正規表現くらい)できるようにするのが先だとおもう。メールとかはいまどきほっておいても使えるようになるだろうし。たとえるなら、日常生活で紙を切りたいときに、はさみとカッター、手でちぎるの中から適切に道具を選んでつかえるような感覚を先に養ってほしい。

この辺の感覚を持った人が、発注側にまわるようになっていかないと、ソフトウェアとして何を作ればいいのか決める段階のコストが異様に高くなるし、ばかげた話がまかり通る気がする。コンピュータにまつわるコミュニケーションコストを十分に下げておくことは社会的要請だとおもうのですが、いかがでしょう?

そもそもにC/C++を使わせるためにはいろいろな準備が多すぎる。includeの説明をごまかしたり省略したりせずに、背景事情を含めてまともにできる人はどのくらいいるだろうか?ほかにもgccを使うとしてオプションの意味を説明するとか・・・したくないし、したところで「魚をとる方法ではなく魚をあたえる」ことになってしまう。何がいいたいかというと、情報収集能力(ばりばりにプラットフォーム依存)が前提になっているということだ。情報収集能力をあげる教育をしないと、変化についていけなくなってしまう。

Cに手を出す前に、構成管理ツールを使うことの意味や、文字コードの話、環境の話とかをするべきだろうし、それらを自分で解決できないとわけがわからない。

メモリへのポインタでのアクセスとかを理解させることには教育的な意味は無いとはおもわないが、順序や費用対効果というものがあるだろう。再帰の概念とかも必須だしscheme本を読ませるとか、各種アルゴリズム(quick sortなめたらあかんらしい。正しい実装は少ない。)を理解させるとか、でもこれってリタレシとよぶレベルを大幅に逸脱している。また、何かをセキュアに作ろうと思えば、OSの権限管理の知識は必須だし背景事情も知っている必要がある。体感的にわかるためには管理者経験も必要だろう。

1000行以上のコードを書こうとすれば、アルゴリズムを知っていて、言語の構文を知っていてアドホックに試して学ぶことができる以上のことが求められる。具体的には既存のよいコード(OSS, proprietaryを問わない)を読んでそこから学ぶことだ。モノが動くようにバグを入れないように書こうとすれば、どのような習慣が必要かも知らなければならない。

それでいて、いまどきのmacを買って来るといきなりhttpdが入っていたりしていきなりwebappが書けたり、本を買ってきてCD-ROM丸写しでwebapp動かして、構成するfileを単体のscriptとして動かすことを知らなかったりするんですよ。

いまいい年の大学教官が学生のころのコンピューティング環境はtime sharingとかでその当時のもっとも速いマシンよりも速いマシンをいまどきのガキンチョは一人で遊ぶために使うわけだ(PS3とか)。

旋盤とかの加工機械を一通りもっている町工場と適当なLinuxディストリビューションをもっているということは同じだ。理論上は何でも作れる。ただ作り手を選ぶし、何を作るかは注意深く選ぶ必要があるし、注意深く作る必要がある。またどう流通させるかも大事だ。


かなり話が発散したが、とにかく急激に物事が変わっていてかかわる人の意識や考えが追いついていない部分が多数あるというのは間違いない。また、急激な変化が内在している。その変化を引き起こしている要素への理解ぬきには変化に対応することはできない。リタレシとしてはその部分に触れる程度のことはしてほしいなぁ。

2008年11月26日水曜日

まだまだですな。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
reload [command名]
とかできちゃうとテストして書き換えてリロードしてテストかできるので楽かも。REPループは短いに限る。

gitage > stack
gitage > file ./gitage/specedit/testdata/gauche.spec
gitage > stack
0: <open file './gitage/specedit/testdata/gauche.spec', mode 'r' at 0x2aaaafb8beb8>
gitage > tag push piy
gitage > stack
0: <open file './gitage/specedit/testdata/gauche.spec', mode 'r' at 0x2aaaafb8beb8>
1: <git.Tag "piyo">
gitage > spec
gitage > stack
0: <gitage.specedit.base.SpecFile instance at 0x2aaaafb97a28>
gitage > write test.spec
gitage > !ls
: dist dump.spec gitage.egg-info log MANIFEST setup.py test.xml
build dump gitage gitage-quickstart log2 setup.cfg test.spec


こんな感じに書き換わる。まだName:しか書き換えてないが。

# Spec file to build Gauche RPM package
# $Id: Gauche.spec,v 1.51 2008-02-13 15:32:09 shirok Exp $
# In order to build different encoding-specific packages (like
# Gauche-euc-jp, etc) from a single source rpm, the actual package
# is created as a subpackage. The command
#
# rpm -ba Gauche.spec
# builds three packages:
# Gauche-VERS.ARCH.rpm ;; dummy package (no use; discard it)
# Gauche-ENC-VERS.ARCH.rpm ;; binary package with encoding ENC
# Gauche-VERS.src.rpm ;; source package
%define version 0.8.14
%define encoding utf8
%define threads pthreads
Buildroot: %{_tmppath}/rpm
Group: Development/Languages
Name: gitage
License: revised BSD
URL: http://practical-scheme.net/gauche/
Summary: Scheme script interpreter with multibyte character handling
Source: Gauche-%{version}.tgz
Version: %{version}

知っているだけでもタシになることがある

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
知っていたことは、Forthというスタックに何でもかんでもつんで処理をする言語が存在するということ。処理系を触れたことはないし、もちろんコードを書いたことは無い。stackの代わりにfifoを使った変態処理系でのパズルプログラミングはしたことがあるが。

今書いているpackage作業を助けるツールで、コマンドの出力としてpythonのobjectを取り扱いたいが、=(使いたくない)とかdefとかはいらない状況では、結果をstackに積んでしまうというのはいけているアプローチかもしれないというアイディアを思いつかなかっただろう。

scriptの作業でもshでの作業でもテンポラリの問題がついて回る。pipeは、複雑になってくるとわけがわからない。2つの結果を組み合わせるとかやり始めると「?」だ。

pythonのobjectの状態でstackにつめば幸せ。ipythonをおとなしく使うのも手ではある。ただ、pythonのobjectをそのままいじることは作業の粒度に比べて細かすぎる。拡張コマンドはpythonで書いてdirに突っ込めば起動時に読み込んで登録。標準のcmdを使っているから保管もしてくれます。

fileをあける操作をあっちこちで書かなくていいし。seekし忘れるとわけがわかりませんが。
引数をobjectで渡せます。これはうれしい。type少なくていいし。


gitage > stack
gitage > !dir
: dist dump.spec gitage.egg-info log MANIFEST setup.py
build dump gitage gitage-quickstart log2 setup.cfg test.xml
gitage > file MANIFEST
gitage > stack
0: <open file 'MANIFEST', mode 'r' at 0x2aaaafb94030>
gitage >

2008年11月25日火曜日

mirror

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
http://git.tonic-water.com/mirror/にapacheのmirrorを追加しました。またdirの構成を変更して、手垢がついたものはworksの下にいれることにしました。


La MaraDNSのツリーはどこにあるのだろう。source forgeにはtgzはころがっているがツリーは無いようだ。作者に聞くかねぇ。

悪文

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
わかるように書いてくれ。著者こそ説明できていない。

ネットワーク関係の文章は「機能」に対する名前と「製品の分類」に対する名前がごたまぜになっているのが原因で意味不明になっているのではないかと、私はおもう。

前者なら、文章のはじめに定義して使うべきだ。歴史的経緯があるなら後者だろう。


「ルータとL3スイッチの違い」を正しく説明できますか?
第1回 ルータとL3スイッチが誕生した理由
第2回 ルータとL3スイッチの真の役割とは!?
第3回 [仮想実験]もしルータをL3スイッチに置き換えたら
第4回 [仮想実験]もしL3スイッチをルータに置き換えたら

この文章の著者と編集者が、言葉に注意を払って書いてないことは明白である。
次に具体的な例を挙げる。

例1:「責任分岐点」ってなに?境界「線」だとおもうんですが?




例2: この絵の説明文いわく、「図3● L3スイッチは、ルーティング処理を内部の専用チップで処理するため、高速にパケットを転送できる」だそうだ。どう見ても絵から想像されるパケットの経路とそれから想像されるパフォーマンスの低下(ルータまで遠回り)と、説明文(専用チップ)が一致していない。書くなら、「ルータとL2間のトラフィックを減らすために、ルーティング機能を持たせ、L3となった。そのために追加しなければならない処理は重いため、実装にASICを用いることで解決された」となるだろう。




例3 説明文「図2● PCの台数が増加したため、各セグメントをハブで構成し、ルータによって接続するという形態が広まった」という説明で、ここでの「セグメント」はなに?イーサネットのコリジョン・セグメント、それともTCP/IPのセグメントかい?

絵の下までスクロールすると

その一方で、Ethernet(CSMA/CD:搬送波感知多重アクセス/衝突検出方式)の限界や、オフィス内のレイアウト変更に伴うネットワーク配線変更時の柔軟性などに新たな問題が生じた。こうした問題を回避するために生まれた「L2スイッチ」は、さらに「VLAN(Virtual LAN)」という技術を実装することになる。

とあって、コリジョン・セグメントではないかと推定できる。リピータハブ(ここでダムハブと書くと減点)を探すほうが難しいこの時代になにも説明無しで推定させるのはどうかとおもう。

こんな調子では「ルータ」という言葉が何をさす単語として使われているかは想像絶する。


費用対効果

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
ハッカーは もはやルートキットを使わない~感染攻撃を行う脅威全体の1%未満
費用対効果の問題でしょう。そんな手の込んだルートキットを作って感染させるコストが高いからでしょう。


結局のところ、犯罪者は「使う必要がない」というごく単純な理由で、ルートキットをあまり使っていないようだ。ハッカーらは、ルートキット技術を用いる代わりに、ウイルス対策ツールがマルウェアを識別するのを困難にする技術を開発してきた。例えば、1つの悪意あるプログラムについて何千種類もの亜種を作成し、攻撃のたびに切り替えて使うといった手法だ。

すでにあるものの亜種をつくる(自動生成)のほうが手の込んだルートキットを動くようにする(当然デバッグしなきゃいけない、ターゲットのハードは多岐にわたる・・・。)よりはるかに楽なはずだ。そして得られるものは対して差が無い。

踏み台がほしいなら狙い撃ちでルートキットを作るだろう。たとえば特定のlinux distributionを狙って配布元のサーバを破ってバックドアで汚染されたパッケージを配布させるとかね。それでもセキュリティの低いWindows PCを狙うほうが楽だろう。管理者の意識が低いし、台数も多いだろうから。

ひとつの推測としては、昔ながらの腕自慢をしたいハッカーはセキュリティ会社に入って、金のほしい(堕落した)ハッカーは犯罪組織にはいって配下のscript kiddyにツールを供給しているのではないだろうか?腕自慢のための研究をしていて、相手が経済犯であるという認識が欠落してるかもしくは不十分なのではないだろうか。

apacheをgit svnする

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
gauche、linux kernelやpythonにつづいてapacheもmirrorしてみようとおもってgit svnしたら、朝目覚めてもまだ終わってない。いま処理中のrevisionがpythonのそれよりも一桁多くて220000とかある。

asamaにつないだterminalでとり始めたのは失敗だった。うらであがっているeditorをkillするか。

asf/httpdのheadのrevistionは720206だから、最低今日一杯、長くて3日掛かるなぁ。

2008年11月23日日曜日

repackaged

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
I repackaged following softwares. Avalilable at collection


  • python-peckchek

  • python-antiparser

  • python-pydot


Use with good care. NO WARRANTY.

調査していたら、fuzzingねたに舞い戻った。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
haskellにはQuickCheckとかあるようだ。pythonでも調査したけど、peckcheckとかantiparseとかだが、どっちもツリーが公開されていないし、メンテもされてない。みんなどうしているのだろう。

検索しているときに、バカはバカにならない - 書評 - ファジング:ブルートフォースによる脆弱性発見手法を見かけた。この本は買った。へんなコメントがついているが、物書きが出版社にけちつけられないのわからないかなぁ?すくなくとも気安くできることじゃない。その辺は、新聞とかTVが広告主に強い態度に出られないのと同じ。ひろゆきもいっていた。「自分でたしかめろ」ってね。べつに2chのみに当てはまることじゃない。世の中、ただのものは無い。

fuzzingするのはよいのだが、膨大な量のテスト結果とテストデータを生成した場合、切り分けが面倒。git bisecみたいなことのdata versionを実装中。問題を起こす入力は1つの文字じゃく複数の文字からなる文字列なんで、そのままbisecは使えない。文字列のbeginとendを見つけてあげればいい。begin/endはどちらも1点なので、大丈夫なはずだ。例外ケースで2回出てくる場合とかあるだろうが、それはそのときだろう。あとは文字列をfileで与えてあげて、seekするようにする。でないとfileがそこそこでかいときに気楽に文字列をcopyされたくないので。んで、切り出したデータを使ってunittestを追記してくれると幸せだね。

fuzzingネタはtonicに入れよう。binarianがらみは外に出す。lineparserはtonicのライブラリに。同じようなもの書きすぎ。その前にgitで管理したときにtagと連携をとりやすくするためにgitageをなんとかしてbootstrapしないとね。

rpmlint

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
rpmのspec fileのparserを書いていて、ふと気になったので、テストケースに使っているspec fileをrpmlintにかけてみた。linux kernelですら文句を言われるようだ。ほかのpackageのspec fileはおして知るべし。

gitage/rpmspec/testdata/kernel.spec:10: W: hardcoded-path-in-buildroot-tag /var/tmp/%{name}-%{PACKAGE_VERSION}-root
gitage/rpmspec/testdata/kernel.spec:11: W: unversioned-explicit-provides kernel-2.6.27.5
gitage/rpmspec/testdata/kernel.spec: E: no-cleaning-of-buildroot %install
gitage/rpmspec/testdata/kernel.spec: E: no-cleaning-of-buildroot %clean


rpmlintの
一部
。うげー。プログラムというより、データだなこりゃ。check関係のfileはひとつのdirにまとめてpackageにすればいいのに。どうせcheckの内容が対象ごとに書いてあって、fileごとに対象が決まっているとかいうつくりでしょ?ならliblint/とかrules/とかpulgin/とlibcheck/かかにしておけばいいのに。名前空間使え~。

rpmlintはスクリプトからの起動時の起点。
SpeckCheck.pyはすさまじいif文のあらし。こんなコード読みたくない。
lintを書こうとすること自体の発想が力技でバッドノウハウに喧嘩売る行為なので、そういう発想のもとにコードを書けば必然なのかもしれない。

おいらだったらif文の代わりにinstance methodかなんかにして、

437 'no-spec-file',
438 '''No spec file was specified in your RPM building. Please specify a valid
439 SPEC file to build a valid RPM package.''',

みたいな部分はmethodのdocumentにするとか、
検出するためのregexpとhandlerとoutputにするtextが一箇所に集中していないのは読み手に優しくないなぁ。もしくは
regexp_xxx/handle_xxx/document_xxxみたいなconventionを採用するとか。
reportのi18nとかもどうするんだか。

2008年11月22日土曜日

出直し。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
rpmbuild時のログ(一部)

Provides: libexpat.so.1()(64bit) libexpat.so.1.5.2.debug()(64bit)
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires: /bin/sh libc.so.6()(64bit) libc.so.6(GLIBC_2.2.5)(64bit) libexpat.so.1()(64bit) rtld(GNU_HASH)
Processing files: expat-debuginfo-2.0.1-1
Provides: libexpat.so.1.5.2.debug()(64bit)
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1


yum update失敗時のログ(一部)

--> Processing Dependency: libexpat.so.0()(64bit) for package: elinks
--> Processing Dependency: libexpat.so.0()(64bit) for package: fontconfig
--> Finished Dependency Resolution
Error: Missing Dependency: libexpat.so.0()(64bit) is needed by package git
Error: Missing Dependency: libexpat.so.0()(64bit) is needed by package wxBase
Error: Missing Dependency: libexpat.so.0()(64bit) is needed by package avahi


libexpat.so.1とlibexpat.so.0は、名前が言うところでは「APIが違うので互換性が無い」。3. 共有ライブラリの「3.1.1. 共有ライブラリ名」を参照。


soname は、「lib」というプレフィックス、ライブラリの名前、「.so」という語句で構成され、さらに後ろに、ピリオドと、インターフェース変更時に必ず増加するバージョン番号、が続きます


fedora掲示板のなんかをみると同じといっている人がいるがねぇ。(おいらは違うとおもうが)

まあ、ソースコードを追えばわかりますが、所詮expatのみの例外だろう。2.0.1からとまっているし。メンテはされているようだが。

で・・・どうすっかなぁ。

ひとつには、expat 2.0.1のyum/rpmのpackage名がexpatであること自体が間違い。so.0なlibraryのpackageがso.1のそれで上書きされてしまう。expat2とか言う名前にしないといけない。仮にそうしたところで、cElementTreeをbuildするときにso.1を使うようにしなけれなならない。expatのpackage名問題をcleanに解決したところでcElementTree側の問題はまったく解決しない。
so.1をso.0としてインストールするdirtyな解決はリスクがでかい。

やはりcElementTreeのbuildの過程がどうおかしいのか追跡する必要がある。

しかしなぁ、状態が悪いとくだらないことも気づかないものだ。

ぶつぶつ2

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
現状の

[nori@asama]~/Desktop/work/expat% ls -ltra /usr/lib64/libexpat*
-rwxr-xr-x 1 root root 708 Jan 7 2007 /usr/lib64/libexpat.la
-rw-r--r-- 1 root root 238708 Jan 7 2007 /usr/lib64/libexpat.a
lrwxrwxrwx 1 root root 29 Aug 13 2007 /usr/lib64/libexpat.so -> ../../lib64/libexpat.so.0.5.0
[nori@asama]~/Desktop/work/expat% ls -ltra /lib64/libexpat.*
-rwxr-xr-x 1 root root 143048 Jan 7 2007 /lib64/libexpat.so.0.5.0
lrwxrwxrwx 1 root root 17 Aug 13 2007 /lib64/libexpat.so.0 -> libexpat.so.0.5.0
[nori@asama]~/Desktop/work/expat% ls -ltra /usr/lib/libexpat*
-rwxr-xr-x 1 root root 706 Jan 7 2007 /usr/lib/libexpat.la
-rw-r--r-- 1 root root 196030 Jan 7 2007 /usr/lib/libexpat.a
lrwxrwxrwx 1 root root 27 Aug 13 2007 /usr/lib/libexpat.so -> ../../lib/libexpat.so.0.5.0
[nori@asama]~/Desktop/work/expat% ls -ltra /lib/libexpa*
-rwxr-xr-x 1 root root 133056 Jan 7 2007 /lib/libexpat.so.0.5.0
lrwxrwxrwx 1 root root 17 Aug 13 2007 /lib/libexpat.so.0 -> libexpat.so.0.5.0


生成したrpm

-rw-r--r-- 1 root root 250604 Nov 22 00:58 /usr/lib/libexpat.a
-rwxr-xr-x 1 root root 817 Nov 22 00:58 /usr/lib/libexpat.la
lrwxrwxrwx 1 root root 17 Nov 22 00:58 /usr/lib/libexpat.so -> libexpat.so.1.5.2
lrwxrwxrwx 1 root root 17 Nov 22 00:58 /usr/lib/libexpat.so.1 -> libexpat.so.1.5.2
-rwxr-xr-x 1 root root 141168 Nov 22 00:58 /usr/lib/libexpat.so.1.5.2

x86_64用が生成されていない。

ぶつぶつ

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
expatの2.0.xをgitに持ってきたが、releaseスクリプトがcvs前提のようだ。
しかもそのままだとsoをどういうわけだか作ってくれず。yum installする段でlib64.soが存在しないと騒がれる。gitageから見て孫依存だから勘弁してほしい。

どういうわけだかcElementTreeがbuild時にシステムのexpatをつかってしまい、付属のexpatを使用しない。付属のexpatを使うようにするかなぁ。システムのexpatを書き換えるのはそれほどただしい方向ではないし。

2008年11月21日金曜日

expatのversion

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
expatのversionが.pyと.c、packageが期待している環境と現状の環境で違うらしい。どうしたものか。


[nori@asama]~/Desktop/work/elementtree/work% make test
python selftest.py
elementtree test
/home/nori/Desktop/work/elementtree/work/elementtree/ElementTree.py:728: FutureWarning: This search is broken in 1.3 and earlier; if you rely on the current behaviour, change it to './tag'
FutureWarning
**********************************************************************
File "/home/nori/Desktop/work/elementtree/work/tests/ptest.py", line 1050, in tests.ptest.bug_200708_version
Failed example:
parser.version
Expected:
'Expat 2.0.0'
Got:
'Expat 1.95.8'
**********************************************************************
1 items had failures:
1 of 4 in tests.ptest.bug_200708_version
***Test Failed*** 1 failures.
342 tests ok.
cElementTree test
**********************************************************************
File "/home/nori/Desktop/work/elementtree/work/tests/ctest.py", line 257, in tests.ctest.encoding
Failed example:
serialize(elem)
Expected:
'<tag key="&lt;&amp;&quot;&apos;&gt;" />'
Got:
'<tag key="&lt;&amp;&quot;\'&gt;" />'

2008年11月20日木曜日

Open IDで想定される攻撃

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Openid or ノーマルなusername+passwordのdual認証をTurbogears実装したけど、そもそもにOpenIDが、攻撃対効果の観点で攻撃者からの十分な保護を提供してくれるのかを考えたい。

まずは攻撃者が経済目的(つまりスパムコメント、スパムTBなど)の場合。

リスク1:OpenIDのアカウントがのっとられる。ユーザの責任といえなくも無い。まあphishingとかで抜かれたアカウントを利用してloginされるとどうにもならない。
ただ、攻撃側のメリットがどのくらいあるかというと、そこがはっきりしない。スパムを打ってくる攻撃側は、非常に安いコストでそこそこのリターンを得ることができるから攻撃してくるわけです。ほかのユーザからOpenIDのアカウントを搾取するのにかかるコストは結構高いのではないだろうか。被害者のPCにトロイの木馬とかを仕込めるならOpenIDのアカウントではなく、銀行のアカウントを奪うだろう。となると・・・

リスク2:不正取得したアカウントからのスパミングが攻撃の中心だろう。
ただ、アカウントがbanされたらすぐに死んでしまうので、メールの不正送信ほど攻撃にか掛かるコストは安くは無いはずだ。メールの場合はここの部分をOpen replayや自前鯖等で迂回できる。しかもProviderは戦うためのリソースを持っている(後述)。


リスク3:OpenIDのServerを制限する必要がある。信用するサーバをどう限定するか?Spammerが自前Provider鯖をたててそこの認証を持ってくる可能性がある。
http://www.baldanders.info/spiegel/log2/000407.shtml

とはいえ、OpenIDのProviderは大手が多いはずだから、大手のProviderのみを受け付ければいい。得体の知れないProviderは却下。この辺の社会構造はPKIに似ている気がする。verisignがえらいのと同じようにProviderとしてえらい会社がひとつあれば十分。メールがブラックリストでがんばるところを、ホワイトリストで済ませることができる。日本ならmixiとかgreeとか個人に「社会的な意味で」密着しているタイプのProviderが望ましいかな。提供する側と提供される側の利害が一致していて、SSLとちがって金はらわんでも使えるのがうれしい。ユーザとしても必要以上に秘密を知っている相手を増やさなくていい。

また、でかいところはphishing対策やアカウントの不正取得対策をするパワーを持っているので、それにただ乗りできるのが弱小サイトのメリットでしょうか。そのかわりユーザがどのサイトにloginしているかを知られてしまいますが。秘密を守るなら一箇所に集中して丁寧に守るほうがやりやすいです。弱小サイトが片手間でがんばったところでたかが知れている防御しかできませんから。ならばOpenIDにたよって、しないほうがいい。節約した労力でサービスを充実させるほうが生産的です。

経済的な観点から見てよくできて、時間は掛かるでしょうがweb上の認証のデフォルトになるのではないかとおもいます。


攻撃者が、経済犯ではなく、社会犯の場合は話が変わってくる。社会犯は被害者を貶めることが目的なので、プロフィールやエントリの改竄、被害者が不適切なコメントを残したように見せかけることが目的になる。また犯人は、地球の反対側にいるかもしれない経済犯と異なり、被害者の半径2メートル以内に入ったことがある可能性が高い。極端な話、背後から被害者のパスワードをのぞき見たり、被害者の所有するPCを直接操作して認証情報を抜き取る可能性がある。このようなケースでOpenIDを奪われると被害は甚大だ。ネット上での人格権を侵害されたとも言うべき状況になるだろう。

この辺はRP側が運用で対処するしかないかもしれない。この発言は本人ではない可能性がありますとか、被害者から連絡があった際に事後報告するなど。なんせ足跡つきまくりで露骨にわかるので・・・。

被害者が自覚しにくい被害はloginして情報を覗き見るだけの場合だろう。last loginのIPとその逆引き、時刻を表示するくらいしか思いつかない。

merge: elementtreeとcElementTree

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

[remote "save"]
url = git://git.tonic-water.com/public/elementtree-patched/.git
push = refs/heads/cimple

[branch "cimple"]
remote = save
merge = refs/heads/master

これでcimpleをcheckoutした状態でgit pushすると、remoteのcimpleにpushされます。

[nori@asama]~/Desktop/work/elementtree/c% git push
Counting objects: 109, done.
Compressing objects: 100% (107/107), done.
Writing objects: 100% (109/109), 128.01 KiB, done.
Total 109 (delta 63), reused 0 (delta 0)
To git://git.tonic-water.com/public/elementtree-patched/.git
* [new branch] cimple -> cimple

あとはworkにcheckoutしてworkでマージです。

[nori@asama]~/Desktop/work/elementtree/work% git checkout origin/cimple
Note: moving to "origin/cimple" which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
git checkout -b
HEAD is now at 473727f... added MANIFEST for python setup.py bdist_rpm

workからみてremoteのbranchをlocalにcheckoutし、それをベースにbranchをつくる(cimple)。
そしてmerge

[nori@asama]~/Desktop/work/elementtree/work% git merge cimple
Auto-merged CHANGES
CONFLICT (add/add): Merge conflict in CHANGES
Auto-merged MANIFEST
CONFLICT (add/add): Merge conflict in MANIFEST
Auto-merged PKG-INFO
CONFLICT (add/add): Merge conflict in PKG-INFO
Auto-merged README
CONFLICT (add/add): Merge conflict in README
Auto-merged VERSION
CONFLICT (add/add): Merge conflict in VERSION
Auto-merged samples/simple-ns.xml
CONFLICT (add/add): Merge conflict in samples/simple-ns.xml
Auto-merged samples/simple.xml
CONFLICT (add/add): Merge conflict in samples/simple.xml
Auto-merged selftest.py
CONFLICT (add/add): Merge conflict in selftest.py
Auto-merged setup.py
CONFLICT (add/add): Merge conflict in setup.py
Automatic merge failed; fix conflicts and then commit the result.

レレレのおじさん?

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

GIT-RERERE(1) Git Manual GIT-RERERE(1)

NAME
git-rerere - Reuse recorded resolution of conflicted merges

SYNOPSIS
git-rerere [clear|diff|status|gc]


ちょっとウケタ。

_elementtreeへ

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
.cでcElementTreeを_elementtreeに直すとかも作業のうちに入る。
で、cElementTree.pyを用意すると・・・。

diffした結果では、やっぱりpython2.6のxml.etreeよりcElementTreeのほうがよりversionが大きい。なので基本はcElementTreeにする。よくわからないのがexpat。expat.hにはどちらも1.95.xと書いてあるが、中身が若干違う。どっちにあわせるべかなぁ~~。

ちなみにローカルにインストールされているexpatは1.95.8だ。

pythonのtrunkがgit svnのエラーで取れていないのはイタイ。

2008年11月19日水曜日

elementtree

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

[nori@housyou]/usr/lib64/python2.4/site-packages% ls cElementTree.so
cElementTree.so
[nori@housyou]/usr/lib64/python2.4/site-packages% ls /usr/lib/python2.4/site-packages/elementtree
ElementInclude.py HTMLTreeBuilder.pyo SimpleXMLWriter.pyc
ElementInclude.pyc __init__.py SimpleXMLWriter.pyo
ElementInclude.pyo __init__.pyc TidyHTMLTreeBuilder.py
ElementPath.py __init__.pyo TidyHTMLTreeBuilder.pyc
ElementPath.pyc SgmlopXMLTreeBuilder.py TidyHTMLTreeBuilder.pyo
ElementPath.pyo SgmlopXMLTreeBuilder.pyc TidyTools.py
ElementTree.py SgmlopXMLTreeBuilder.pyo TidyTools.pyc
ElementTree.pyc SimpleXMLTreeBuilder.py TidyTools.pyo
ElementTree.pyo SimpleXMLTreeBuilder.pyc XMLTreeBuilder.py
HTMLTreeBuilder.py SimpleXMLTreeBuilder.pyo XMLTreeBuilder.pyc
HTMLTreeBuilder.pyc SimpleXMLWriter.py XMLTreeBuilder.pyo


結局、これを実現しなきゃいけないのよね~。
repositoryが別々なのを1つにマージしちゃおうかなぁ~~。履歴が消えてしまうのがイタイのですが。いや、branchの名前をうまく操作してあげれば、何とかなるかも。
mergedなrepositoryを掘るためにpurepythonとcimplとかいうbranchの名前にして
空のrepositoryにそれぞれをpush。空のrepository上でmasterにmerge。あとはdoctestなやつをunittest化してnosetestを使うようにするか。

pythonをupgradeしてもelementtreeのversionが1.3以降にならないのはイタイ。

python2.6のxml.etreeはこうなっている。

[nori@housyou]/usr/local/repos/git/public/python-mirror/python-2.6/Lib/xml/etree% ls
cElementTree.py ElementPath.py __init__.py
ElementInclude.py ElementTree.py


そしてcElementTree.pyの中身は

# Wrapper module for _elementtree

from _elementtree import *

である。だからbuildすると同じ場所に_elementtreeができるはず。.cはModules下にある。
.cな中身はこれで全部なのかはちょっと不明。手持ちのcelementtreeのファイルと中身を比較すればはっきりしますね。まあ明日以降ですな。

[nori@housyou]/usr/local/repos/git/public/python-mirror/python-2.6/Modules% ls _elementtree.c
_elementtree.c

celementtree

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
elementtreeだけでは話にならないというわけでpackagingを試みるわけですが・・・。

bdist_rpmするとここでこけるので

expat/xmlparse.c:23:19: error: ascii.h: No such file or directory
expat/xmlparse.c:85:22: error: internal.h: No such file or directory

とりあえず

gcc -pthread -fno-strict-aliasing -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -fPIC -DXML_STATIC -DHAVE_MEMMOVE=1 -DXML_NS=1 -DXML_DTD=1 -DBYTEORDER=1234 -DXML_CONTEXT_BYTES=1024 -Iexpat -I/usr/include/python2.4 -c expat/xmlparse.c

とやってみる。これはちゃんとコンパイルできる。なぜだ~。

追記:何のことは無い、tgzに入ってないようだ。

drwxr-xr-x nori/nori 0 2008-11-19 22:34:07 cElementTree-1.0.6.fix0/
-rw-r--r-- nori/nori 6158 2008-11-19 22:31:39 cElementTree-1.0.6.fix0/README
drwxr-xr-x nori/nori 0 2008-11-19 22:34:07 cElementTree-1.0.6.fix0/cElementTree.egg-info/
-rw-r--r-- nori/nori 223 2008-11-19 22:34:07 cElementTree-1.0.6.fix0/cElementTree.egg-info/SOURCES.txt
-rw-r--r-- nori/nori 565 2008-11-19 22:34:07 cElementTree-1.0.6.fix0/cElementTree.egg-info/PKG-INFO
-rw-r--r-- nori/nori 1 2008-11-19 22:34:07 cElementTree-1.0.6.fix0/cElementTree.egg-info/dependency_links.txt
-rw-r--r-- nori/nori 13 2008-11-19 22:34:07 cElementTree-1.0.6.fix0/cElementTree.egg-info/top_level.txt
-rw-r--r-- nori/nori 565 2008-11-19 22:34:07 cElementTree-1.0.6.fix0/PKG-INFO
-rw-r--r-- nori/nori 2693 2008-11-19 22:34:06 cElementTree-1.0.6.fix0/setup.py
-rw-r--r-- nori/nori 59 2008-11-19 22:34:07 cElementTree-1.0.6.fix0/setup.cfg
drwxr-xr-x nori/nori 0 2008-11-19 22:34:07 cElementTree-1.0.6.fix0/expat/
-rw-r--r-- nori/nori 32476 2008-11-19 22:31:39 cElementTree-1.0.6.fix0/expat/xmlrole.c
-rw-r--r-- nori/nori 41408 2008-11-19 22:31:39 cElementTree-1.0.6.fix0/expat/xmltok.c
-rw-r--r-- nori/nori 193934 2008-11-19 22:31:39 cElementTree-1.0.6.fix0/expat/xmlparse.c
-rw-r--r-- nori/nori 74357 2008-11-19 22:31:39 cElementTree-1.0.6.fix0/cElementTree.c


追記2:
不良生成なMANIFESTが原因でした。
ちゃんとbuildできるtgzつくるか、まったく何もしないかのどっちかにしてほしい。
Extesionに関してはsetuptoolsは無力だし。:P

やはり・・・。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
心配していたことが起きた。


[nori@asama]~/Desktop/work/gitage/libgitage% yum search mach
There was a problem importing one of the Python modules
required to run yum. The error leading to this problem was:

No module named cElementTree

Please install a package which provides this module, or
verify that the module is installed correctly.

It's possible that the above module doesn't match the
current version of Python, which is:
2.4.3 (#1, May 24 2008, 13:57:05)
[GCC 4.1.2 20070626 (Red Hat 4.1.2-14)]

If you cannot solve this problem yourself, please go to
the yum faq at:
http://wiki.linux.duke.edu/YumFaq


追記:
sudo rpm -U --force python-elementtree-1.2.6-5.x86_64.rpm
で上書きして出直し。
さてcのソースを探さねば。

ほしいものはdatabaseじゃない

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
履歴を管理できるdatabaseかなぁ。
web applicationを一度動かし始めた後は、過去のデータとプログラム側の型の整合性が問題になる。神聖にして不可侵(?)なtableの変更が自由にできればいいのだが。

プログラムはテストを書きながら変更していけば何とかなるが、ユーザが作り出したデータはなかなか微妙だ。過去のデータが「同じ結果を生み出す」ことを保障しようとするとわけがわからない。wikiとかならレンダリング結果が同じ、つまりdom等価ならいいやというのならまだ望みがありそうだが。

「データが書き換わるタイミング」と「プログラムが書き換わるタイミング」の問題。まあ最悪とめればいい。電話システムとかじゃないんだから。

すぐに思いつくのは、

実データに対して安全、気楽(?)にテストを流せる仕組み。
時間軸上での実データセットを指定できる。(version管理)
データをbisecして問題点を自動切り出しする仕組み。

とかかなぁ。

考えてみれば、web applicationてのは設備+データ+プログラムで、構成管理されているのは、現状プログラムだけだ。データや設備も構成管理に含めれることができればより幸せ度は高いはずだ。サービスを丸ごと仮想化して構成管理したいんだよね。

設備は仮想化で何とかできるはず。ネットワークとかは無理かもしれんが。
データもなんとかなるはず。HDDは安いし、あのはてなですら十数GByteだ。

とはいってもsqliteで構成して、dbのファイルをgitに入れるのは明らかにだめだろう。
データベースはasidのためにがんばっているのだから、やっていることはgitなどのrepositoryと根本では近いはずだ。近い将来、出てくるはず。

やりたかったことはこっちだ。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
別にpropertyでなくてよい。classが生成されたときに、継承ツリーに沿って計算してくれればいい。いちいち継承されたクラスでごたごた書いたり、meta classを使う側のクラスの定義を汚し(継承ツリーをたどるコードを書く)たくない。

あとは_xをdelすれば完全犯罪(?)です。


class KlassMeta(type):
def __init__(cls, name, bases, dictionary):
base = cls.__base__
if base == object:
cls.x = dictionary.get('_x', 0)
else:
cls.x = dictionary.get('_x', 0) + base.x

class Klass(object):
__metaclass__ = KlassMeta
_x = 1

class DerivedKlass(Klass):
_x = 2

print Klass.x
print DerivedKlass.x

kp = Klass()
dkp = DerivedKlass()
print kp.x
print dkp.x

実行結果

1
3
1
3

あたまがmetametaでござる。

metaclassでpropertyを使う。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
気持ち悪い。

usual = UsualWayOfUse()
usual.x = 1
print usual.x

print '=' * 60

class KlassPropertyMeta(type):
def fgetx(self):
base = self.__base__
if base != object:
return base.fgetx() + self._x
return self._x
x = property(fgetx, None, None)

class KlassProperty(object):
__metaclass__ = KlassPropertyMeta
_x = 1

class DerivedKlassProperty(KlassProperty):
_x = 2

print KlassProperty.x
print DerivedKlassProperty.x

kp = KlassProperty()
dkp = DerivedKlassProperty()
print kp
print dkp


実行結果

[nori@asama]~/Desktop/study/python/experiment% python classproperty.py
1
============================================================
1
3
<__main__.KlassProperty object at 0x2aaaaab48e50>
<__main__.DerivedKlassProperty object at 0x2aaaaab48e90>

stash

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
事故ってstashをほかのbranchにapplyしてしまって、さらにあるべきブランチをcheckout -fした。なんかだめげ。

あああ、checkout -fしてcheckoutして戻ってきてからapplyしなおせばOKだった。一件落着。

2008年11月18日火曜日

快眠の元

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

[nori@asama]~/Desktop/work/gitage% nosetests libgitage/rpmspec_test.py
...................
----------------------------------------------------------------------
Ran 19 tests in 0.147s

OK

ふう。

unofficial fix start line for elementtree

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
python-elementtree 1.3.a3.fix0 is the base code for unofficial fix.

I gurantee nothing. Use it with good care.

The RPM package will be found in
http://yum.tonic-water.com/collection/

Wanna see code? Here is git repository.
http://git.tonic-water.com/elementtree-patched/

This is based on http://svn.effbot.org/public/elementtree-1.3/

And it is git cloned to http://git.tonic-water.com/elementtree-1.3-mirror/

elementtree v1.3をpackageする。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
まずは準備。

昨日pythonのコードもかっぱらってきたが、1.2の中でも古い部類に入るようだ。ということで、1.3を自分でbugfixしたりpackageしたりする準備。ミラーはdaily syncする必要ないでしょう。開発止まってるみたいだし。


で、python setup.py bdist_rpmするも・・・あれ?

hard linking elementtree/TidyTools.py -> elementtree-1.3a3-20070912-preview/elementtree
hard linking elementtree/__init__.py -> elementtree-1.3a3-20070912-preview/elementtree
creating dist
tar -cf dist/elementtree-1.3a3-20070912-preview.tar elementtree-1.3a3-20070912-preview
gzip -f9 dist/elementtree-1.3a3-20070912-preview.tar
removing 'elementtree-1.3a3-20070912-preview' (and everything under it)
copying dist/elementtree-1.3a3-20070912-preview.tar.gz -> build/bdist.linux-x86_64/rpm/SOURCES
building RPMs
rpmbuild -ba --define _topdir /home/nori/Desktop/work/elementtree/work/build/bdist.linux-x86_64/rpm --clean build/bdist.linux-x86_64/rpm/SPECS/elementtree.spec
error: File /home/nori/Desktop/work/elementtree/work/build/bdist.linux-x86_64/rpm/SOURCES/elementtree-1.3a3_20070912_preview.tar.gz: No such file or directory
error: command 'rpmbuild' failed with exit status 1

なんでだ?

[nori@asama]~/Desktop/work/elementtree/work% ls build/bdist.linux-x86_64/rpm/SOURCES
elementtree-1.3a3-20070912-preview.tar.gz

は?!-と_が違う!

よくわからないが

#from distutils.core import setup
from setuptools import setup
...snip...
#version=open("VERSION").read().strip(),
version='1.3a3-20070912-preview',

とかしてsetup.cfgを追加してごまかす。gitなときはversionをみんなどうしているんだろう・・・。


追記:
原因はこの子ですね、おそらく。
/usr/lib64/python2.4/distutils/command/bdist_rpm.py
でこんなことになってます。しかしわかったところねぇ・・・。

def _make_spec_file(self):
"""Generate the text of an RPM spec file and return it as a
list of strings (one per line).
"""
# definitions and headers
spec_file = [
'%define name ' + self.distribution.get_name(),
'%define version ' + self.distribution.get_version().replace('-','_'),
'%define release ' + self.release.replace('-','_'),
'',
'Summary: ' + self.distribution.get_description(),
]

2008年11月17日月曜日

しまつた。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
elementtreeの1.3からのfeatureを使ってしまった。

→Last updateが2007とかだめじゃん。

2.5からpythonの標準ライブラリに取り込まれたんだっけ?

pythonをgit-svnしてしまえ。(これが間違い!!)

revisionが67240とかなのに、始めて10分でまだ5000とかだ・・・。

そうだよ、matzがrubyをgitするのに一晩掛かったのだから、同じことだ。

elementtreeのversionだけでここまではまるとは。うかつだった。
python本体のversionをあげたり、混在したりするつもりはあまり無いです。yumとかが依存しているので混乱の元です。/usr/local/binにいれるのは手ですが、やっぱりやだなぁ。だってpythonまわりがdriesとかから入れているrpmだから事故りたくない。


見ていると1revision/1秒なんで60000秒つまり1000分それは18時間とかか?今日はもう寝ようか。。。。まあ、一度とってしまえば、ローカルにヒストリを含めて持っているのは幸せだろう。git-svnでネットワーク越しにimportするよりsvnでmirrorしてからローカルでimportしたほうがよかったかもしれないが、もう間に合わないだろう。repositopry全体をtgzしたものとかは落ちていないだろうし。

ちなみにCPU負荷は6%x2くらい(2.5と2.6を同時にimportしている)ためです。おそらくDisk I/O boundでしょう。gyaoは普通に視聴できてたし。

追記:
diskでもないなぁ・・・。なんだろう。メモリ帯域?、プライオリティが間違っている?・・・niceは15だしなぁ。

sudo /usr/sbin/lsof -i -P | grep git
でなんとなくわかった。tcp connect/closeを繰り返すらしい。そりゃだめだわ。

git-svn 13378 gitbot 4u IPv4 23937457 TCP housyou:58265->svn.python.org:80 (CLOSE_WAIT)
git-svn 13378 gitbot 7u IPv4 25139921 TCP housyou:60507->svn.python.org:80 (ESTABLISHED)
git-svn 28084 gitbot 4u IPv4 24045356 TCP housyou:36746->svn.python.org:80 (CLOSE_WAIT)
git-svn 28084 gitbot 7u IPv4 25138436 TCP housyou:60506->svn.python.org:80 (ESTABLISHED)
git 29497 gitbot 4u IPv4 24045356 TCP housyou:36746->svn.python.org:80 (CLOSE_WAIT)
git 29497 gitbot 7u IPv4 25138436 TCP housyou:60506->svn.python.org:80 (ESTABLISHED)
git 29507 gitbot 4u IPv4 23937457 TCP housyou:58265->svn.python.org:80 (CLOSE_WAIT)
git 29507 gitbot 7u IPv4 25139921 TCP housyou:60507->svn.python.org:80 (ESTABLISHED)

Intel Core i7

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
売り出されたみたいですね。

安定したMB、対応したLinuxが手に入るまで関係ないですが、ロードマップを知っておくことはよいことなので、ちょっくら調査。


使われていないCPUコアを動的に停止させ,TDP(Thermal Design Power:熱設計電力)の許す限り,残りのコアの動作クロックを引き上げることができる「Turbo Mode」がサポートされている。


Coreごとにクロックを変更できるらしい。また、稼動していないコアは電力を止められる。OS のプロセス管理やOS仮想化との兼ね合いがどうなるのか興味深いところです。

OSの歴史はプロセスの切り替えができる、複数ユーザのサポート、プロセスの切り替えが強制(プリエンプティブ、真のマルチタスク)、複数プロセッサ、仮想化と進んできている。複数プロセッサや仮想化の次になにが来るのかは想像がつかない。ただ間違いが無いのは複数のプロセッサに「賢く」タスクを割り付けることができることがOSの価値の一部を作ることは間違いないだろう。

いまだにシングルプロセッサでもタコなことはいくらでもあるが(UIがとまるとか)。まあ、OS側でタスクの意味(backgroundでちまちまこなしていけばいいのか、UIのイベントなのできたらすぐにこなしてほしいが重くは無い)を理解していないのでしょうがないとも言うが。APIを通してプログラマに決めさせるのはナンセンスなので、プログラムの挙動から推定するか走っているときにユーザにプロセスのタイプを指定させるしかないのだろう。

そのうちにシステムが、測定ベースでTDPを自動認識するようになるのも時間の問題だろう。だって温度計もついているし、消費電力も測定することが可能だろうから。

2008年11月13日木曜日

パッケージ管理

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
rpm+yumでおおよそ満足(70点)くらいなのだが、いろいろと。

rpm+yum

  • distribution標準

  • 依存性解析が遅いときがある

  • 大量(100>)にupdateするとおかしくなることがある。とくにyum自身が書き換わる場合。さきにyum update yumすべし。

  • プラットフォーム依存

  • binary生成が簡単とはいえ手動、rpmbuild -taなど

  • 何も考えなくていいので楽

  • build済みなので速い、とくに台数が多いときは有利だろう

  • yumのサーバ(createrepo + http)を立てて、そこを参照すればかなり自由にできる



setuptools/easy install(python)

  • pythonの世界、でもまあ、setup.py bdist_rpmで回避可能なことも。

  • プラットフォーム依存していない

  • 依存性管理がタコな時がある。rpmでいれたpackageがversion管理用のfile(egg-info
    )を持っていないときなど

  • package serverの立て方は?(知らないだけだとおもうが・・・)



macportsの不満点

  • 標準じゃない(finkも同じ)

  • 作り方不明

  • 依存解析が遅い

  • buildに失敗することがある。

  • buildするので時間が掛かる。作業台数が多いときはひどい目に遭いそう。


う~んこれだけ書いただけでもこなれてない感じがする。
redhatに乗っかっているcentosと比較するのが間違いだが。


しかしなんでどいつもこいつも依存解析が遅いのだろう?naiveにはsatisfiedとrequestedの辞書を持っていて、

最初は両者とも空
requestedにインストール要求されたpackageが入る

while requested:
requestedからpackageを取り出す。
packageが依存するpackageでa)かつb)なものをrequestedに追加する。
  a)satisfiedに存在しない
b)systemに存在しない
取り出したものをsatisfiedに入れる。
assert requestedは空
satisfiedに残ったものをインストールする。

とかになるとおもう。なんでこれが遅いんだか。

本当はrpmbuildに必要なデータを取ってくる方法を記述したfileを用意してそれに対してrpmbuildが走り、rpmがcacheされるような仕組みがいいんですけどね。portsとrpmのいいとこ取りみたいな。
fileにsourceをgetする方法を記述しておけば(たとえばsvn updateなりgit pullなり)、
勝手にversion番号を打ってくれて、rpbuild。あとはyumでウマーな仕組み。

gitのある生活(2)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
今日は、myserverにgit pushするための設定です。
client, serverはともにLANにあるものとします。つまり認証なしのケースです。

この例での作業フローはoriginでclone/fetchをcrontabで行っています。それをbranch masterにpullしてきてローカルでマージし、こんどそれをlocalのbackgammonbaseにmergeします。最後にmergeしたbackgammonbaseをworkにpushします。

client側の設定です。まあ、branchごとに対応するremoteを設定できることがよいところでしょう。masterをcheckoutしてgit pullと打てば、git.tonic-water.comからとってきますし、backgammonbaseをcheckoutしてgit pushと打てばwork.tonic-water.comにpushされます。

[remote "origin"]
url = http://git.tonic-water.com/gnubg-mirror/.git
fetch = +refs/heads/*:refs/remotes/origin/*

[remote "save"]
url = git://work.tonic-water.com/gnubg/.git
fetch = +refs/heads/*:refs/remotes/origin/*

[branch "master"]
remote = origin
merge = refs/heads/master

[branch "backgammonbase"]
remote = save
merge = refs/heads/master


サーバ側の設定
git daemonのためにport9418をあけましょう。
使用したいrepositoryをおくdirを決めましょう。今回は
/usr/local/repos/git/work
におくことにしました。この下に/gnubg, /gnubg/.gitが配置されています。

server.shを用意しましょう。中身はこんな感じです。

#!/bin/sh
git-daemon --verbose --base-path=/usr/local/repos/git/work --reuseaddr --export-all --syslog --detach

manに書いてありますが、reuseaddrはつけておいたほうが無難でしょう。再起動をかけたときにすぐつながるし。--syslog --detachは動くようになってからつけましょう。base-pathはapacheでいうところのDocumentRootですな。--export-allしない場合はdirごとになんかfileをおいてコントロールするらしい(エラーしやすい気がするからあまりよいソリューションとは思えないが・・・)。私は、特定のdirの下を再帰的に特定用途に使うほうが筋がよいとおもっているのでexport-allです。

server.shを適切な権限で実行します。privilege dropしているとは思えないので、普段使うユーザアカウントやrootで実行しないようにしましょう。念のため。

gnubg/.gi/configtはこんな感じです。書き加えられているのはdaemonの部分で、どういうことかはmanを参考にしてください。この設定は認証が無いのでセキュアなLANからのみ使用可能にしましょう。私のLANは有線しかないです。

[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[daemon]
uploadpack = true
uploadarch = true
receivepack = true
[remote "origin"]
url = http://git.tonic-water.com/gnubg-mirror/.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master

2008年11月12日水曜日

asm習作

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
感想

  • 寝癖およびAT&T formatとintel formatの違いで頭がぐしゃぐしゃ。

  • divで割られるreigsterがedx:eax固定。恐怖のRISC脳はsrc/dst合計4つを想像する

  • test x xとかxor x xとか。恐怖のry




unsigned int
asm_gcd(unsigned int x, unsigned int y)
{
/* asm memo
* mnemonic: div
* to be diveded edx:eax
* diveded by source: r/m32
* result
* div -> eax
* mod -> edx
*/
/*
skelton
div, mod = divmod(x, y)
divmod(y, mod)
*/
/*
implementaion idea (intel format)
eax, edx = div(edx:eax, ecx)
mov eax, ecx; # next x(1)
mov ecx, edx; # next y
mov edx, 0; # next x(2)
loop if ecx:
return eax
*/
assert(x >= y);
__asm__ __volatile__(
"init: \n\t"
" xor %%edx,%%edx \n\t"
"while: \n\t"
" div %%ecx \n\t"
" mov %%ecx,%%eax \n\t"
" mov %%edx,%%ecx \n\t"
" xor %%edx,%%edx \n\t"
" cmp %%edx,%%ecx \n\t"
" jne while \n\t"
: "=a"(x):"a"(x),"c"(y));
return x;
}

unsigned int
w_gcd(unsigned int x, unsigned int y)
{
unsigned int z;
while(y){
z = y;
y = x%y;
x = z;
}
return x;
}

unsigned int
c_gcd(unsigned int x, unsigned int y)
{
if(y){
return c_gcd(y, x%y);
}
return x;
}

int
main(int argc, char**argv)
{
assert(c_gcd(1, 1) == 1);
assert(c_gcd(4, 2) == 2);
assert(c_gcd(4, 3) == 1);
assert(c_gcd(12, 3) == 3);
assert(w_gcd(1, 1) == 1);
assert(w_gcd(4, 2) == 2);
assert(w_gcd(4, 3) == 1);
assert(w_gcd(12, 3) == 3);
assert(asm_gcd(1, 1) == 1);
assert(asm_gcd(4, 2) == 2);
assert(asm_gcd(4, 3) == 1);
assert(asm_gcd(12, 3) == 3);
assert(asm_gcd(15, 3) == 3);
assert(asm_gcd(144, 64) == 16);
return 0;
}


ちなみに-O2でコンパイルするとw_gcdもc_gcdは同一のasmに落ちて、手で組んだコードとほぼ同じになる。末尾最適化とレジスタ割付の最適化はちゃんと行われているようだ

.globl w_gcd
.type w_gcd, @function
w_gcd:
.LFB28:
test %esi, %esi
mov %eax, %edi
jne .L7
jmp .L2
.p2align 4,,7
.L9:
mov %esi, %edx
.L7:
xor %edx, %edx
div %esi
mov %eax, %esi
test %edx, %edx
jne .L9
mov %eax, %esi
.L2:
rep ret



.globl c_gcd
.type c_gcd, @function
c_gcd:
.LFB29:
test %esi, %esi
mov %eax, %edi
jne .L17
jmp .L12
.p2align 4,,7
.L18:
mov %esi, %edx
.L17:
xor %edx, %edx
div %esi
mov %eax, %esi
test %edx, %edx
jne .L18
mov %eax, %esi
.L12:
rep ret

2008年11月8日土曜日

塞翁が馬

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
loadとaddの依存関係を解析してpipelineがとまらないようにする話は、耳にタコができるほど聞いたが、addでoperandにaddressをとれるというのは、依存関係の解析の手間が省けるから中の人は楽なのかもしれない。load addって命令が連続しているのを調べて、loadをaddより十分先行させないとaddがメモリ読み出し待ちになってしまうからな。load addが依存しているのをloadさきのregisterとadd元のregisterが同じということからみつけるのと、そもそも1つの演算である場合では後者のほうがはるかに楽だし。

とにかく直交性の高い素片に分解してしまえという考え方は、最終的な演算の直前なら正しいのかもしれないが、プログラムがコンパイルされた時点にすることとして正しいかどうかは疑わしい。実行codeから依存関係を知るコストが高いので。

さらに命令セットの寿命とプロセッサ設計の寿命は前者が圧倒的に長い一方で、直交性をあげることでのゲインの具合とその箇所はプロセッサ設計の寿命に依存していて、商業的には損な選択なのかもしれない。

μOPはよく考えられているのか運がいいのか。命令セットを変えられないのは64bit化のときにIntelがポカをやったことからあまりにもclearだ。しかしまあ、LLしてておもうのは
source code -> byte code -> VM -> x86 instruction -> CPU -> μOP
というたくさんのステップを経て実行されているんだよね~。byte codeと機械語の差って大きいねぇ。VM上でμOPにJITできたら幸せかもしれないが、仕様が公開されることは無いだろうしなぁ。

とまあ、まったくまとまりなくだらだらしてみました。

goto in python

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
http://entrian.com/goto/
jokeらしいが。assemblyから直接pyにmapするような場合にはgotoがないと苦しい。

exceptや関数を飛び越えられないという制限はべつに問題ないしねぇ。


from goto import goto, label

print 'hello'
goto.skip
print "won't be seen"
label.skip
print 'bye'

2008年11月7日金曜日

gcc memo

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

-m32

32bit、ええx86_64なマシンだと-m64に相当するので。諸般の事情でpointerは32bitであってほしいときなのです

-march

アーキテクチャの指定。64bitなマシンだと、-m32を使わないとi386とかは指定不能。

-masm=intel

-Sの時に吐く.sのformatをintelにする。


何をしているかって?32bitのintel formatなアセンブリを生成したかったのですよ。

LinuxとSSE

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
これを読んでいてふとおもったのだが、


アイコン 「AVXをどう使えばいいの?」「使うべきじゃない」

 Intelは、2010年にコードネーム「Sandy Bridge」において、x86プラットフォームに「YMMレジスタ」と呼ばれる256bitのレジスタを新設し、それに対するSIMD命令群を追加することを発表しました。これを「Intel AVX(Advanced Vector Extensions)」と呼んでいます。

 Agner Fogは、AVXにおいて「XSAVE」「XRESTOR」という新しいレジスタ退避命令が追加されるようであるが、ドライバ開発者はどうやってこのレジスタを使えばいいの? と尋ねました。それに対してArjan van de Venは、「使うべきじゃないよ。SSE(注)を使うべきじゃないのと同じ理由でね」と答えました。
注:SSE(Streaming SIMD Extensions)とは、Pentium IIIから搭載されているSIMD拡張命令セットのこと。SSE(無印)からSSE4まであるが、ArjanはXMMレジスタ(SSEレジスタ)を使う命令全般を「SSE」とひとくくりにしています。

 Andi Kleenはさらに、「まず、ドライバがカーネルインターフェイスを使わずに勝手にXSAVE、XRESTORを使った場合はカーネルが死んでしまう。決してやってはいけない。カーネルインターフェイスを使うべきだ。カーネル本体のコードは2.6.27で入るよ。でもFPUやSSEなどと同じで、使用されるときだけLazyにレジスタ退避を行う仕様になるので、ほとんどのドライバでは使うと性能が劣化するだろう」と補足しています。

 2010年に登場予定の機能が、パフォーマンス得失まで含めてすでに議論がされていることにちょっと驚いてしまいますね

はあ。。。

いままで適当にgnubgをbuildしていたけど、SSE関係をチェックしてちゃんとbuildされているか検証しよう。また仕事が増えた。あとそれからasmを生でいじるのもありだな。かなりしっかりしたテストsetを用意する必要があるが。

2008年11月5日水曜日

x86でのセマフォ実装?

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

label: 00406BEA
mov ecx, FFFFFFFF
xchg dword[esi+14], ecx
test ecx, ecx
jne 00406BEA

最初意味がわからなかったが、おそらくatomic exchange and testってやつだ。
大学の研究室時代を思い出した。当時はinner loopにしか興味が無くて(行列とかのHPCなのだ)、RISCの命令の中ではずいぶんと遠く(基盤の上バス)まで影響の出る命令があるもんだと印象に残った。

ほかのRISCの命令・・・とはいってもmull/add/loadとかばっかしで単純で、bspがどーのこーのとかはあまり気にすることはなかった。そいつらが綺麗にならんでくれることが大事で、そいつらがstallしないことがすべてだった。xchgでとまるなんてあまり興味が無かった。

時は流れてHT全盛で、遅いメモリアクセスを使ってlockするのが仕方ないのが結構影響ありそうなアプリは世の中山盛りのようだ。

で、Intelのドキュメント(IA32)によると、

XCHG (exchange) 命令は、2 つのオペランドの内容を入れ替える。この命令は、3 つのMOV命令と同じ効果を持つが、一方のオペランドをロードする間に他方のオペランドの内容を保存するための一時的なロケーションを必要としない。XCHG命令でメモリ・オペランドを処理するときは、プロセッサのLOCK 信号が自動的にアサートされる。この命令は、プロセスの同期をとるためにセマフォまたは同様のデータ構造を実装するのに便利である(バス・ロックについての詳細は、『IA-32 インテル®アーキテクチャ・ソフトウェア・デベロッパーズ・マニュアル、下巻』の第7 章の「バス・ロック」を参照)。


ここまでよんで、あああ~~atomic exchangeなのね。了解。マルチスレッドとかじゃないなら、とりあえずは無視だな。

「x86 セマフォ xchg」でgoogle神に神託したらObj-C 最適化:不可分な操作とか転がり出てきた。

日本語でわかりやすそうな記事は4.Atomicなメモリ書き換えとかだな。

MySQLのバグレポートに完全におなじassmelbyが出てくる。

Use GCC-provided atomic lock operations to replace pthread_mutex_lock functions


At least it doesn't hurt. Since we use xchg for both lock and unlock.
Here is the assembly code:

lock:
00000000000005b0 :
5b0: b8 01 00 00 00 mov $0x1,%eax
5b5: 86 47 08 xchg %al,0x8(%rdi)
5b8: 84 c0 test %al,%al
5ba: 75 04 jne 5c0

assemblyのoperandの順番が違うのに注意ね。

関数呼び出し

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
callは関数呼び出しなんだろうが、その前にいろいろpushしている。leaはebxになんか値をロードしている。
このへんとか見る限り、callの前にpushしたものは引数と見ていいだろうが、どこのpushがそうなのやら。
呼び出し側と呼び出され側を突き合わせて推測するのだろうが。

ともあれ呼出規約@Wikipediaを参考に、近そうなやつはどれかあたりをつけよう。
callしたあと、ガシガシjumpしたり次の処理をやっていて、「add esp, 12 ;スタック上の引数を除去」の形跡がないから、Pascal系の規約を想定したほうが賢いだろう。まあ、0個の引数の可能性やfastcallも否定できないが。

x86(IA-32) アーカイブ
とかかなぁ。

caller

frag: 0040217B callers = [00402146;;]
lea ebx, dword[ebp+FFFFFECC]
push ebx
lea ebx, dword[ebp+FFFFFED0]
push ebx
lea ebx, dword[ebp+FFFFFF2C]
push ebx
lea ebx, dword[ebp+FFFFFE68]
push ebx
lea ebx, dword[ebp+FFFFFEFC]
push ebx
lea ebx, dword[ebp+FFFFFF74]
mov dword[ebx], esi
push ebx
lea ebx, dword[ebp+FFFFFCE8]
push ebx
lea ebx, dword[ebp+FFFFFF74]
mov dword[ebx], esi
push ebx
lea ebx, dword[ebp+FFFFFD5C]
push ebx
call 0040133C


callee

proc: 0040133C callers = [00401DD8;00401EE6;004021BE;0040232A;00402AAF;00402BD8;00402D01;;00402E2A;004030B7;004031E0;00403309;0040357B;
004036A4;004038FB;;]
frag: 0040133C callers = []
push ebp
mov ebp, esp
push ebx
push esi
push edi
sub esp, 05C
xor esi, esi
push esi
push esi
push esi
push esi
push esi
push esi
xor eax, eax
mov ebx, dword[ebp+1C]
movsx ecx, word[ebx]
mov ebx, dword[ebp+08]
sub ecx, dword[ebx+18]
shl ecx, 1
add ecx, dword[ebx]
movsx ecx, word[ecx]
cmp ecx, eax
jne 00401377
mov ebx, dword[ebp+28]
mov word[ebx], 0000
jmp 0040150E

2008年11月4日火曜日

だんだん濃くなってきたぞ

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
そろそろ周辺調査から、目標に向かう作業にいったほうがよさそうだ。

アセンブラとPEフォーマットとマシン語の注入ってもう逝っちゃってますな。

「任意のプログラムを他プロセスへ注入する」そんなこと許しちゃだめでしょ> OS
もちろん「誰が注入する」かもありますが。

リバースエンジニアリング
64ビット環境でのリバースエンジニアリング

引数に関しては4つまでレジスタ、以後スタックという流れだが、call命令のリターン値については、スタックに積まれる。

ってこういうことを生成系無しでやるのか~ちょっとつらいかも。
32bitと64bitという違いはあるが、手練の視点がみれたのはいいことだ。


BASICからも機械語を呼び出せる
あああーーー、そうだ。Call by ValueなのかCall by Nameなのかもしらないなぁ、もとの処理系。頭痛い。

調査の続き

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Javaバイトコードをデコンパイルする効果的なアルゴリズム

制御フローをある程度調べる必要があるわけで、「ドミネータツリーを使った制御構造の復元を考えるにあたって、まずは、簡単にわかる情報をあげておきます。」とかのくだりは少しさんこうになるかな?しかし、吹き出しがぐしゃぐしゃに入っていて、ステップごと解説はよむことができない・・・。


書き手の癖や高級言語の性質がマシン語にでるならね。しかし元の言語の処理系が手に入らないと無理だな~。だってマシン語と制御構造の関係がまったく探れないので。x86はLoopとjumpがあるから、それに変換していてくれているならまあ、望みがないわけではないが。

Loopならloopかもしれないが、jumpもloopかもしれないがそうでないかもしれない。
おまけ程度ですな。

デコンパイル、リバースエンジニアリング

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
世の中、いろいろな人がいるものだ。


といっても,復元不可能とわかっている情報 (コメントやローカル変数名など)についてはスッパリあきらめて,復元可能な情報をいかに読みやすい形で表示するか (元通りでなくても読みやすければよい) という目標にすり替えます.

まさにそれなんだけど。今やろうとしている解析の対象のバイナリは、元の言語がCですらない。

解析としては、

  • 関数引数がわかる。

  • ループ変数がわかる。

  • 短絡評価を消すことができる


とかでもかなり大きい。fragmentをつなぎあわせるのが楽になるからだ。

すべてを解析したいわけではない(興味のあるのはアルゴリズムとかファイルフォーマットであるケースがほとんどのはず)ので、絞込みを効率化することも大事。
ジャンルによってはfloatがらみのコードが出てくる関数かどうかを判定するだけでも違う。普通はUIにはfloat演算は出てこないからだ(MacのQuartzは違うだろうが)。
ただ、GUIは普通、APIをCallするので、その周辺を捨てることはできる。逆にFile Formatはfileを読み書きするAPIから切り崩すことになる。

visualizing .asm with graphiviz

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
When you are tired, stop coding. Because you are getting no where.
If your head is clear, you get the clear ans.

def graphbuilder(t):
edge_list = []
for proc in t.findall('procedure'):
for frag in proc.findall('fragment'):
for op in frag.findall('op'):
operator = op.get('operator')
operand = op.get('operand')
if operator.startswith('call'):
if operand.startswith('dword'):
call_target = operand.rsplit(' ')[-1][:-1]
else:
call_target = operand
edge_list.append((proc.get('addr'), call_target))
return pydot.graph_from_edges(edge_list, directed=True)

pydot

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

edge_list = [
('0', '1a'),
('0', '2'),
('2', '1a'),
('2', '2'),
]
g = pydot.graph_from_edges(edge_list, directed=True)
g.write_raw('test.dot')

generates

digraph G {
0 -> "1a";
0 -> 2;
2 -> "1a";
2 -> 2;
}

2008年11月3日月曜日

graphivizで視覚化

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
.asmのreference関係を描画させてみたが、びみょ~。
生成されるグラフの縦横比が1:1000くらいで、全体図だとただの横線になっている。
レイアウトのエンジンをいじるか、捨象するようなことをしないとだめだ。スクロールもままならないし。

git-svn

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
git-cvsが気に入ったし、Gaucheをrpm化したいので、Gaucheをgit-svnして見ることにした。
git-svnがうまく動けば、いま使っているsubversionをやめてgitに代えよう。
そのかわりtracにgitプラグインをいれてgitに対応させる必要があるが。


git-svnとgit-importsvnは違うらしい。前者は双方向、後者はgit行きのみのようだ。gitとsvnを双方向でつなぐメリットは何だろう?

  • revision stampの問題

  • meta 構成管理

  • すでにsubversionを使っているプロジェクトで自分だけgitのlocal repositoryを持つ

アイディアを証明することと実用に耐えるものにすること

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
だいぶ差がある。

2:8の法則を使って説明すると、労力(コード、テスト量)の2割は前者、残りの8割は後者。
で、価値を感じる部分は使い始めるまでは、前者が8割、後者が2割。

「使い始める前」が味噌。いままでできなかったことを可能にすることによる価値は8割だといいたいところだが、微妙。もちろん作り始めるモチベーションはそのバランスじゃないと存在しないのですが。

使い始めて使いにくさを実感すると、実用に耐えるものにすることに価値を感じ始める。実用に耐える状態を維持する、つまりユーザの不快感を取り除く作業です。メンテナンスと呼ばれていますが、エレベーターとかのメンテナンスとかとはちがい、ソフトウェアのメンテナンスは、定期整備というよりむしろ建物のリフォームや改築といったほうがあたっている。

当然、リフォームや改築をするためには元の構造がよくないとだめでしょう。設計が悪いとリフォームや改築自体が不可能で、建て直しになってしまう。ソフトウェアも同じ。

結局、使うのをやめるまでにトータルの8割の労力を投じることになるだろう。よい設計、悪い設計があるとしたら、その労力を減らす点が上げられる。しかし所詮未来予知なので、完全にはできない。そこで使いながら作り変えていく形態が必然になる。

作り変えていく作業を行うと、当然その過程でbugが入ってサービスがとまる危険が常に付きまとう。となると、常にテストし続けることになる。テストのコストが高い、つまり自動化されていないということは、そのような形態を不可能にしてしまう。
No automated test, No evolutionである。

また、メンテナンスを常にし続けるという形態を考えると、頻繁にリフォームされる家というのは現実的には存在しなくて、むしろ生物のホメオスタシーや新陳代謝を想起するほうがよりよいだろう。

当たり前のことだが、人間は、生きている間に皮膚がない状態になったり、骨がない状態になったりはしない。機能停止状態がいない。80年間365日24時間無停止である。生きているとはそういうことである。心臓の筋細胞とて例外ではない。しかし心臓はとまることはない。

あるサイトを支えるプログラムがあるとしたら、とめずに置き換えていくことができることが大事で、生物のあり方を目指すべきだろう。サイトの機能が増えていく過程は、生物が発生して成長していく過程になぞらえてもいいだろう。

細胞は全部入れ替わるし、構成している分子も入れ替わる。機能も変わる(蝶を考えてみよう)。しかし同一の生き物であることにはかわりがない。サイトもそのようにあるべき。ウェブサイトのデザインといった場合、cssで見えがどうのこうのというのはごくごく小さな話で、ユーザにサービスを提供する生物を創造するつもりでデザインすべきだとおもう。
そのレベルから見た場合、アイディアというのは、羽をつけたら空を飛ぶことができそうだみたいな要素です。

飛んでいるトンボをみたらよくできているとおもうが、卵からヤゴになり、脱皮して飛べるようになるプロセスを支え続けるものはなにか?という命題はサイトを通じてサービスを提供するのなら考えざるを得ないのです。

2008年11月1日土曜日

parser for disassm output

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Use it as you like. This is BSD License.
It format output into html, with fragment jump for jump and call instructions.

import pprint
import re
import elementtree.ElementTree as ET

ASSEMBLY_START = '+++++++++++++++++++ ASSEMBLY CODE LISTING +++++++++++++++++++'
PROC_BOUNDARY = '========='
FRAG_BOUNDARY = '---------'
ASM_FORMAT = (r''':(?P[0-9A-Z]{8}) '''
'''(?P([0-9A-Z]{2})+)[ ]+'''
'''(?P[a-z]+)'''
'''( (?P([a-zA-Z0-9,+-<> ]|\[|\])+))?'''
)
asm_line = re.compile(ASM_FORMAT)

CROSS_REFERENCE_START = '*************** Cross Reference Listing ****************'
FRAG_LINE = r'--(?P[0-9A-Z]{8})::((?P[0-9A-Z]{8}),)+'
PROC_LINE = r'==(?P[0-9A-Z]{8})::((?P[0-9A-Z]{8}),)+'
CONT_LINE = r'[ ]+((?P[0-9A-Z]{8}),)+'


class Parser(object):
'''
>>> asm = file('hoge.asm', 'r')
>>> p = Parser()
>>> p.parse(asm)
>>> for n in p.assembly:
... print n, n.attrib['addr']

>>> print p.assembly[14][3], p.assembly[14][3].attrib['addr']
>>> for op in p.assembly[14][3]:
... print op, op.attrib['addr'], op.attrib['raw'], \
op.attrib['operator'],\
op.attrib['operand']
'''
def __init__(self):
self.current_parse = self.parse_something
self.prev_line = ''
self.assembly = ET.Element('program')
self.proc = ET.SubElement(self.assembly, 'procedure')
self.proc.attrib['addr'] = '0x0'
self.frag = ET.SubElement(self.proc, 'fragment')
self.frag.attrib['addr'] = '0x0'

def parse(self, f):
try:
for i, line in enumerate(f):
#pprint.pprint((i+1, line))
self.current_parse(line)
self.prev_line = line
except Exception, e:
print 'something wrong around line', i+1
pprint.pprint((i, self.prev_line))
pprint.pprint((i+1, line))
pprint.pprint(self.assembly)
pprint.pprint(self.proc)
pprint.pprint(self.frag)
raise e
def parse_assembly(self, line):
assert self.assembly is not None
if line.startswith(CROSS_REFERENCE_START):
print 'CROSS_REFERENCE_START'
self.current_parse = self.parse_reference
return
elif line.startswith(PROC_BOUNDARY):
self.proc = ET.SubElement(self.assembly, 'procedure')
assert self.proc is not None
self.frag = ET.SubElement(self.proc, 'fragment')
assert self.frag is not None
return
elif line.startswith(FRAG_BOUNDARY):
assert self.proc is not None
self.frag = ET.SubElement(self.proc, 'fragment')
assert self.frag is not None
return
else:
pass

for matchobj in asm_line.finditer(line):
d = matchobj.groupdict()
if 'line_addr' in d:
match = d['line_addr']
if self.prev_line.startswith(PROC_BOUNDARY):
assert self.proc is not None
self.proc.attrib['addr']= match

if self.prev_line.startswith(PROC_BOUNDARY) or \
self.prev_line.startswith(FRAG_BOUNDARY):
assert self.proc is not None
assert self.frag is not None
self.frag.attrib['addr']= match

op = ET.SubElement(self.frag, 'op')
op.attrib['addr'] = match
if 'raw_binary' in d:
op.attrib['raw'] = d['raw_binary']
if 'operator' in d:
op.attrib['operator'] = d['operator']
if 'operand' in d:
op.attrib['operand'] = d['operand']

def parse_reference(self, line):
pass

def parse_something(self, line):
if line.startswith(ASSEMBLY_START):
print 'ASSEMBLY_START'
self.current_parse = self.parse_assembly

class DocTree(object):
def __init__(self):
self.root = ET.Element('html')
assert self.root is not None
self.head = ET.SubElement(self.root, "head")
title = ET.SubElement(self.head, "title")
title.text = "formatted asm result"
self.body = ET.SubElement(self.root, 'body')
assert self.body is not None
def setcss(self, css):
e = ET.fromstring(css)
print 'using css'
print '-' * 20
print e
print e.text
print '-' * 20
self.head.append(e)

def html(self):
return ET.ElementTree(self.root)

css = '''
'''
class MakeHTMLVisit(object):
def __init__(self):
self.doc = DocTree()
self.doc.setcss(css)
assert self.doc.body is not None
self.current = self.doc.body
assert self.current is not None

def accept(self, e):
assert self.current is not None
parent = self.current
self.current = self.SubElement(parent, e)
for c in e:
self.accept(c)
self.current = parent

def SubElement(self, doc_parent, src_element):
assert doc_parent is not None
assert ET.iselement(doc_parent)
assert src_element is not None
assert ET.iselement(src_element)
e = ET.SubElement(doc_parent, 'div')
try:
self.decoreate(e, src_element)
except Exception, ex:
pprint.pprint(src_element)
raise ex
return e

def decoreate(self, e, src_element):
addr = src_element.get('addr')
if src_element.tag == 'procedure':
e.attrib['class'] = 'procedure'
e.text = 'proc: %s\n'%(addr)
elif src_element.tag == 'fragment':
e.attrib['class'] = 'fragment'
e.text = 'frag: %s\n'%(addr)
elif src_element.tag == 'op':
e.attrib['id'] = addr
e.attrib['class'] = 'op'
operator = src_element.get('operator', None)
operand = src_element.get('operand', None)
if 'j' in operator:
a = ET.SubElement(e, 'a', href='#%s'%(operand))
a.text = '%s %s\n'%(operator, operand)
elif operator.startswith('call'):
if operand.startswith('dword'):
html_frag = operand.rsplit(' ')[-1][:-1]
else:
html_frag = operand
a = ET.SubElement(e, 'a', href='#%s'%(html_frag))
a.text = '%s %s\n'%(operator, operand)
else:
e.text = '%s %s\n'%(operator, operand)
else:
pass

asm = file('hoge.asm', 'r')
p = Parser()
p.parse(asm)
visit = MakeHTMLVisit()
visit.accept(p.assembly)
html = visit.doc.html()
html.write("hoge.html")

もきゅ~。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

:0040110C 8B5D10 mov ebx, dword[ebp+10]
:0040110F 0303 add eax, dword[ebx]
:00401111 8B5D10 mov ebx, dword[ebp+10]
:00401114 8903 mov dword[ebx], eax
---------
:00401116 66FF4580 inc word[ebp-80]

のようなもの(disassemの出力)を食って

<Element procedure at 12aa4170> 00407BD9
<Element procedure at 12aa4680> 00407BEC
<Element fragment at 12614dd0> 00404A20
<Element op at 12614ea8> 00404A20 E806000000 call 00404A2B
<Element op at 12614f38> 00404A25 E87D000000 call 00404AA7
<Element op at 12614fc8> 00404A2A C3 ret None

を吐くものを作った。もう眠いです。
あす以降、このtreeをレンダリングするプログラムを書きます。
まあ、手でwikiに貼り付けるより、効率的だし、楽しいのだが。コードもそのうち公開します。

disassembleの結果でどこが大事かを追跡するのはフローグラフっぽいものを生成できないとつらいので。graphvizで絵でもだすか。ウィルスの解析結果出力みたいだな。

結局、コンピュータウイルスのコード静的解析による特徴抽出と分類とかとやろうとすることかわらないよな~。

2008年10月31日金曜日

同じ

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
diffで同じところだけを出すのはどうしたらいいのだ?
helpを眺めて思いつかなかったので、pythonでdifflibをつかったscriptを書いてしまった。

テクノロジーにまつわる対する態度、文化

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
日本のゲーム産業はもはやトップでない


鍵となるのは「日本がものを作る力、進歩する速度を、世界の技術革新の能力が上回ったことだ」という。そして、その速度を引き上げたのが「英語圏を中心に急成長したゲーム業界のコミュニティー」だと分析する。



その構造は完結して閉じており、すべてを自社で一貫開発する垂直統合だった。2000年代に入って、ミドルウエアなどの分業化を前提とする水平モデルへと世界が移行したときに、技術革新の速度に追いつけなくなった。


垂直統合だと、人材の流動性が低い(スキルの持ち歩きがしにくい。)/ イノベーションが起こりにくい。(会社という枠にとらわれてしまう、PointyHeadを説得しようとする無駄、機動力低下)ということかな。そうなると「業界として、給料が上がらない。あがらないと人が来なくなる。人が来ないから斜陽化に拍車がかかる。」という展開だろう。

OSSがはやるのはほかでもなく、(派遣のようなインチキな流動性ではなく)本質的に人材の流動性があがるからだ。会社とエンジニアの双方にとって好ましい。経済的必然だ。

またソフトウェアはよくスケールし一度書いたものを使いまわしたいので、グラフィックライブラリの仕様ようなものは機能的に優れたものが世界で1つあればいい。世界にゲーム会社が独占1社でない以上、OSSもしくはそれに近い形、ミドルウェアという形でシェアされることは必然だ。

知識が人間の上で動くソフトウェアだとしたら、知識・知性主導である産業には同じ現象が起こるだろう。情報を発信することにおいて、権威(楊枝)よりもコンテンツ(くいもの~~)である。知識やソフトウェアを共有する手段はここ10年で劇的に変わったことを計算に入れよう。

2008年10月27日月曜日

セキュリティ監査本

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
「実践ネットワークセキュリティ監査~リスク評価と危機管理」を一応、読み終えた。
掛け値なしに非常によい本だと思う。

出版は2005年だが、スタンスというか、活動としてのあり方が具体的な作業から見えてくる。いずれにせよこの手の話は日々updateなので。この本をスタート地点に自分なりの監査プロセスを作ってまわし、情報を収集してプロセスを改善し続けないと意味がない。ヤレヤレ。

まあ、個人レベルで運用しているサーバに求められるセキュリティと金銭のやりとりが行われているサーバでは求められるレベルが違いますが。

まずは安易な穴(global側からみてopen portが存在する、fileがupできる、open relay、SQL Injectionが可能)がないことですね。

外からはhttpかssh(22ではない)しか開いていないはずなのでhttpの設定さえただしければ大丈夫なはずだが・・・。vhostでいろいろやっているのでそこが怖いかな。

databaseをつかうwebアプリの投入前には確実にしておきたい。wikiといえどもdatabaseを使うので。databaseを攻略される→権限昇格で終了なので。databaseを攻略できて権限昇格ができる人にはchrootなんて屁みたいなものでしょう。それならこちらが攻撃を検出する能力を上げることに時間を割いたほうが効果的だろう。

所詮、攻撃予算 v.s. 防御予算なのだから。

5万円のものを守るのに50万円の金庫を使う人はいないし、5万円奪うのに50万円つかう馬鹿もいない(そういう観点からは巷の強盗殺人とかはとても理解できない)。

22portに力技攻撃とかならすでにはじいている。もっとも攻撃側のコストも小さい。完全に自動化されているから。凡人が思いつくレベルの自動攻撃は防げていることだね。

攻撃側の予算は、攻撃側が攻撃の結果得る経済利益で決まる。経済利益で動かないであろう、防御を破ることに血筋をあげる古典的ハッカーを防ぐ気はないし、うちのサーバを破ったところで彼の知的好奇心は満たされないだろう。
大体、攻撃予算が5000円もあったらホームセンターに行ってドライバを買ってきて私の住んでいる家の窓を破って物理的にサーバに進入したほうが楽だろう。10万円あったら自分でハード買ったほうが安いし。違法な踏み台がほしい以外はあまりメリットがない。その場合はうちは効率が悪いはずだ。ほかに穴だらけのマシンは一杯ネットにつながっている。

というわけで、結論としては予算5000円未満のアタック from world wideを防げればO.K.
こちらの、のこりの仕事は、いかに低予算で確実にまわすかということ。
定期的なbackupと同時にスキャンスクリプトを実行するのがよいかな。

nstalkとかniktoでスキャンした結果は、バージョン以外大丈夫だったが。バージョンはCentOSをつかっている以上、どうにもならないし、同じ状況のマシンは山盛りあるはずなので。

スキャン残したlogの抜粋。なるほどなぁ~~。

- - "GET /index.cgi?action=topics&viewcat=../../../../../../../../../../../../.
./etc/passwd HTTP/1.1" 302 125 "" "Mozilla/4.0 (compatible)"
- - "GET /WebAPP/index.cgi?action=topics&viewcat=../../../../../../../../../../
../../../etc/passwd HTTP/1.1" 302 125 "" "Mozilla/4.0 (compatible)"
- - "GET /webapp/index.cgi?action=topics&viewcat=../../../../../../../../../../
../../../etc/passwd HTTP/1.1" 302 125 "" "Mozilla/4.0 (compatible)"
- - "GET /breakcal/calendar.cgi HTTP/1.1" 302 125 "" "Mozilla/4.0 (co

tarfile

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
pythonはtarをとりあつかうtarfileモジュールがあるので、先のGitPyhonと組み合わせれば、やりたいことの8割方をカバーできそうだ。

GitPython

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
GitPythonをとってきてコードを観察中。よい意味で普通なpythonコード。SQLObjectみたいに変態じゃない。

ざらっと見た感じではcommand lineで打つコマンドや引数をpython objectにしたかんじ。
tagやHEADオブジェクトがある模様。commitへのsha1アクセスはdictかな?
validなpropertyになれないだろうから。

git.HEADとかできるらしい。

class TestHead(object):
def setup(self):
self.repo = Repo(GIT_REPO)

@patch(Git, '_call_process')
def test_repr(self, git):
git.return_value = fixture('for_each_ref')

head = self.repo.heads[0]

assert_equal('' % head.name, repr(head))

assert_true(git.called)
assert_equal(git.call_args, (('for_each_ref', 'refs/heads'), {'sort': 'committerdate', 'format': '%(refname)%(objectname)'}))


今回用がありそうなのはこの辺かな?

def test_archive_tar(self):
self.repo.archive_tar

def test_archive_tar_gz(self):
self.repo.archive_tar_gz


class Repoには、archive_tarメソッドとarchive_tar_gzメソッドがある。

def archive_tar(self, treeish = 'master', prefix = None):
"""
Archive the given treeish

``treeish``
is the treeish name/id (default 'master')

``prefix``
is the optional prefix

Examples::

>>> repo.archive_tar


>>> repo.archive_tar('a87ff14')


>>> repo.archive_tar('master', 'myproject/')


Returns
str (containing tar archive)
"""
options = {}
if prefix:
options['prefix'] = prefix
return self.git.archive(treeish, **options)


class Gitにこんなメソッドがある。

def __getattr__(self, name):
if name[:1] == '_':
raise AttributeError(name)
return lambda *args, **kwargs: self._call_process(name, *args, **kwargs)

これは参考になりますね。__getattr__でlambdaをつかった無名関数を返す。
関数が第一級objectであることとは何ぞや?ということですな。

そして_call_process

_kwargs = {}
for kwarg in execute_kwargs:
try:
_kwargs[kwarg] = kwargs.pop(kwarg)
except KeyError:
pass

# Prepare the argument list
opt_args = self.transform_kwargs(**kwargs)
ext_args = map(str, args)
args = opt_args + ext_args

call = ["git", dashify(method)]
call.extend(args)

return self.execute(call, **_kwargs)

ん~~エレガント。

2008年10月26日日曜日

gitのある生活

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
運用上、rpmでpackageしてすべてyum化するので、gitで管理するブツもyum化しないといけない。一人で開発運用をやっていると、妙な環境を作って失敗して原因追及に時間を割きたくないのだ。となるとcleanにpackageを作る必要性に駆られる。


#!/bin/sh
git-archive --format=tar --prefix=gnubg-backgammonbase-edition-0.2/ -v HEAD | gzip > gnubg-backgammonbase-edition-0.2.tar.gz
rpmbuild -ta gnubg-backgammonbase-edition-0.2.tar.gz &> log

tar ballを作る機能はgitにあり(git-archive)、それをrpmbuildに渡しさえすればいい。


releaseのtagを打ったらそれ自動的に反映したversion stringを持ってほしいのだが。pythonのsetup.py bdist_rpmはどうやっているのだろう?とくに、subersionのrevisionを拾ってくる機能はありがたい。

gitではどうしたらよいのだろう。思いつくアイディアをつれづれなるままに書いてみる。

  1. version postfixを用意してあげる。

    • gitがもつsha1をつかう→×。どれだか特定はできるが、順番がわからない。

    • 日時を使う→×。古いものをcheckoutしてあとからpackageするかもしれない。

    • incrementalなtagを用意してをつかう。→?
      どうやってtagの生成を自動化するのか?



  2. version postfixをつける。

    • dirやtar ballのfile名:前出のscriptに変数を導入すればOK

    • spec file。こまった。spec fileを書き換える必要が出てくる。version 番号が埋め込まれているので。ひとつ方法としては、Makefile.amよろしく、動的に生成し、何らかの方法によってtar ballに挿入するか、rpmbuildにspec fileを別途渡すかするというもの。





とまあ、運用を視野に入れて物を作るのは面倒なのですよ。特にすべてyum一発インストールを維持しようと思うと。
もちろん、yumでインストールできるようにしてもpackageが正しくできているか検証する必要があるわけで、最近は仮想環境(VM fusion)をつかって検証しています。こいつはメンテ時の代用サーバもかねています。

あ~~あ。調べ物が多いなぁ。google先生があるだけマシなのだが。

2008年10月25日土曜日

gitのrepository, gnubg-mirror

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
http://git.tonic-water.com/
gnubgのCVSをimportしたrepositoryしかないですが。gnubgの開発チームが悟ってくれるとうれしいです。

git以外でmerge作業をやるには人生短すぎます。