2010年12月15日水曜日

実行の中断・再開

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
KeyboardInterruptされたら状態をファイルに退避して、再実行時に復帰する。
#!/usr/bin/python

import sys
import os
import time

import pickle

fname = 'serialized'

try:
f = open(fname, 'r')
counter = pickle.load(f)
f.close()
except:
counter = 0
pass


try:
while counter < 10:
    print counter
    counter += 1
    time.sleep(1)
except KeyboardInterrupt:
  print 'Got KeyboardInterrupt'
  f = open(fname, 'w')
  pickle.dump(counter, f)
  f.close()
  sys.exit()
try:
  os.remove(fname)
except:
  pass
実行結果:
[nori@nagato]~/Desktop/backgammon/bgonline% python int.py
0
1
Got KeyboardInterrupt
[nori@nagato]~/Desktop/backgammon/bgonline% python int.py
2
3
4
5
6
7
8
9
[nori@nagato]~/Desktop/backgammon/bgonline% python int.py
0
1
2
3
4
5
6
7
8
9
[nori@nagato]~/Desktop/backgammon/bgonline%

2010年6月5日土曜日

CPUCooler

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


Push Pin 方式爆発しろ。80%の領域で接触してないし。CPUが焼けていたかもしれない。

2010年5月21日金曜日

障害

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Giga得で動くようになったらこんどfletzが動かなくなった。
[root@housyou httpd]# /sbin/route 
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.2.0     *               255.255.255.0   U     0      0        0 eth1
192.168.2.0     *               255.255.255.0   U     0      0        0 eth0
default         192.168.2.253   0.0.0.0         UG    0      0        0 eth0
default         192.168.2.254   0.0.0.0         UG    0      0        0 eth1
[root@housyou httpd]# /sbin/ifconfig 
eth0      Link encap:Ethernet  HWaddr 00:19:DB:62:B9:9F  
          inet6 addr: fe80::219:dbff:fe62:b99f/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:3904027 errors:0 dropped:0 overruns:0 frame:0
          TX packets:238768 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:376937452 (359.4 MiB)  TX bytes:23816244 (22.7 MiB)
          Interrupt:21 Base address:0xa800 

eth0:0    Link encap:Ethernet  HWaddr 00:19:DB:62:B9:9F  
          inet addr:192.168.2.68  Bcast:192.168.2.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Interrupt:21 Base address:0xa800 

eth1      Link encap:Ethernet  HWaddr 00:90:CC:EF:8C:3F  
          inet6 addr: fe80::290:ccff:feef:8c3f/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:35328031 errors:0 dropped:0 overruns:0 frame:0
          TX packets:29598053 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:30421424249 (28.3 GiB)  TX bytes:12771102790 (11.8 GiB)
          Interrupt:17 Base address:0xec00 

eth1:0    Link encap:Ethernet  HWaddr 00:90:CC:EF:8C:3F  
          inet addr:192.168.2.64  Bcast:192.168.2.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Interrupt:17 Base address:0xec00 

eth1:1    Link encap:Ethernet  HWaddr 00:90:CC:EF:8C:3F  
          inet addr:192.168.2.65  Bcast:192.168.2.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Interrupt:17 Base address:0xec00 

eth1:2    Link encap:Ethernet  HWaddr 00:90:CC:EF:8C:3F  
          inet addr:192.168.2.66  Bcast:192.168.2.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Interrupt:17 Base address:0xec00 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:24306168 errors:0 dropped:0 overruns:0 frame:0
          TX packets:24306168 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:4125876982 (3.8 GiB)  TX bytes:4125876982 (3.8 GiB)

htttpd.conf
Listen 192.168.2.64:80
Listen 192.168.2.65:80
Listen 192.168.2.68:80
NameVirtualHost 192.168.2.64:80
NameVirtualHost 192.168.2.65:80
NameVirtualHost 192.168.2.68:80

< VirtualHost 192.168.2.64:80 192.168.2.65:80 192.168.2.68:80 >
    ServerName www.tonic-water.com

2010年4月29日木曜日

亀記事:「Webを支える技術」

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

ジュンク堂での出版記念なんたらにて、@t_wadaさんが書いたマインドマップ。

2010年4月15日木曜日

auひかり

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Localは100Mbps
Yahoo! BB ADSL Speed Cheker
 set one
  Dn 31,384Kbps
  Up 72,557Kbps
 set two
  Dn 45,618Kbps
  Up 72,558Kbps
 set three
  Dn 50.296Kbps
  Up 72,558Kbps

Usen 
 44.634 Mbps
 81.958 Mbps
 67.745 Mbps
 48.848 Mbps

goo
 33.71Mbps
 33.71Mbps
 38.84Mbps
 39.57Mbps

speedtest.jp (単位は?byte? bit?)
 7.18M
 4.336M
 7.152M
 6.258M

Down Loadはともかく、uploadはこちらの機材の上限に近いだろう。
固定IPがとれるならまた考えるべし。

2010年4月5日月曜日

Quad Displayへの道

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
nvidia-settings(1) を使えばよかった。かなり遠回りした。

usr/bin/nvidia-xconfig -a --multigpu=Auto --randr-rotation --separate-x-screens --xinerama 
とかで上書き。

単一のカードからでた出力は1つのモニタを作る。かなり意外だった。

# nvidia-xconfig: X configuration file generated by nvidia-xconfig
# nvidia-xconfig:  version 1.0  (buildmeister@builder58)  Fri Mar 12 02:12:40 PST 2010

# Xorg configuration created by pyxf86config
# re-written by hand
# references:
#   https://calomel.org/mulidisplay_xorg.html
#   http://ubuntu-ky.ubuntuforums.org/showthread.php?t=1354736

Section "ServerLayout"

    #Screen       "Screen2" Leftof "Screen A" #Right G2220HD, upcomming.
    #Screen         "ExtraScreen" 3840 0 # Rightof "Screen A" #Princeton
    Identifier     "Default Layout"
    Screen      0  "Screen A"
    Screen      1  "Screen B" RightOf "Screen A"
    Screen      2  "Screen C" RightOf "Screen B"
    # Screen      3  "Screen D" RightOf "Screen C"
    InputDevice    "Mouse0" "CorePointer"
    InputDevice    "Keyboard0" "CoreKeyboard"
    Option         "Xinerama" "1"
    Option         "Clone" "off"
EndSection

Section "Files"
    RgbPath         "/usr/share/X11/rgb"
    FontPath        "unix/:7100"
EndSection

Section "Module"

    # http://us.download.nvidia.com/XFree86/Linux-x86/1.0-9755/README/chapter-03-section-02.html
    # It is a submodule used by other video drivers. A fbdevhw module is currently 
    # available for linux framebuffer devices. 
    # video for linux
    #"Load "GLCore"
    #"Load "dri"  # won't boot.
    Load           "nvidia"
    # proprietary nvidia driver.
    Load           "dbe"
    # Double Buffering
    Load           "extmod"
    # misc extension
    Load           "fbdevhw"
    # provides functions for talking to a framebuffer device. It is os-specific. 
    Load           "glx"
    # OpenGL for X window
    Load           "record"
    Load           "freetype"
    Load           "type1"
    # Load  "v4l"
    Load           "ddc"
    Load           "int10"
EndSection

Section "ServerFlags"
    Option         "Xinerama" "on"
    Option         "Clone" "off"
    # Option         "AutoAddDevices" "off"
    Option         "AutoAddDevices" "on"
EndSection

Section "InputDevice"

    # generated from default
    Identifier     "Mouse0"
    Driver         "mouse"
    Option         "Protocol" "auto"
    Option         "Device" "/dev/input/mice"
    Option         "Emulate3Buttons" "no"
    Option         "ZAxisMapping" "4 5"
EndSection

Section "InputDevice"
    Identifier     "Keyboard0"
    Driver         "kbd"
    Option         "XkbModel" "pc101"
    Option         "XkbLayout" "us"
EndSection

Section "Monitor"
    Identifier     "MonitorAB"
    VendorName     "Unknown"
    ModelName      "Unknown"
    HorizSync       28.0 - 33.0
    VertRefresh     43.0 - 72.0
    Option         "DPMS"
EndSection

Section "Monitor"
    Identifier     "MonitorCD"
    VendorName     "Unknown"
    ModelName      "Unknown"
    HorizSync       28.0 - 33.0
    VertRefresh     43.0 - 72.0
    Option         "DPMS"
EndSection

Section "Device"
    Identifier     "Device A"
    Driver         "nvidia"
    VendorName     "NVIDIA Corporation"
    BoardName      "GeForce 7600 GS"
    BusID          "PCI:1:0:0"
    Screen          0
EndSection

Section "Device"
    Identifier     "Device B"
    Driver         "nvidia"
    VendorName     "NVIDIA Corporation"
    BoardName      "GeForce 7600 GS"
    BusID          "PCI:1:0:0"
    Screen          1
EndSection


Section "Device"
    Identifier     "Device C"
    Driver         "nvidia"
    VendorName     "NVIDIA Corporation"
    BoardName      "GeForce 7600 GS"
    BusID          "PCI:2:0:0"
    Screen          0
EndSection

#Section "Device"
#    Identifier     "Device D"
#    Driver         "nvidia"
#    VendorName     "NVIDIA Corporation"
#    BoardName      "GeForce 7600 GS"
#    BusID          "PCI:2:0:0"
#    Screen          1
#EndSection

Section "Screen"
    Identifier     "Screen A"
    Device         "Device A"
    Monitor        "MonitorAB"
    DefaultDepth    24
    Option         "RandRRotation" "True"
    Option         "MultiGPU" "off"
    SubSection     "Display"
        Depth       24
    EndSubSection
EndSection

Section "Screen"
    Identifier     "Screen B"
    Device         "Device B"
    Monitor        "MonitorAB"
    DefaultDepth    24
    Option         "RandRRotation" "True"
    Option         "MultiGPU" "off"
    SubSection     "Display"
        Depth       24
    EndSubSection
EndSection

Section "Screen"
    Identifier     "Screen C"
    Device         "Device C"
    Monitor        "MonitorCD"
    DefaultDepth    24
    Option         "RandRRotation" "True"
    Option         "MultiGPU" "off"
    SubSection     "Display"
        Depth       24
    EndSubSection
EndSection

#Section "Screen"
#    Identifier     "Screen D"
#    Device         "Device D"
#    Monitor        "MonitorCD"
#    DefaultDepth    24
#    Option         "RandRRotation" "True"
#    Option         "MultiGPU" "off"
#    SubSection     "Display"
#        Depth       24
#    EndSubSection
#EndSection

Section "Extensions"
    Option         "Composite" "Disable"
EndSection

2010年3月27日土曜日

おみくじ

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

Q:10種類のおみくじ(それぞれ無限大数入っている)で、10種類、全てを引くのに要する回数の期待値は?

 ガチャガチャでも、野球カードでも何でもいいです。10種類のcharacterがあって、全て揃うには、いくら注ぎ込まなければならないか?ということです。
 そして、N種類の場合は?

import sys
import random

def isCompleted(xs):
  b = True
  for x in xs:
    b = b and x
    if not b:
      return False
  return True

def experiment(N):
  count = 0
  xs = [False for x in range(N)]
  while not isCompleted(xs):
    xs[random.randint(0, N - 1)] = True
    count += 1
  return count

assert not isCompleted([False])
assert isCompleted([True, True])
assert not isCompleted([True, False])

N = 10
print experiment(N)

sum = 0.0
trial = int(sys.argv[1])
for i in range (trial):
  sum += experiment(N)
print sum / trial
実行結果
[nori@shinano]~/Desktop/study/python% python lotto.py 10000
46
29.1639
よさげ。ただ収束が非常に遅い。

2010年3月24日水曜日

封筒の話(さらに続き)

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

ここで、封筒に入れる金額は、ホストは以下のように決定します。ホストは、さいころを5か6が出るまで連続して振ります。この時、1〜4の目が出た数をn とします。

「この時、1〜4の目が出た回数をn とします」と読まないといけないのね。
しかしでかい方をあけた確率が0.6になってる。変すぎ。
[nori@shinano]~/Desktop/study/python/hack% python two2envelopes.py 100000
[1, 16685, 16685, 33370, 0] 2.0 0.0
[2, 27613, 55226, 60634, 16606] 1.09792489045 0.601383406367
[4, 18749, 74996, 82270, 11287] 1.09699183956 0.60200544029
[8, 12305, 98440, 107588, 7441] 1.09292970337 0.604713531085
[16, 8234, 131744, 144736, 4948] 1.09861549672 0.600923002186
[32, 5508, 176256, 194928, 3283] 1.10593681917 0.596042120552
[64, 3664, 234496, 263456, 2141] 1.1234989083 0.584334061135
[128, 2399, 307072, 337280, 1442] 1.09837432263 0.60108378491
[256, 1624, 415744, 453248, 985] 1.09020935961 0.606527093596
[512, 1074, 549888, 599808, 651] 1.09078212291 0.606145251397
[1024, 729, 746496, 821760, 437] 1.10082304527 0.599451303155
[2048, 469, 960512, 1048576, 284] 1.09168443497 0.605543710021
[4096, 296, 1212416, 1374208, 171] 1.13344594595 0.577702702703
[8192, 197, 1613824, 1667072, 127] 1.03299492386 0.644670050761
[16384, 162, 2654208, 3072000, 91] 1.15740740741 0.561728395062
[32768, 96, 3145728, 3342336, 60] 1.0625 0.625
[65536, 79, 5177344, 6324224, 41] 1.22151898734 0.518987341772
[131072, 39, 5111808, 5111808, 26] 1.0 0.666666666667
[262144, 27, 7077888, 7864320, 16] 1.11111111111 0.592592592593
[524288, 21, 11010048, 11796480, 13] 1.07142857143 0.619047619048
['sum', 99970, 40770819, 44700102]
修正後のコード
import sys
import random

class Die:
  def roll(self):
    r = random.randint(1, 6)
    assert r > 0
    assert r < 7
    return r
  def odd(self):
    return bool(random.randint(0, 1))

die = Die()

class Envelope:
  def __init__(self):
    r = die.roll()
    n = 0
    while r < 5:
      n += 1
      r = die.roll()
    self.values = (1 << n, 1 << (n + 1))
    self.first_opend = die.odd()

  def open(self):
    return self.values[self.first_opend]

  def swap(self):
    return self.values[not self.first_opend]
    

def experiment(t):
  result = {}
  for i in range(t):
    e = Envelope()
    v = e.open()
    s = e.swap()
    r = result.get(v, None)
    if r is None:
      r = [0, 0, 0, 0] #occurence, none-swap case, swap case, respectivly
    r[0] += 1
    r[1] += v
    r[2] += s
    r[3] += int(e.first_opend)
    result[v] = r
  return result

r = experiment(int(sys.argv[1]))

xs = [[i, r[i][0], r[i][1], r[i][2], r[i][3]] for i in r]
xs.sort(key=lambda x: x[0])

total = ['sum', 0, 0, 0]
for i in range(20):
  r=xs[i]
  total[1] += r[1]
  total[2] += r[2]
  total[3] += r[3]
  print r, 1.0*r[3]/r[2], 1.0*r[4]/r[1]
print total

封筒の話(続き)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
r[3]/r[2]を計算するようにしてみた。あとそれからn=20までを対象にした。それ以上は外れ値として無視(頻度が低いため、十分な試行数がないから)
しかし2の時、交換すると減るという変な結果が出てるな。

[nori@shinano]~/Desktop/study/python/hack% python two2envelopes.py 100000
[1, 16715, 16715, 33430, 0] 2.0 0.0
[2, 19473, 38946, 27669, 16741] 0.710445231859 0.85970317876
[4, 5991, 23964, 31428, 2750] 1.3114672008 0.459021866133
[8, 7006, 56048, 73312, 3232] 1.30802169569 0.46131886954
[16, 8180, 130880, 172504, 3719] 1.31803178484 0.454645476773
[32, 6796, 217472, 222064, 4435] 1.02111536198 0.652589758682
[64, 4601, 294464, 364864, 2334] 1.2390784612 0.507281025864
[128, 4500, 576000, 697152, 2369] 1.21033333333 0.526444444444
[256, 4016, 1028096, 1234432, 2140] 1.20069721116 0.532868525896
[512, 3305, 1692160, 1935104, 1887] 1.14357034796 0.570953101362
[1024, 2764, 2830336, 3424256, 1456] 1.20984081042 0.526772793054
[2048, 2332, 4775936, 5721088, 1247] 1.19789879931 0.534734133791
[4096, 2095, 8581120, 10489856, 1086] 1.22243436754 0.518377088305
[8192, 1740, 14254080, 16429056, 983] 1.1525862069 0.564942528736
[16384, 1504, 24641536, 30138368, 779] 1.22307180851 0.51795212766
[32768, 1294, 42401792, 51724288, 673] 1.21986089645 0.520092735703
[65536, 1133, 74252288, 88834048, 607] 1.19638128861 0.53574580759
[131072, 984, 128974848, 148832256, 555] 1.15396341463 0.564024390244
[262144, 763, 200015872, 238026752, 412] 1.19003931848 0.53997378768
[524288, 682, 357564416, 438304768, 352] 1.22580645161 0.516129032258

どういう訳だか、2が出たときに、大きな数値が入っている封筒を高確率で先に開けてしまう。
import sys
import random

class Die:
  def roll(self):
    r = random.randint(1, 6)
    assert r > 0
    assert r < 7
    return r
  def odd(self):
    return bool(random.randint(0, 1))

die = Die()

class Envelope:
  def __init__(self):
    r = die.roll()
    n = 0
    while r < 5:
      n += r
      r = die.roll()
    self.values = (1 << n, 1 << (n + 1))

  def open(self):
    self.first_opend = die.odd()
    return self.values[self.first_opend]

  def swap(self):
    return self.values[not self.first_opend]
    

def experiment(t):
  result = {}
  for i in range(t):
    e = Envelope()
    v = e.open()
    s = e.swap()
    r = result.get(v, None)
    if r is None:
      r = [0, 0, 0, 0] #occurence, none-swap case, swap case, respectivly
    r[0] += 1
    r[1] += v
    r[2] += s
    if e.first_opend:
      r[3] += 1
    result[v] = r
  return result

r = experiment(int(sys.argv[1]))

xs = [[i, r[i][0], r[i][1], r[i][2], r[i][3]] for i in r]
xs.sort(key=lambda x: x[0])

total = ['sum', 0, 0, 0]
for i in range(20):
  r=xs[i]
  total[1] += r[1]
  total[2] += r[2]
  total[3] += r[3]
  print r, 1.0*r[3]/r[2], 1.0*r[4]/r[1]
print total

2つの封筒問題 - バリエーションB をシミュレートする

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
2つの封筒問題 - バリエーションB をシミュレートするプログラムを書いてみた。なーんか違う気がするんですけどね。

import sys
import random

class Die:
  def roll(self):
    return random.randint(1, 6)

die = Die()

class Envelope:
  def __init__(self):
    r = die.roll()
    n = 0
    while r < 5:
      n += r
      r = die.roll()
    self.values = (1 << n, 1 << (n + 1))

  def open(self):
    self.first_opend = die.roll() % 2
    return self.values[self.first_opend]

  def swap(self):
    return self.values[not self.first_opend]
    

def experiment(t):
  result = {}
  for i in range(t):
    e = Envelope()
    v = e.open()
    s = e.swap()
    r = result.get(v, None)
    if r is None:
      r = [0, 0, 0] #occurence, none-swap case, swap case, respectivly
    r[0] += 1
    r[1] += v
    r[2] += s
    result[v] = r
  return result

r = experiment(int(sys.argv[1]))

xs = [[i, r[i][0], r[i][1], r[i][2]] for i in r]
xs.sort(key=lambda x: x[0])

total = ['sum', 0, 0, 0]
for r in xs:
  total[1] += r[1]
  total[2] += r[2]
  total[3] += r[3]
  print r
print total
実行結果
[nori@shinano]~/Desktop/study/python/hack% python two2envelopes.py 1000  
[1, 193, 193, 386]
[2, 185, 370, 269]
[4, 56, 224, 304]
[8, 52, 416, 496]
[16, 84, 1344, 1824]
[32, 72, 2304, 2304]
[64, 50, 3200, 4192]
[128, 59, 7552, 8000]
[256, 37, 9472, 12416]
[512, 21, 10752, 10752]
[1024, 31, 31744, 45056]
[2048, 21, 43008, 46080]
[4096, 13, 53248, 51200]
[8192, 26, 212992, 241664]
[16384, 16, 262144, 303104]
[32768, 11, 360448, 573440]
[65536, 9, 589824, 589824]
[131072, 13, 1703936, 2228224]
[262144, 8, 2097152, 1835008]
[524288, 4, 2097152, 2621440]
[1048576, 2, 2097152, 4194304]
[2097152, 5, 10485760, 14680064]
[4194304, 2, 8388608, 10485760]
[8388608, 6, 50331648, 62914560]
[16777216, 4, 67108864, 83886080]
[33554432, 2, 67108864, 134217728]
[67108864, 4, 268435456, 234881024]
[134217728, 2, 268435456, 536870912]
[268435456, 1, 268435456, 134217728]
[1073741824, 2, 2147483648, 1073741824]
[2147483648, 1, 2147483648, 4294967296]
[4294967296, 2, 8589934592, 10737418240]
[17179869184, 1, 17179869184, 34359738368]
[68719476736, 1, 68719476736, 34359738368]
[1099511627776, 1, 1099511627776, 549755813888]
[281474976710656, 2, 562949953421312, 703687441776640]
[2251799813685248, 1, 2251799813685248, 1125899906842624]
['sum', 1000, 2815949081296883, 1830223154961391]

2010年3月21日日曜日

twitter searchからデータを拾ってほげほげする話(2)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
http://viratter.jp/なるものを発見し、やる気喪失。
のこりの残骸を放置する。ahooonがアカウント削除したおかげで、あとからデータとるやり方では
どうしても追跡できない部分ができてしまう。viratterは定期的にデータをとっているので
そのようなことは起こらない。しかしどんだけのtweetをDLして持っているのだろう?

どうguessして埋めるかを考えるモノまた楽しみではあるが。

#!/usr/bin/python
# coding:utf8

import pickle
import re

#def make_strucure(

f = open('data.pkl')
try:
  all = pickle.load(f)
finally:
  f.close()


peopleTweet = {}
users = {}

for tweet in all.values():
  user_id = tweet['from_user_id']
  tweeted = peopleTweet.get(user_id, None)
  if not tweeted:
    tweeted = []
  tweeted.append(tweet['id'])
  peopleTweet[user_id] = tweeted

  user_name = tweet['from_user']
  possible = users.get(user_name, None)
  if not possible:
    possible = []
  if user_id not in possible:
    possible.append(user_id)
  users[user_name] = possible


USERNAME_PATTERN = r'(?P[_A-Za-z0-9]+)'
RETWEET_PATTERN = r'(RT )?@' + USERNAME_PATTERN + r':'

RETWEET_RE = re.compile(RETWEET_PATTERN)

def GuessParent(tweet):
  text = tweet['text']
  m = RETWEET_RE.match(text)
  if not m:
    return None
  n = (m.groupdict()['username']).encode('ascii')
  from_parent = text[len(n):]
  if n not in users:
    #this means that n is a special user ! 
    users[n] = [n,]
  candidates = users[n]
  if not len(candidates) == 1:
    print n, candidates
  c = candidates[0]

  try:
    ts = peopleTweet[c]
  except:
    return c #ugh!
    
  for t in ts:
    t = all[t]
    if t['text'].startswith(from_parent):
      return t['id']
  return None

RT = {}
for tweet in all.values():
  parent = GuessParent(tweet)
  if not parent:
    continue
  children = RT.get(parent, None)
  if not children:
    children = []
  children.append(tweet['id'])
  RT[parent] = children

print RT

twitter searchからデータを拾ってほげほげする話(1)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
#!/usr/bin/python
# -*- coding: utf-8 -*-


import sys
import urllib
import simplejson

def printer(d, indent=''):
  if isinstance(d, dict):
    for k in d:
      print indent+k,
      printer(d[k], indent+' ')
  elif isinstance(d, list):
    print indent + 'item in list length =', len(d)
    for i in d:
      print indent+'-'*10
      printer(i, indent+ ' ')
  else:
    print (d, )
    #print indent, str(d).encode('utf-8')

def twittersearch(word, since_id=None, page=None):
  baseurl = "http://search.twitter.com/search.json"
  #param = urllib.urlencode({'q': u'堺市 中学'})
  if page is None:
    page = 1
  param = '?q=%E5%A0%BA%E5%B8%82%20%E4%B8%AD%E5%AD%A6' + '&per_page=20&page=%i'%(page,)
  #param = '?q=%E5%A0%BA%E5%B8%82%20%E4%B8%AD%E5%AD%A6' + '&since_id=%s'%(since_id,) #ugh!


  print >> sys.stderr, 'retreiving:', param
  h = urllib.urlopen(baseurl + param)
  return simplejson.load(h)


all = {}

since_id = None
page = 1
try:
  while True:
    result = twittersearch(None, since_id, page)
    if 'results' not in result or not len(result['results']):
      break
    #since_id = result['max_id']
    for item in result['results']:
      all[item['id']] = item
      if not since_id or int(item['id']) < int(since_id):
        since_id = item['id']
    printer(result)
    page += 1
finally:
  print len(all)
  print 'done'

since_idが使われていなかったり、()で囲ってprintしたりしているのはまあ、そういうモンだ

2010年2月21日日曜日

自作commandクラスをロードする step(2)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
setup.pyから相対パスでcommandクラスを実装したファイルをロードできるようにします。
def get_command_class (self, command):
        """Return the class that implements the Distutils command named by
        'command'.  First we check the 'cmdclass' dictionary; if the
        command is mentioned there, we fetch the class object from the
        dictionary and return it.  Otherwise we load the command module
        ("distutils.command." + command) and fetch the command class from
        the module.  The loaded class is also stored in 'cmdclass'
        to speed future calls to 'get_command_class()'.

        Raises DistutilsModuleError if the expected module could not be
        found, or if that module does not define the expected class.
        """
        klass = self.cmdclass.get(command)
        if klass:
            return klass

        for pkgname in self.get_command_packages():
            module_name = "%s.%s" % (pkgname, command)
            klass_name = command

            try:
                __import__ (module_name)
                module = sys.modules[module_name]
            except ImportError:
                continue

            try:
                klass = getattr(module, klass_name)
            except AttributeError:
                raise DistutilsModuleError, \
                      "invalid command '%s' (no class '%s' in module '%s')" \
                      % (command, klass_name, module_name)

            self.cmdclass[command] = klass
            return klass

        raise DistutilsModuleError("invalid command '%s'" % command)
cmdclassは辞書で、setupの引数で渡すことができます。
from distutils.cmd import Command
class build_js(Command):
  def initialize_options(self):
    pass
  def  finalize_options(self):
    pass
  def run(self):
    print 'build_js is here!'

setup(
  ...
  cmdclass={'build_js':build_js},
  ...
)
前のエントリに加えてこのようにしてやると、次の実行結果を得られます。
[nori@shinano]~/Desktop/study/JavaScript/closure-proj% python setup.py build
running build
build:has_js
Distribution:has_js
running build_js
build_js is here!

build_js.pyとしてまとめるとこのようになる。
#!/usr/bin/env python
# -*- coding: us-ascii -*-
# vim: syntax=python
#
# Copyright 2010 Noriyuki Hosaka bgnori@gmail.com
#


import calcdeps 


def jscompile(inputs, search_paths, output, compiler_jar, compiler_flags):
  import logging
  import os
  import os.path
  cwd = os.getcwd()
  output = os.path.join(cwd, output)
  inputs = [os.path.join(cwd, f) for f in inputs]

  logging.basicConfig(format='calcdeps.py: %(message)s', level=logging.INFO)
  logging.info('Scanning files...')
  logging.info('cwd: %s'%(os.getcwd(),))
  logging.info('search_paths: %s'%(search_paths,))
  search_paths = calcdeps.ExpandDirectories(search_paths)
  #logging.info('expanded paths: %s'%(search_paths,))
  out = open(output, 'w')
  logging.info('Finding Closure dependencies...')
  deps = calcdeps.CalculateDependencies(search_paths, inputs)

  calcdeps.Compile(compiler_jar, deps, out, compiler_flags)
  return output

the_args = {}

from distutils.cmd import Command
class build_js(Command):
  def initialize_options(self):
    pass
  def  finalize_options(self):
    print dir(self)
  def run(self):
    print the_args
    jscompile(**the_args)
def CompiledJS(**attr):
  the_args.update(attr)
  return [True] #place holder

def blackmagic(Distribution, build):
  def xxx(self):
    print 'Distribution:has_js'
    return self.js and len(self.js) > 0
  Distribution.has_js = xxx

  def yyy(self):
    print 'build:has_js'
    return self.distribution.has_js() 
  build.has_js = yyy
  build.sub_commands.append( ('build_js', build.has_js),)

  orig = Distribution.__init__
  def myinit(self, attrs):
    self.js = None
    orig(self, attrs)
  Distribution.__init__ = myinit

setup.pyでこのように呼んであげる。
from build_js import build_js
from build_js import blackmagic
from build_js import CompiledJS

from distutils.dist import Distribution
from distutils.command.build import build

blackmagic(Distribution, build)
setup()の引数。
js=CompiledJS(
    inputs=['src/notepad.js'],
    search_paths=['/home/nori/lib/closure/'],
    output='static/notepad.c.js',
    compiler_jar='/home/nori/bin/closure/compiler.jar',
    compiler_flags=['ADVANCED_OPTIMIZATION'],
  ),
bdist_rpmするとコンパイルして生成したjsファイルがパッケージに含まれない問題があるので、明日はそれに対処したいと思います。

setup.py bdist_rpmでjsをコンパイルする (step1)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
前のエントリで、distutilsの挙動がわかったので本筋に戻る。

まず、build_js.pyを作る。
print 'build_js is here!'
まだ呼び出しが行われることを確認するだけなので、これだけにしておく。

で、setup.pyをいじる。
from distutils.dist import Distribution
def xxx(self):
        print 'Distribution:has_js'
        return self.js and len(self.js) > 0
Distribution.has_js = xxx

from distutils.command.build import build
def yyy(self):
        print 'build:has_js'
        return self.distribution.has_js() 
build.has_js = yyy
build.sub_commands.append( ('build_js', build.has_js),)

from distutils.dist import Distribution
orig = Distribution.__init__
def myinit(self, attrs):
  self.js = None
  orig(self, attrs)

Distribution.__init__ = myinit

def CompiledJS(*args, **attr):
  return ['hogehoge']

setup(
  ...
  js=CompiledJS('/var/www/static/notepad.c.js',
                ['./src/notepad.js'],
                ['/home/nori/lib/closure/'],
                '/home/nori/bin/closure/compiler.jar',
                ['ADVANCED_OPTIMIZATIONS'],
               ),
   ...
)

すると実行されて
[nori@shinano]~/Desktop/study/JavaScript/closure-proj% python setup.py build
running build
build:has_js
Distribution:has_js
running build_js
error: invalid command 'build_js'
となる。あと一息。build_js.pyがloadされるように細工する必要がある。
クラスが変更されるのでbdistからbuildが呼び出された場合でも実行される。
[nori@shinano]~/Desktop/study/JavaScript/closure-proj% python setup.py bdist
running bdist
running bdist_dumb
running build
build:has_js
Distribution:has_js
running build_js
error: invalid command 'build_js'

2010年2月20日土曜日

distutils.buildを理解する。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
大筋でどのようにできているかというと、setupは、distributionを表現するdistがあって、実際の生成プロセスを記述するcommandとsubcommandからなっている。次に関係ないところをすっ飛ばして引用してみた。
  • core.py
    def setup (**attrs):
                dist.run_commands()
    
  • dist.py
    def run_commands (self):
            for cmd in self.commands:
                self.run_command(cmd)
    
  • cmd.py
    def run_command (self, command):
            """Run some other command: uses the 'run_command()' method of
            Distribution, which creates and finalizes the command object if
            necessary and then invokes its 'run()' method.
            """
            self.distribution.run_command(command)
    
  • dist.py
    def run_command (self, command):
            """Do whatever it takes to run a command (including nothing at all,
            if the command has already been run).  Specifically: if we have
            already created and run the command named by 'command', return
            silently without doing anything.  If the command named by 'command'
            doesn't even have a command object yet, create one.  Then invoke
            'run()' on that command object (or an existing one).
            """
            # Already been here, done that? then return silently.
            if self.have_run.get(command):
                return
    
            log.info("running %s", command)
            cmd_obj = self.get_command_obj(command)
            cmd_obj.ensure_finalized()
            cmd_obj.run()
            self.have_run[command] = 1
    
  • cmd.py コメントにあるとおり、サブクラスではメソッドrunを実装している。
    # Subclasses must define:
        #   initialize_options()
        #     provide default values for all options; may be customized by
        #     setup script, by options from config file(s), or by command-line
        #     options
        #   finalize_options()
        #     decide on the final values for all options; this is called
        #     after all possible intervention from the outside world
        #     (command-line, option file, etc.) has been processed
        #   run()
        #     run the command: do whatever it is we're here to do,
        #     controlled by the command's various option values
    
  • install.py
    def run (self):
            # Obviously have to build before we can install
            if not self.skip_build:
                self.run_command('build')
    
            # Run all sub-commands (at least those that need to be run)
            for cmd_name in self.get_sub_commands():
                self.run_command(cmd_name)
    
  • cmd.py (get_sub_commands)
    def get_sub_commands (self):
            commands = []
            for (cmd_name, method) in self.sub_commands:
                if method is None or method(self):
                    commands.append(cmd_name)
            return commands
    
  • build.py sub_commandはclass変数として定義されている。
    sub_commands = [('build_py',      has_pure_modules),
                        ('build_clib',    has_c_libraries),
                        ('build_ext',     has_ext_modules),
                        ('build_scripts', has_scripts),
                       ]
    
  • build_ext.py subcommandの一例として、build_extを追ってみる。
    def run (self):
            ...skip...
            self.compiler = new_compiler(compiler=self.compiler,
                                         verbose=self.verbose,
                                         dry_run=self.dry_run,
                                         force=self.force)
            customize_compiler(self.compiler)
            ...skip...
            # Now actually compile and link everything.
            self.build_extensions()
    
        def build_extensions(self):
            # First, sanity-check the 'extensions' list
            self.check_extensions_list(self.extensions)
    
            for ext in self.extensions:
                self.build_extension(ext)
    
        def build_extension(self, ext):
        ...skip...
            objects = self.compiler.compile(sources,
            ...skip
            self.compiler.link_shared_object(
    
  • ccompiler.py はcコンパイラの抽象。_compileとlinkをsubclassで実装する。
    def compile(self, sources, output_dir=None, macros=None,
        def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
         --> empty
        def link (self,
         --> empty
    
        def spawn (self, cmd):
            spawn (cmd, dry_run=self.dry_run)
         --> from distutils.spawn import spawn
    
  • unixccompiler.py posix系はこのsubclassで実装されている。
    def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
            try:
                self.spawn(self.compiler_so + cc_args + [src, '-o', obj] +
                           extra_postargs)
            except DistutilsExecError, msg:
                raise CompileError, msg
    
        def link(self, target_desc, objects,
                    self.spawn(linker + ld_args)
    
  • spawn.py まあ当然ですが、forkに落ちます。
    def _spawn_posix (cmd,
        pid = os.fork()
    
  • build.pyでsub_commandのmappingを定義していたが、そのペアの右側はbuildのメソッドであり、distのmethodに転送される。。なので、このmethodがtrueを返すと、sub_commandが実行される。

    build.py
    def has_scripts (self):
            return self.distribution.has_scripts()
    
    dist.py
    def has_scripts (self):
            return self.scripts and len(self.scripts) > 0
    
  • ではこのscriptsはどこからきているかというとsetupの**attrからきている。
    core.py
    def setup (**attrs):
            _setup_distribution = dist = klass(attrs)
    
    
    dist.py
    class Distribution:
        def __init__ (self, attrs=None):
            if attrs:
                for (key,val) in attrs.items():
                    if hasattr(self.metadata, key):
                        setattr(self.metadata, key, val)
                    elif hasattr(self, key):
                        setattr(self, key, val)
                    else:
                        msg = "Unknown distribution option: %s" % repr(key)
                        if warnings is not None:
                            warnings.warn(msg)
                        else:
                            sys.stderr.write(msg + "\n")
    

2010年2月19日金曜日

twitmateのデータを作ったときのスクリプト

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
かきすてでも読めるレベルになるのがpythonのいいところ。
http://king-soukutu.com/twit/?n=twitmate
http://king-soukutu.com/twit/?n=twitmate2

import re
user = re.compile(r'"http://twitter.com/(?P[A-Za-z0-9_+]+)"')

m = user.search('"http://twitter.com/pothos"')
#print m.group()

found = {}
for i in range(1, 31):
  f = open('following%i.html'%(i))
  lines = f.read()
  ms = user.findall(lines)
  for m in ms:
    found[m] = 'http://twitter.com/%s'%(m,)
  f.close()
for u in found:
  print u

setup.py bdist_rpmでjsをコンパイルする (step0)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
calcdeps.pyから、そのために必要な部分をほじくり出す。optparseで取り出している引数を関数の引数として渡すようにすればよいので、たいしたことはない。問題はこれをsetup.pyのなかでフックするようにするにはどうしたらよいかということ。
import calcdeps 

def jscompile(inputs, search_paths, output, compiler_jar, compiler_flags):
  import logging
  import os
  logging.basicConfig(format='calcdeps.py: %(message)s', level=logging.INFO)
  logging.info('Scanning files...')
  logging.info(os.getcwd())
  logging.info(search_paths)
  search_paths = calcdeps.ExpandDirectories(search_paths)
  out = open(output, 'w')
  logging.info('Finding Closure dependencies...')
  deps = calcdeps.CalculateDependencies(search_paths, inputs)

  calcdeps.Compile(compiler_jar, deps, out, compiler_flags)
  return output

2010年2月18日木曜日

pep8チェック

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
http://pypi.python.org/pypi/pep8/を使ってチェックしてみた。思ったより少なかったなぁ。個人的には変数名とかが単語として辞書にあるかどうかとかもチェックしてほしいが。
[nori@shinano]~/Desktop/work/bglib/bglib% /usr/bin/pep8 bglib 
bglib/doc/__init__.py:7:1: E302 expected 2 blank lines, found 0
bglib/doc/__init__.py:8:3: E111 indentation is not a multiple of four
bglib/doc/__init__.py:10:3: E301 expected 1 blank line, found 0
bglib/doc/__init__.py:26:1: W391 blank line at end of file
bglib/doc/bgwiki.py:21:1: W291 trailing whitespace
bglib/doc/bgwiki.py:34:44: E261 at least two spaces before inline comment
bglib/doc/bgwiki.py:39:3: E303 too many blank lines (2)
bglib/doc/bgwiki.py:46:80: E501 line too long (113 characters)
bglib/doc/bgwiki.py:46:35: E201 whitespace after '('
bglib/doc/bgwiki.py:46:42: E225 missing whitespace around operator
bglib/doc/bgwiki.py:213:13: E222 multiple spaces after operator
bglib/doc/bgwiki.py:310:51: E231 missing whitespace after ':'
bglib/doc/bgwiki_test.py:72:19: E202 whitespace before ')'
bglib/doc/fuzzing_test.py:16:25: W602 deprecated form of raising exception
bglib/doc/html.py:46:14: E221 multiple spaces before operator
bglib/encoding/dbbyte.py:69:27: E203 whitespace before ','
bglib/encoding/gnubgid.py:41:14: E211 whitespace before '('
bglib/encoding/gnubgid.py:158:19: E251 no spaces around keyword / parameter equals
bglib/model/move_test.py:16:18: E701 multiple statements on one line (colon)
bglib/record/gnubg.py:30:13: W601 .has_key() is deprecated, use 'in'

2010年2月17日水曜日

google closure調査中のメモ(コンパイラの制限事項編)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
まとまった情報が欲しい方はごめんなさい。
ほんとに走り書きです。

オプティマイザの制限

全般的な制限:
1)Ecmascript 262 revision 3.しか理解しません。クロスブラウザなものを作ろうとしているのだから、当然の帰結。
2)コメントが消えてなくなります。JSの最適化とは転送量の削減なので、そういうことです。

SIMPLE_OPTIMIZATIONSに関して
- withを取り扱えない。
- evalもだめ。
- a[""]のたぐいもだめ。

ADVANCED_OPTIMIZATIONSにかんして
関数が消えたり、定数値を計算してしまったり。

スコープ外で定義されている変数を使うこと(コンパイラがスコープの外を見ることができないのでだめ)。
明示的なエキスポートしないで内部の名前を外部で使うこと(コンパイラがリネームしてしまうから)
a.hogeとa["hoge"]を混在させる(後者はリネームの対象にならないため)

外で使う関数なら必ずexportする。でないとコンパイラが消してしまう。このへんはCでgccでも-O2とかにするとなるんじゃなかったっけ?

名前の展開(object property flattening)
{}をネストすると遅くなるので、それへの対策として名前を展開する。
var foo = {};
   foo.bar = function (a) { alert(a) };
   foo.bar("hello");
を次のように変換する。
var foo$bar = function (a) { alert(a) };
   foo$bar("hello");
よってこのコードはthisへの参照が破壊される危険がある。のでthisを使わなければならない、つまりメソッド内(正確にはprototype property)とnew関数のなかでやる必要がある。

To prevent property flattening from breaking your references to this, only use this within constructors and prototype methods. The meaning of this is unambiguous when you call a constructor with the new keyword, or within a function that is a property of a prototype.


function Foo() {
  this.bar = function(a){alert(a)};
  return this;
}

参照破壊系のバグが増えるので、*コンパイル後*のコードを使ってunittestを走らせるとよいかもしれません。

2010年2月13日土曜日

QUnut + JSDeferred, テストのためにnextに手を入れる。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
nextの中に手を突っ込んで、callbackの前後でstart/stopすることを目的に書き換えたのだが、なにかうまくいって無くて、testの結果が記録されない。
なにを見落としているのだろう?
var AJAXTIMEOUT = 100;
var surpress_stop  = false;
(function(){
  Deferred.prototype._fire = function (okng, value) {
    var next = "ok";
    start();
    debug('+++');
    try {
      value = this.callback[okng].call(this, value);
    } catch (e) {
      next  = "ng";
      value = e;
      if (Deferred.onerror) Deferred.onerror(e);
    }
    if (!surpress_stop){
      debug('---');
      stop(AJAXTIMEOUT);
    }
    if (value instanceof Deferred) {
      value._next = this._next;
    } else {
      if (this._next) this._next._fire(next, value);
    }
    return this;
  }
})();


2/16追記... startの中でsetTimeoutしているので、nextにフックするべきではない。start/stop pairはtestの中で1つだけにすべき。13msの遅延が何を引き起こすか予想不能。非同期問い合わせが返ってくる前にqueueからテストケースを抜いて実行されると不味い。
        start: function() {
                // A slight delay, to avoid any current callbacks
                if ( window.setTimeout ) {
                        window.setTimeout(function() {
                                if ( config.timeout ) {
                                        clearTimeout(config.timeout);
                                }

                                config.blocking = false;
                                process();
                        }, 13);
                } else {
                        config.blocking = false;
                        process();
                }
        },

2010年2月9日火曜日

JSDeferredを誤解。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
当たり前だけど、「chainのnextとそうでないnextは違う」ということです。debugを挿入して追ってみました。

prototypeのnext
  next  : function (fun) { 
    debug('Deferred.prototype.next');
    return this._post("ok", fun) 
  },

ブラウザ差異を吸収するためのモノのようなので、defaultのみをチェックする。
Deferred.next_default = function (fun) {
  debug('Deferred.next_default');
        var d = new Deferred();
        var id = setTimeout(function () { d.call() }, 0);
        d.canceller = function () { clearTimeout(id) };
        if (fun) d.callback.ok = fun;
        return d;
};

Deferred.next = Deferred.next_faster_way_readystatechange ||
                Deferred.next_faster_way_Image ||
                Deferred.next_default;

実行したテストコード
test('very simple tests for using jsdeferred with qunit.', function(){
  var x = 0;
  Deferred.define();
  next(function(){
    x = 2;
  })
  .next(function(){
    x = 1;
  })
  .next(function(){
    equals(x, 1);
    start();
  });
  stop(100);
});
これも当たり前だけど、グローバルに挿入されているinstanceのものが1回呼ばれた後、prototypeのほうが2回呼ばれます。設計意図を理解せねば。それからDeferred.prototype.fnとDeferred.fnの違いもわからないとだめだなぁ。var d = Deferred; d.fn()とDeferred.fn()は意味が違う。

2010年2月8日月曜日

まださまよっている。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
x=2が行われた後、next()が積まれてx=1が実行され、そしてequals(x, 1)にいく。
test('bad use (3) of jsdeferred with qunit.', function(){
  var x = 0;
  Deferred.define();
  next(function(){
    x = 1;
  });
  equals(x, 0);
  x = 2;
  next(function(){
    equals(x, 1);
    start();
  });
  stop(100);
});

しかし、これはあまり自明じゃない。consoleにはpiyo hogeの順番で表示される。つまりstartが呼ばれてからx=1の代入が行われる。まったく好ましくない挙動。timerを消す必要があるかもしれない。
test('bad use (2) of jsdeferred with qunit.', function(){
  var x = 0;
  Deferred.define();
  next(function(){
    equals(x, 2);
    return next(function(){
      ok(true);
      x = 1;
      debug('hoge');
    });
  });
  equals(x, 0);
  x = 2;
  next(function(){
    equals(x, 2);
    debug('piyo');
    start();
  });
  stop(100);
});

QUnut + JSDeferred

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
  • Why it works?
  • Need to obtain Deferred instance for testing part from the target.

test('bad use (1) of jsdeferred with qunit.', function(){
  var x = 0;
  Deferred.define();
  next(function(){
    next(function(){ x = 1;
    });
  })
  .next(function(){
    equals(x, 0);
    start();
  });
  stop(100);
});

test('bad use (2) of jsdeferred with qunit.', function(){
  var x = 0;
  Deferred.define();
  next(function(){
    return next(function(){
      x = 1;
    });
  });
  next(function(){
    equals(x, 0);
    start();
  });
  stop(100);
});

test('right way: some thing bit complex using jsdeferred with qunit.', function(){
  var x = 0;
  var d = Deferred.define();

  d.next(function(){
    debug('first next ');
    return next(function(){
      debug('nested next');
      x = 1;
    });
  })
  .next(function(){
    debug('equals next ');
    equals(x, 1);
    start();
  });
  stop(100);
});

2010年2月6日土曜日

QUnitのstartとstop

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
QUnitはjQueryのunittest frame work. setTimeoutで13ってなんだよ!
クロスブラウザのJSのunittest frameworkで非同期のテストをサポートしているモノはほかに何があるのだろう?知っている方がいたら教えてください。

from jQueryのテスティングフレームワーク QUnit

start( ) : テストを再開します。

stop( ) : テストを中断します。Ajaxで非同期通信等のテストを行うとき、非同期処理前にstop()でテストを止め、非同期処理終了時のコールバック関数内で start()を実行して、テストを再開します。

とのこと。これだけではどういう機構なのかさっぱりわからないのでソースを見てみる。
  start: function() {
    // A slight delay, to avoid any current callbacks
    if ( window.setTimeout ) {
      window.setTimeout(function() {
        if ( config.timeout ) {
          clearTimeout(config.timeout);
        }

        config.blocking = false;
        process();
      }, 13);
    } else {
      config.blocking = false;
      process();
    }
  },

  stop: function(timeout) {
    config.blocking = true;

    if ( timeout && window.setTimeout ) {
      config.timeout = window.setTimeout(function() {
        QUnit.ok( false, "Test timed out" );
        QUnit.start();
      }, timeout);
    }
  },
  • stopは一時中断で、あまり長く中断しているとテスト失敗にする。通常は非同期な動作を開始する前に呼ぶ。つまり非同期動作が長いということはその非同期動作の制御が戻ってきていないということになる。
  • startの13は謎。processを呼ぶとテストが走るのだと思う。
さらにprocessを追ってみる。
function process() {
  while ( config.queue.length && !config.blocking ) {
    config.queue.shift()();
  }
}
  1. キューから関数を抜いてそれを実行する。
  2. config.blockingをたててテストを中断し、フラグをクリアしてprocessを呼べば再開できる。キューから抜かれてしまったモノは中断できないはず、というかブロッキングですね。
古いMacOSの協調的マルチプロセッシングを思い出します。pre-emptされないのでプログラムは自発的に制御を明け渡さないといけない。キューに詰められるitemの粒度が大きいと気持ち悪いですね。

JSSpecのなか。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
コアっぽいコードを発見。そのなかでも特に注目すべきは次の点だろう。
  • setTimeout 0している。
  • targetの中で各test caseを実行しているのだろう。
    result = self.target();
       self.onSuccess(self, result);
    
JSSpec.Executor.prototype.run = function() {
  var self = this;
  var target = this.target;
  var onSuccess = this.onSuccess;
  var onException = this.onException;

  window.setTimeout(
    function() {
      var result;
      if(JSSpec.Browser.Trident) {
        window._curExecutor = self;

        result = self.target();
        self.onSuccess(self, result);
      } else {
        try {
          result = self.target();
          self.onSuccess(self, result);
        } catch(ex) {
          if(JSSpec.Browser.Webkit) ex = {message:ex.message, fileName:ex.sourceURL, lineNumber:ex.line};

          if(JSSpec._secondPass)  {
            ex = self.mergeExceptions(JSSpec._assertionFailure, ex);
            delete JSSpec._secondPass;
            delete JSSpec._assertionFailure;

            ex.type = "failure";
            self.onException(self, ex);
          } else if(JSSpec._assertionFailure) {
            JSSpec._secondPass = true;
            self.run();
          } else {
            self.onException(self, ex);
          }
        }
      }
    },
    0
  );
};
もっともhighレベルで呼ばれるrunはこれ。Runnerがspecからcaseを読み込んでexecuterとして追加し、その後実行している。
JSSpec.Runner.prototype.run = function() {
        JSSpec.log.onRunnerStart();
        var executor = new JSSpec.CompositeExecutor(function() {JSSpec.log.onRunnerEnd()},null,true);
        for(var i = 0; i < this.specs.length; i++) {
                executor.addExecutor(this.specs[i].getExecutor());
        }
        executor.run();
};
addExecuterの中身。Executerインスタンスが作るツリーをトラバースしながらcaseを実行していく。 Composite Patternを使っているのでCompositeExecuter。Unittestの実装では普通な実装。 呼び出す順序はここで決まってしまう。
JSSpec.CompositeExecutor.prototype.addExecutor = function(executor) {
        var last = this.queue.length == 0 ? null : this.queue[this.queue.length - 1];
        if(last) {
                last.next = executor;
        }
        
        executor.parent = this;
        executor.onSuccessBackup = executor.onSuccess;
        executor.onSuccess = function(result) {
                this.onSuccessBackup(result);
                if(this.next) {
                        this.next.run();
                } else {
                        this.parent.onSuccess();
                }
        };
        executor.onExceptionBackup = executor.onException;
        executor.onException = function(executor, ex) {
                this.onExceptionBackup(executor, ex);

                if(this.parent.continueOnException) {
                        if(this.next) {
                                this.next.run();
                        } else {
                                this.parent.onSuccess();
                        }
                } else {
                        this.parent.onException(executor, ex);
                }
        };

        this.queue.push(executor);
};

大筋で理解はしたつもりだが、Deferredと混ぜるのはどうすれば良いのやら。

JSSpec + jsDeferredどうしよう?(動機編)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
当たり前なんですが、このテストは意図に反して成功します。なぜならnextに与えている関数はたぶん1つも実行されないためです。最後nextで呼び出されるはずのshould_beは実行されません。。通る前にテストが終わってしまうのです。

マルチスレッドでいうところの「待ち合わせ」が必要なのです。Deferredの中のキューをのぞき見て、非同期のリクエストがすべて完了したことを見計らってテストから抜けてもらわないといけないのですが、ユーザ側でなんと化しようとするとyieldとかが無いとたぶん無理でそれはJSの標準にはありません。もしくはJSSpecになんらかのpatchを当てて待ち合わせるように書き直す必要があるのです。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<title>jsboard.jy JSSpec results</title>
<link rel="stylesheet" type="text/css" href="../JSSpec/JSSpec.css" />
<script type="text/javascript" src="../JSSpec/diff_match_patch.js"></script>
<script type="text/javascript" src="../JSSpec/JSSpec.js"></script>

<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">// <![CDATA[
google.load("jquery", "1.4.0");
// ]]></script>
<script type="text/javascript" src="../jsdeferred/jsdeferred.jquery.js"></script>
<script type="text/javascript">// <![CDATA[
describe('async', {
        'before': function() {
                target = {};
        },
  'テスト' : function (){
  var x = 0;
  var d = Deferred.define();
  d.next(function (){
    x =1;
  })
  .wait(1000)
  .next(function(){
    value_of(x).should_be(1);
    value_of(x).should_be(0);
  });
  }
});



// ]]></script>
</head>
<body><div style="display:none;"><p>A</p><p>B</p></div></body>
</html>

2010年2月2日火曜日

LRU at BPStudy with JSSpec.

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
まあまあ手になじむ。あとは非同期なモノをどうかけるのかをチェックするべし。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<title>LRU JSSpec results</title>
<link rel="stylesheet" type="text/css" href="../src/JSSpec.css" />
<script type="text/javascript" src="../src/diff_match_patch.js"></script>
<script type="text/javascript" src="../src/JSSpec.js"></script>
<script type="text/javascript">// <![CDATA[

function debug(){
  if (window['console']){
    console.log.apply(null, arguments);
  }
};

function LRU(maxSize){
  return {
    '_store' : [],
    '_maxSize': maxSize,
    'maxSize': function(){
      var self = this;
      return self._maxSize;
    },
    'put': function(key, value){
      var self = this;
      var p;
      var n;
      var v;
      p = self._peek(key);
      n = p[0];
      v = p[1];
      if (n >=0){
        self._store = self._store.slice(0, n).concat(self._store.slice(n+1));
        debug('self._store', self._store);
        self._store.push([key, value]);
        return;
      }else{
        self._touch(key, value);
      };
    },
    '_peek': function(key){
      var self = this;
      var n;
      var k;
      var v;
      for (n in self._store){
        k = self._store[n][0];
        v = self._store[n][1];
        if (k==key){
          return [n, v];
        };
      };
      return [-1, null];
    },
    'get': function(key){
      var self = this;
      var n;
      var k;
      var v;
      p = self._peek(key);
      n = p[0];
      v = p[1];
      if (v != null){
        self._touch(key);
      };
      return v;
    },
    _touch: function(key, new_value){
      var self = this;
      var p;
      var n;
      var v;
      p = self._peek(key);
      n = p[0];
      v = p[1];
      if (n >=0){
        // found so pop and append key-value pair with new value.
        self._store = self._store.slice(0, n).concat(self._store.slice(n+1));
        self._store.push([key, v]);
        return;
      };

      // not found in _store, 
      if (typeof(new_value) != 'undefined'){
        self._store.push([key, new_value]);
        if (self._store.length > maxSize){
          self._store = self._store.slice(1);
        };
        return;
      }
    }
  };
};


describe('LRU', {
        'before': function() {
                target = LRU(2);
        },
        'should 現在の最大サイズを返す。': function() {
                value_of(target.maxSize()).should_be(2);
        },
        'should keyに対応する値がなければnullを返す。': function() {
                value_of(target._peek('foo')[1]).should_be(null);
        },
        'should keyに対応する値を内部状態を変えることなく返す。': function() {
                target.put('foo', 42);
                value_of(target._peek('foo')[1]).should_be(42);
        },
        'should keyの値が同じなら上書きされる。': function() {
                target.put('foo', 0);
                target.put('foo', 42);
                value_of(target._peek('foo')[1]).should_be(42);
        },
        'should sizeよりたくさん入れると始めに入れたやつが消える。': function() {
                target.put('foo', 42);
                target.put('bar', 43);
                target.put('buzz', 44);
                value_of(target._peek('foo')[1]).should_be(null);
                value_of(target._peek('bar')[1]).should_be(43);
                value_of(target._peek('buzz')[1]).should_be(44);
        },
        'should getすると居残る。': function() {
                target.put('foo', 42);
                target.put('bar', 43);
                value_of(target.get('foo')).should_be(42);
                target.put('buzz', 44);
                value_of(target._peek('foo')[1]).should_be(42);
                value_of(target._peek('bar')[1]).should_be(null);
                value_of(target._peek('buzz')[1]).should_be(44);
        },

})

// ]]></script>
</head>
<body><div style="display:none;"><p>A</p><p>B</p></div></body>
</html>

JSSpecを始めました。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
BPStudyでRSpecが気になったのと、JavaScriptのコードをテストしなくなったので、JSSpecを始めて見た。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<title>LRU JSSpec results</title>
<link rel="stylesheet" type="text/css" href="../src/JSSpec.css" />
<script type="text/javascript" src="../src/diff_match_patch.js"></script>
<script type="text/javascript" src="../src/JSSpec.js"></script>
<script type="text/javascript">// <![CDATA[

function LRU(size){
  return {
    'size': function(){
      var self = this;
      return 9;
    }
  };
};


describe('LRU', {
        'before': function() {
                target = LRU(2);
        },
        'should 現在のサイズを返す。': function() {
                value_of(target.size()).should_be(2);
        }
})

// ]]></script>
</head>
<body><div style="display:none;"><p>A</p><p>B</p></div></body>
</html>

2010年1月27日水曜日

debug() for js

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
(function($){
  function debug(){
    if (window['console']){
      console.log.apply(null, arguments);
    };
  };
}(jQuery);
いくつかpointをば:
  • functionで囲う。これはname spaceの汚染防止
  • window['console']のガード:firebugがあがっていないときはlogしない。
  • apply~はdebugに可変長引数をパススルーするトリック。

2010年1月18日月曜日

Param serialization of JQuery 1.4.0

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

shinano.tonic-water.com - - [18/Jan/2010 17:21:47] "GET /?callback=jsonp1263802906929&_=1263802907163&move%5B%5D=4%2F2+1%2Foff(2)+&gnubgid=ZwAAwGwAAAAAAA%3AcIkkAAAAAAAA HTTP/1.1" 500 59


with jQuery 1.3.2

shinano.tonic-water.com - - [18/Jan/2010 17:23:24] "GET /?callback=jsonp1263803004677&_=1263803004910&move=4%2F2+1%2Foff(2)+&gnubgid=ZwAAwGwAAAAAAA%3AcIkkAAAAAAAA HTTP/1.1" 200 80

I need to figure out differences.
%5B%5 is [], so it comes from some where in js, and it looks like array related.

ajax with

data : {'move' : m, gnubgid : alt},

generates "move%5B%5=" so, I changed it to

data : {'move' : m[0], gnubgid : alt},

and it gives the right result.

m comes from

var m = mv.match(moveRegexp);

so I guess that 1.4.0 has changed over match object handling on serialization.


Param serialization now happens in the PHP/Rails style by default. You can use jQuery.ajaxSettings.traditional = true; to use traditional parameter serialization.

Source: Backwards-Incompatible Changes

PHP/Rails means "recursive". If match object is an iterable of strings, then it is reasonable.

Home server NIC

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

eth0 Link encap:Ethernet HWaddr 00:19:DB:62:B9:9F
inet6 addr: fe80::219:dbff:fe62:b99f/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:193946 errors:0 dropped:0 overruns:0 frame:0
TX packets:565 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:12137016 (11.5 MiB) TX bytes:23946 (23.3 KiB)
Interrupt:21 Base address:0xa800

eth1 Link encap:Ethernet HWaddr 00:90:CC:EF:8C:3F
inet6 addr: fe80::290:ccff:feef:8c3f/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:3381843 errors:0 dropped:0 overruns:0 frame:0
TX packets:3000068 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2379434048 (2.2 GiB) TX bytes:1767241216 (1.6 GiB)
Interrupt:17 Base address:0xec00

eth1:0 Link encap:Ethernet HWaddr 00:90:CC:EF:8C:3F
inet addr:192.168.2.64 Bcast:192.168.2.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
Interrupt:17 Base address:0xec00

eth1:1 Link encap:Ethernet HWaddr 00:90:CC:EF:8C:3F
inet addr:192.168.2.65 Bcast:192.168.2.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
Interrupt:17 Base address:0xec00

eth1:2 Link encap:Ethernet HWaddr 00:90:CC:EF:8C:3F
inet addr:192.168.2.66 Bcast:192.168.2.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
Interrupt:17 Base address:0xec00

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:2086245 errors:0 dropped:0 overruns:0 frame:0
TX packets:2086245 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:305132509 (290.9 MiB) TX bytes:305132509 (290.9 MiB)


  • one of NIC is not used.

  • I do not have cross cable!

2010年1月12日火曜日

チャレンジ

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
人材獲得作戦・4 試験問題ほかの試験問題にチャレンジ
30分でLv2まで。おせー。 goalから小さくなるように逆にたどればいい気もしてきた。面倒だからもういいや。プログラミングコンテストに出る連中だと10分とかだろ。これ。

* * * * * * * * * * * * * * * * * * * * * * * * * *

* S * ( 7) * ( 9)(10)(11)(12)(13)(14)(15)(16)(17)(18)(19)(20)(21)(22)(23)(24)(25)(26)(27)(28) *

* ( 0) * ( 6) * ( 8)( 9) * (13)(14) * * * * * * * * * * * * * (28)(29) *

* ( 1) * ( 5)( 6)( 7) * (15)(14)(15)(16) * * * * * * * * * * * * (29)(30) *

* ( 2)( 3)( 4)( 5) * (17)(16)(15)(16)(17)(18)(19)(20)(21)(22)(23)(24)(25)(26)(27)(28)(29)(30)(31) *

* * * * * * * * * * * * * * (22) * * * * * * * * * * *

* (36)(35)(34)(33)(32)(31)(30)(29)(28)(27)(26)(25)(24)(23)(24)(25)(26)(27)(28)(29)(30)(31)(32)(33) *

* * (36) * * * * * * * * * * * * * * * * * * * * * * *

* (38)(37)(38)(39)(40)(41) * (45)(46)(47)(48)(49)(50)(51)(52)(53)(54)(55)(56)(57)(58) G (64)(65) *

* (39)(38) * (40)(41)(42)(43)(44)(45) * * * * * * * * * * * (59) * (63)(64) *

* (40)(39)(40)(41) * (43)(44)(45)(46)(47)(48)(49)(50) * * * * * * * (60) * (62)(63) *

* (41)(40)(41)(42)(43)(44)(45) * (47)(48)(49)(50)(51)(52)(53)(54)(55)(56)(57)(58)(59)(60)(61)(62) *

* * * * * * * * * * * * * * * * * * * * * * * * * *

コード

f = open('meiz')

m = []
for line in f:
cs = []
for c in line:
cs.append(c)
m.append(cs)

f.close()
print m


def locate_x(x, m):
for i, line in enumerate(m):
for j, c in enumerate(line):
if c==x:
return i, j
raise

print locate_x('S', m)
print locate_x('G', m)

def pretty(m):
for cs in m:
line = ''
for c in cs:
if isinstance(c, int):
line += '(%2i)'%c
else:
line += ' '+c+' '
print line


def clone(m):
r = []
for line in m:
cs = []
for c in line:
cs.append(c)
r.append(cs)
return r

def step(x, m, count):
V = m[x[0]][x[1]]
if isinstance(V, int):
if V <= count:
return False
m[x[0]][x[1]] = count
return search(x, m, count + 1)
if V == ' ':
m[x[0]][x[1]] = count
return search(x, m, count + 1)
elif V == 'G':
return True
elif V == 'S':
return False
elif V == '*':
return False
else:
pass
assert False

def search(S, m, count):
#print count ,S

# North
x = S[0] - 1, S[1]
step(x, m, count)
# South
x = S[0] + 1, S[1]
step(x, m, count)
# West
x = S[0], S[1] - 1
step(x, m, count)
# East
x = S[0], S[1] + 1
step(x, m, count)

search(locate_x('S', m), m, 0)
pretty(m)

2010年1月11日月曜日

packaging

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

python*/site-packages/hoge.py
python*/site-packages/hoge-usage-sample/sample.py
python*/site-packages/hoge-usage-sample/sample-data/*.dat

みたいなdir構造にしたいのだが、開発中は

foo/src/hoge.py
foo/src/sample.py
foo/src/sample-data/*.dat

というdir構造をしていて、どのようにやればpackageできるのか困り果てる。
妥協してhoge/hoge.pyとhoge/__init__.pyをインストールするようにするかぁ。

2010年1月7日木曜日

werkzeug

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
contrib.sessionsがまさにほしかったもの。

これでcomet sideとweb sideのsessionの共有が可能になる。


session_store = FilesystemSessionStore()

def application(environ, start_response):
request = Request(environ)
sid = request.cookie.get('cookie_name')
if sid is None:
request.session = session_store.new()
else:
request.session = session_store.get(sid)
response = get_the_response_object(request)
if request.session.should_save:
session_store.save(request.session)
response.set_cookie('cookie_name', request.session.sid)
return response(environ, start_response)